【问题标题】:Efficient use of Numpy to process in blocks of rows高效使用 Numpy 以行块的形式进行处理
【发布时间】:2018-06-19 13:11:40
【问题描述】:

我需要遍历一组唯一帐户(下面示例代码中的 AccountID)并为每个唯一 AccountID 计算一系列特征(目前仅显示 TargetCol 作为示例)。实际上,我正在将 csv 文件作为 Pandas 数据帧(1M 行)读取,然后转换为 Numpy 记录数组,以便我仍然可以在循环中引用标题名称。我解决这个问题的方法是为每个唯一的 AccountID 创建一个切片,为每个切片计算 TargetCol,然后将这些切片连接在一起。

我下面的代码工作正常,但我很确定它可以以更有效的方式完成(高效是指减少处理时间)。

%%time
import pandas as pd
import numpy as np
from numpy.random import randn

x=300 #make x higher to test more records
df = pd.DataFrame(randn(x,3),columns=['AccountID','Bcol','Ccol'])
for m,row in df.iterrows():
    df.loc[m,'AccountID'] = np.random.randint(int(x/10))
    df.loc[m,'Bcol'] = np.int(np.random.uniform(low=0.0, high=1000.0, size=None))/10000
    df.loc[m,'Ccol'] = np.int(np.random.uniform(low=0.0, high=1000.0, size=None))/10000

df['TargetCol']=np.nan
dfnum = df.to_records(index=False)
dfnum = np.sort(dfnum, order=['AccountID']) 
pd.DataFrame(dfnum)

uniquelist = np.unique(dfnum['AccountID'])
for u in range(0,len(uniquelist)):
    dfslice = dfnum[dfnum['AccountID'] == uniquelist[u]]
    for i in range(0,len(dfslice)):
        if (len(dfslice) - i) >= 6:
            dfslice['TargetCol'][i] = np.nansum(dfslice['Bcol'][i:i+6]) / dfslice['Ccol'][i]
        else:
            dfslice['TargetCol'][i] = np.NaN
    if u==0:
        dfconcat = dfslice
    else:
        dfconcat = np.concatenate((dfconcat, dfslice),axis=0)

pd.DataFrame(dfconcat)

【问题讨论】:

    标签: python-3.x loops numpy


    【解决方案1】:

    IIUC 我认为你需要:

    import pandas as pd
    df = pd.DataFrame({'AccountID': [1,  1,       1, 2,   1,  2,  1,      2, 2],
                       'RefDay':    [1,  2,       3, 1,   4,  2,  5,      3, 4],
                       'BCol':      [1., 2., np.nan, 1., 3., 2., 1., np.nan, 2.] ,
                       'CCol':      [3., 2.,     3., 1., 3., 4., 5.,     2., 1.] })
    df = df.sort_values(by=['AccountID','RefDay']).reset_index(drop=True)
    
    # Replace with 6 in real data
    periods = 3
    result = df.groupby('AccountID').apply(lambda g: g['BCol'].fillna(0).rolling(periods).sum().shift(-periods + 1) / g['CCol'])
    df['TargetColumn'] = result.sortlevel(1).values
    print(df)
    

    输出:

       AccountID  BCol  CCol  RefDay  TargetColumn
    0          1   1.0   3.0       1      1.000000
    1          1   2.0   2.0       2      2.500000
    2          1   NaN   3.0       3      1.333333
    3          1   3.0   3.0       4           NaN
    4          1   1.0   5.0       5           NaN
    5          2   1.0   1.0       1      3.000000
    6          2   2.0   4.0       2      1.000000
    7          2   NaN   2.0       3           NaN
    8          2   2.0   1.0       4           NaN
    

    【讨论】:

    • 谢谢。我认为如果我添加一个 RefDay 列会更清楚: df = pd.DataFrame({'AccountID': [1, 1, 1, 2, 1, 2, 1, 2, 2], 'RefDay': [1 , 2, 3, 1, 4, 2, 5, 3, 4], 'BCol': [1., 2., np.nan, 1., 3., 2., 1., np.nan, 2 .] , 'CCol': [3., 2., 3., 1., 3., 4., 5., 2., 1.] }) df = df.sort_values(by=['AccountID', 'RefDay']) 这样,TargetColumn 的值应该是:1|2.5|1.33|NaN|NaN|3|1|NaN|NaN
    • @GivenX 好的,我已经添加了这一点,不确定这是否正是您所需要的......否则最好在问题中有一个完整的输入和预期输出示例。
    • 超优雅超快速!谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-07-22
    • 2011-08-30
    • 2017-12-13
    • 2016-10-21
    • 1970-01-01
    • 2013-06-02
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多