
在数据分析场景中,我们经常会遇到一种宽格式(wide format)的dataframe,其中包含一个标识符列(如code)、一个参照日期列(如date),以及一系列以日期作为列名的数值列(如202001, 202002等)。我们的目标是对于dataframe的每一行,根据其date列中指定的日期,计算出该日期之前所有数值列的总和,以及该日期之后所有数值列的总和,并将这两个结果作为新的列添加到原始dataframe中。
例如,如果一行数据的Date列值为202004,我们需要将202001、202002、202003列的值相加作为“之前”的总和,将202005、202006等列的值相加作为“之后”的总和。
解决此类问题的关键在于将宽格式数据转换为长格式,以便于进行日期比较和分组聚合。Pandas提供了强大的数据重塑和聚合工具,可以高效地完成这一任务。
首先,我们创建一个示例DataFrame来模拟问题中描述的数据结构:
import pandas as pd
import numpy as np
# 创建模拟数据
data = {
'Code': ['12345', '12346', '12347'],
'202001': [1000, 999, 1983],
'202002': [1001, 1000, 1984],
'202003': [1002, 1001, 1985],
'202004': [1003, 1002, 1986],
'202005': [1004, 1003, 1987],
'202006': [1005, 1003, 1988],
'202007': [3006, 1005, 1989],
'202008': [1007, 1006, 1990],
'202009': [1008, 1007, 1991],
'202010': [1009, 1008, 1992],
'202011': [1010, 1009, 1993],
'202012': [1011, 1010, 1994],
'Date': ['202004', '202006', '202010'] # 参照日期
}
df = pd.DataFrame(data)
print("原始DataFrame:")
print(df)pd.melt()函数是处理宽格式数据的利器。它将指定的列(这里是日期列)从列头转换为行数据,使得每个数值与对应的Code和日期(原列名)配对。
# 将日期列融化为长格式
tmp = df.melt(id_vars=['Code', 'Date'])
print("\n融化后的DataFrame (tmp):")
print(tmp.head())在融化后的tmp DataFrame中,Date列和variable列(原日期列名)都是字符串类型。为了进行正确的日期比较,我们需要确保它们是可比较的类型。在这个例子中,由于日期格式统一为'YYYYMM',直接进行字符串比较(字典序)可以得到正确的结果。
接着,我们使用np.where根据Date列和variable列的比较结果,为每行数据分配一个标签:'Before'(如果variable日期早于Date日期)或'After'(如果variable日期晚于或等于Date日期)。
# 确保日期和变量列为字符串类型,并进行日期比较,分配'Before'/'After'标签
tmp = tmp.astype({'Date': str, 'variable': str})
tmp = tmp.assign(col=lambda d: np.where(d['Date'].gt(d['variable']), 'Before', 'After'))
print("\n添加'col'标签后的DataFrame (tmp):")
print(tmp.head())现在,tmp DataFrame中有了Code、col('Before'/'After')和value。我们可以按照Code和col进行分组,并对value进行求和。
groupby(['Code', 'col'])['value'].sum() 会得到一个MultiIndex的Series,索引是Code和col。 unstack('col') 将col的层级从索引移到列,从而将'Before'和'After'作为独立的列。
# 按Code和'col'(Before/After)分组求和,然后unstack将Before/After变为列
summed_data = tmp.groupby(['Code', 'col'])['value'].sum().unstack('col')
# 确保'Before'和'After'列的顺序
summed_data = summed_data[['Before', 'After']]
print("\n分组求和并重塑后的数据 (summed_data):")
print(summed_data)最后一步是将计算出的Before和After总和合并回原始的df DataFrame。我们使用pd.merge()函数,以Code列作为连接键。
# 将求和结果合并回原始DataFrame
out = df.merge(summed_data, left_on='Code', right_index=True, how='left')
print("\n最终结果DataFrame (out):")
print(out)将上述步骤整合,得到完整的解决方案代码:
import pandas as pd
import numpy as np
def calculate_before_after_sums(df: pd.DataFrame) -> pd.DataFrame:
"""
根据DataFrame中每行的指定日期,计算其之前和之后数值列的总和。
参数:
df (pd.DataFrame): 原始DataFrame,包含'Code'列、'Date'列
以及以日期字符串为列名的数值列。
返回:
pd.DataFrame: 包含'Before'和'After'求和结果的原始DataFrame。
"""
# 1. 数据重塑:将日期列融化为长格式
# 保留 'Code' 和 'Date' 作为标识符列
# 将其他日期列名放入 'variable' 列,对应值放入 'value' 列
tmp = df.melt(id_vars=['Code', 'Date'])
# 2. 类型转换与条件判断
# 确保 'Date' 和 'variable' 列是字符串类型,以便进行字符串比较
tmp = tmp.astype({'Date': str, 'variable': str})
# 根据 'Date' 和 'variable' 的比较结果,分配 'Before' 或 'After' 标签
# np.where(condition, value_if_true, value_if_false)
# d['Date'].gt(d['variable']) 判断 Date 是否大于 variable (即 variable 在 Date 之前)
tmp = tmp.assign(col=lambda d: np.where(d['Date'].gt(d['variable']), 'Before', 'After'))
# 3. 分组求和与重塑
# 按 'Code' 和新创建的 'col' (Before/After) 进行分组,并对 'value' 求和
# unstack('col') 将 'col' 的值 ('Before', 'After') 转换为新的列
summed_data = tmp.groupby(['Code', 'col'])['value'].sum().unstack('col')
# 确保 'Before' 和 'After' 列的顺序(可选,但推荐保持一致性)
# 如果某一Code没有'Before'或'After'的数据,unstack会生成NaN,这里不处理,默认merge会保留NaN
if 'Before' not in summed_data.columns:
summed_data['Before'] = np.nan
if 'After' not in summed_data.columns:
summed_data['After'] = np.nan
summed_data = summed_data[['Before', 'After']]
# 4. 结果合并
# 将计算出的求和结果合并回原始 DataFrame
# left_on='Code': 原始DataFrame的连接键
# right_index=True: summed_data 的索引 (即 Code) 作为连接键
# how='left': 左连接,保留原始DataFrame的所有行
out = df.merge(summed_data, left_on='Code', right_index=True, how='left')
return out
# 模拟数据
data = {
'Code': ['12345', '12346', '12347'],
'202001': [1000, 999, 1983],
'202002': [1001, 1000, 1984],
'202003': [1002, 1001, 1985],
'202004': [1003, 1002, 1986],
'202005': [1004, 1003, 1987],
'202006': [1005, 1003, 1988],
'202007': [3006, 1005, 1989],
'202008': [1007, 1006, 1990],
'202009': [1008, 1007, 1991],
'202010': [1009, 1008, 1992],
'202011': [1010, 1009, 1993],
'202012': [1011, 1010, 1994],
'Date': ['202004', '202006', '202010']
}
df_original = pd.DataFrame(data)
# 执行计算
result_df = calculate_before_after_sums(df_original.copy()) # 使用copy避免修改原始df
print("\n最终计算结果:")
print(result_df)本教程展示了如何利用Pandas的melt、groupby、unstack和merge等核心功能,高效地解决DataFrame行数据根据动态日期条件进行分组求和的复杂问题。这种方法不仅代码简洁、易于理解,而且在处理大规模数据时表现出良好的性能。掌握这些数据重塑和聚合技巧,对于进行复杂的数据分析和报表生成至关重要。
以上就是使用Pandas根据指定日期对DataFrame行数据进行条件求和的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号