在多列上迭代日期范围函数,将结果输出到新列而不明确说明列名

Iterate date range function over multiple columns, output results to new columns without explicitly stating column names

我在语法方面遇到问题并提出了问题:

并收到了一个很好的函数,它为一列创建了一个解决方案:

    def to_value(row):
      return df1[(row['Start'] <= df1['Date']) & (df1['Date'] <= row['End'])]['Val'].sum()

    df3 = df2.copy()
    df3['Sum_Val'] = df3.apply(to_value, axis=1)

我的主管现在希望能够传递开始日期列表 (MM-DD) 并让函数从多列中为每一行生成结果,并将结果输出到与每个输入对应的新列柱子。然后将导出新列并在不同的程序中使用。

这是一个非常简化的数据集示例:

    import pandas as pd
    import numpy as np

    # ** df1: Dates and Values **
    rng = pd.date_range('2011-11-30', periods=100, freq='D')
    np.random.seed(1)
    rnd = np.random.randint(low=1, high=10, size=100)
    df1 = pd.DataFrame({'Date': rng,
                        'Val': rnd,
                       })

    #** df2: End Dates **
    df2 = pd.DataFrame({'Year': ['2012', '2012', '2012'],
                        'End_Date': ['2012-02-29', '2012-03-04', '2012-03-06'],
                        'YearOffset': ['2011', '2011', '2011']
                       })

实际数据集相当大(18x37K、52x16K),日期列表会根据需要更改,例如,一个 运行 可能有 12 个日期,下一个 运行 有 31 个新日期.因此,只创建一个初始列表并让函数遍历该列表是很方便的。开始日期都在特定年份,但年份会根据分析而变化。

我已经用下面的代码处理了列表问题:

    # List of start dates that will change
    start_list = ['12-01', '12-10', '12-23'] 

    # Create columns based on the start list with the appropriate year offset
    for i in start_list:
        df2[i] = df2['YearOffset'] + '-' + i

    # Create a list of columns to place the results. This block may not be necessary depending how the function is written
    sum_col_list = []
    for i in start_list:
        x = i + '_Sum'
        sum_col_list.append(x)
    df2 = pd.concat([df2, pd.DataFrame(columns=sum_col_list)])

我可以通过明确说明每一列,通过各种方式将函数应用于多列,例如,

    def f(x):
        return x**2
    df[['C','D']] = df[['A','B']].apply(f)

但我正在努力如何遍历日期范围函数内多列的每一行并将结果输出到相应的新列 而无需 明确说明每一列名字.

所需的输出如下所示:

    #** Desired Output:  df2 -- Date Range with Summed Values **
    df2_1 = pd.DataFrame({
                        'Year': ['2012', '2012', '2012'],
                        'End_Date': ['2012-02-29', '2012-03-04', '2012-03-06'],
                        '12-01': ['12-01-2011', '12-01-2011', '12-01-2011'],
                        '12-10': ['12-10-2011', '12-10-2011', '12-10-2011'],
                        '12-23': ['12-23-2011', '12-23-2011', '12-23-2011'],
                         '12-01_Sum': [482, 499, 511],
                         '12-10_Sum': [440, 457, 469],
                         '12-23_Sum': [369, 386, 398]
                       })

n.b。 np.random.seed 可能不会产生与其他系统相同的结果

正在复制您已有的大部分内容:

import pandas as pd
import numpy as np

# ** df1: Dates and Values **
rng = pd.date_range('2011-11-30', periods=100, freq='D')
np.random.seed(1)
rnd = np.random.randint(low=1, high=10, size=100)
df1 = pd.DataFrame({'Date': rng,
                    'Val': rnd,
                   })

#** df2: End Dates **
df2 = pd.DataFrame({'Year': ['2012', '2012', '2012'],
                    'End_Date': ['2012-02-29', '2012-03-04', '2012-03-06'],
                    'YearOffset': ['2011', '2011', '2011']
                   })


# List of start dates that will change
start_list = ['12-01', '12-10', '12-23'] 

# Create columns based on the start list with the appropriate year offset
for i in start_list:
    df2[i] = df2['YearOffset'] + '-' + i

然后我稍微更改了您的函数,使其适合列名。

def to_value(row):
    return df1[(row['Start'] <= df1['Date']) & (df1['Date'] <= row['End_Date'])]['Val'].sum()

创建一个新的“开始”列,其中包含开始日期,然后“分解”它,以便每一行都有一个,每个结束日期:

df2["Start"] = df2.iloc[:, 3:].values.tolist()
df2 = df2.explode("Start")

应用函数:

df2["Sum"] = df2.apply(to_value, axis=1)

将“开始”值更改为所需的列名称:

df2["Start"] = pd.to_datetime(df2["Start"]).dt.strftime("%m-%d") + "_Sum"

将数据帧旋转到所需的输出:

df2 = df2.pivot(index=["Year", "End_Date"]+start_list, columns="Start", values="Sum").reset_index()

输出:

#Out: 
#Start  Year    End_Date       12-01  ... 12-01_Sum 12-10_Sum  12-23_Sum
#0      2012  2012-02-29  2011-12-01  ...       482       440        369
#1      2012  2012-03-04  2011-12-01  ...       499       457        386
#2      2012  2012-03-06  2011-12-01  ...       511       469        398

#[3 rows x 8 columns]

代码中可能有稍微更高效的部分,但我使用了你已经拥有的并从那里开始工作,而不是 re-writing 那些 parts/combining 它们后面的部分。