我试图为每ID个月添加缺失的月份.添加的月份应该有关于IDyear_month的信息,以及产品的NaN.我的代码使用apply()实现了这一点,但速度很慢——我正在寻找一个矢量化版本,它可以运行得更快.

具体来说,在我的系统上,有60000行,df.set_index(df.index).groupby('ID').apply(add_missing_months)行大约需要20秒.我计划处理数百万行的数据,因此我认为需要对操作进行矢量化.非常感谢您的帮助!

import pandas as pd
df = pd.DataFrame({'ID': [1, 1, 1, 2, 2, 3], 'year_month': ['2020-01-01','2020-08-01','2020-10-01','2020-01-01','2020-07-01','2021-05-01'], 'product':['A','B','C','A','D','C']})

# Enlarge dataset to 60 000 rows
for i in range(9999):
    df2 = df.iloc[-6:].copy()
    df2['ID'] = df2['ID'] + 3
    df = pd.concat([df,df2], axis=0, ignore_index=True)

df['year_month'] = pd.to_datetime(df['year_month'])
df.index = pd.to_datetime(df['year_month'], format = '%Y%m%d')
df = df.drop('year_month', axis = 1)

# The slow function
def add_missing_months(s):
    min_d = s.index.min()
    max_d = s.index.max()
    s = s.reindex(pd.date_range(min_d, max_d, freq='MS'))
    return(s)

df = df.set_index(df.index).groupby('ID').apply(add_missing_months)
df = df.drop('ID', axis = 1)
df = df.reset_index()

推荐答案

不确定是否更快,但更简单的代码是:

df = df.sort_index().groupby('ID').apply(lambda x: x.asfreq('MS'))

df1 = df.groupby('ID').apply(lambda x: x.asfreq('MS'))
df2 = df.set_index(df.index).groupby('ID').apply(add_missing_months)

print (df1.equals(df2))
True

编辑:为了提高性能,创建Series.dt.to_period个月周期,将最小值和最大值与重复索引的差值相加Index.repeat,最后添加计数器GroupBy.cumcount个foa append months ranges,通过Series.dt.to_timestamp转换为时间戳,最后使用左连接:

df1 = (df.assign(year_month = df['year_month'].dt.to_period('m'))
         .groupby(['ID'])['year_month']
         .agg(['min', 'max']))

diff = df1['max'].astype('int').sub(df1['min'].astype('int')) + 1
df1 = df1.loc[df1.index.repeat(diff)]
df1 = (df1['min'].add(df1.groupby(level=0).cumcount()))
                 .dt.to_timestamp()
                 .reset_index(name='year_month'))

df = df1.merge(df.rename_axis(None), how='left')

Performance:

In [276]: %timeit jez(df)
126 ms ± 7.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [277]: %timeit vogel(df)
312 ms ± 32.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

df = pd.DataFrame({'ID': [1, 1, 1, 2, 2, 3], 'year_month': ['2020-01-01','2020-08-01','2020-10-01','2020-01-01','2020-07-01','2021-05-01'], 'product':['A','B','C','A','D','C']})

# Enlarge dataset to 60 000 rows
for i in range(9999):
    df2 = df.iloc[-6:].copy()
    df2['ID'] = df2['ID'] + 3
    df = pd.concat([df,df2], axis=0, ignore_index=True)

df['year_month'] = pd.to_datetime(df['year_month'])
df.index = pd.to_datetime(df['year_month'], format = '%Y%m%d')

def jez(df):

    df1 = df.assign(year_month = df['year_month'].dt.to_period('m')).groupby(['ID'])['year_month'].agg(['min', 'max'])
    df1 = df1.loc[df1.index.repeat( df1['max'].astype('int').sub(df1['min'].astype('int')) + 1)]
    df1 = (df1['min'] +  df1.groupby(level=0).cumcount()).dt.to_timestamp().reset_index(name='year_month')
    
    return df1.merge(df.rename_axis(None), how='left')

def vogel(df):
    min_d = df['year_month'].min()
    max_d = df['year_month'].max()
    
    # generate all possible combinations of date and ID
    df_agg = df.groupby(['ID'])['year_month'].agg(['min', 'max'])
    df = pd.DataFrame(
        index=pd.MultiIndex.from_product(
            [pd.date_range(min_d, max_d, freq='MS'), df_agg.index]
        )
    )
    
    # reduce to only relevant dates
    df = df.merge(df_agg, left_on='ID', right_index=True)
    df = df.reset_index().rename(columns={'level_0': 'year_month'})
    df = df[df['year_month'].between(df['min'], df['max'])]
    df = df.drop(columns=['min', 'max'])
    
    # add product information
    df = df.merge(df, how='left')
        
    return  df

Python-3.x相关问答推荐

在Python中基于组/ID将两个数据帧进行映射,找出较接近的值

Pandas教程:如何更新行内数值的位置

通过 Pandas 通过用户定义函数重命名数据框列

如何使用 Selenium 和 Python 作为线程来使用事件(Chrome-Developer-Tools)?

Pandas 窗口聚合两个排序表

包含值超出范围的 ID 的新 DataFrame 列?

为什么 return node.next 会返回整个链表?

Jupyter Notebook 拒绝打印一些字符串

在判断列表变量时如何判断特定列的值并分配加权整数值

如何将虚拟变量列转换为多列?

每个数据行中每个数据帧值的总和

如何禁用 pylint 禁止自用警告?

基本 Flask 应用程序未运行(TypeError:模块中缺少必填字段type_ignores)

使用逗号时,除了处理程序中的语法无效

try 注释散列变量时,ABCMeta对象不可下标

AttributeError:LinearRegression 对象没有属性coef_

如何将 Matplotlib 图形转换为 PIL Image 对象(不保存图像)

使用 python2 和 python3 创建一个 virtualenv

计数大于Pandas groupby 中的值的项目

Python 3 中的连接列表