Pandas,根据列值的唯一子集附加列

时间:2016-05-30 13:52:32

标签: python pandas optimization dataframe

我有一个包含很多行的数据框。我使用自定义函数生成的数据附加一列,如下所示:

import numpy

df['new_column'] = numpy.vectorize(fx)(df['col_a'], df['col_b'])
# takes 180964.377 ms

工作正常,我想要做的就是加快速度。实际上只有一小组col_acol_b的独特组合。许多迭代都是多余的。我想也许pandas可能会自己解决这个问题,但我不认为是这种情况。考虑一下:

print len(df.index) #prints 127255
df_unique = df.copy().drop_duplicates(['col_a', 'col_b'])
print len(df_unique.index) #prints 9834

我也确信通过运行这个可能的加速:

df_unique['new_column'] = numpy.vectorize(fx)(df_unique['col_a'], df_unique['col_b'])
# takes 14611.357 ms

由于存在大量冗余数据,我要做的是更新大型数据帧(df 127255行),但只需要运行fx函数最少次数( 9834次)。这是因为col_acol_b的所有重复行。当然,这意味着df中有多个行具有col_acol_b的相同值,但是没关系,df的其他列不同并使每一行都是唯一的。

在我创建一个正常的迭代循环以循环遍历df_unique数据帧并在df上进行条件更新之前,我想问一下是否有更多“pythonic”整齐的方法来做这个有点更新。非常感谢。

**更新**

我创建了上面提到的简单for循环,如下所示:

df = ...
df_unique = df.copy().drop_duplicates(['col_a', 'col_b'])
df_unique['new_column'] = np.vectorize(fx)(df_unique['col_a'], df_unique['col_b'])
for index, row in df_unique.iterrows():         
    df.loc[(df['col_a'] == row['col_a']) & (df['col_b'] == row['col_b']),'new_column'] = row['new_column']
# takes 165971.890

因此,使用此for循环可能会略微提升性能但不会达到我预期的水平。

FYI

这是fx函数。它查询mysql数据库。

def fx(d):
    exp_date = datetime.strptime(d.col_a, '%m/%d/%Y')
    if exp_date.weekday() == 5:
        exp_date -= timedelta(days=1)

    p = pandas.read_sql("select stat from table where a = '%s' and b_date = '%s';" % (d.col_a,exp_date.strftime('%Y-%m-%d')),engine)
    if len(p.index) == 0:
        return None
    else:
        return p.iloc[0].close

2 个答案:

答案 0 :(得分:1)

<强>更新

如果您可以设法将属于['stat','a','b_date']表的三列table读入tab DF,那么您可以将其合并为:

tab = pd.read_sql('select stat,a,b_date from table', engine)
df.merge(tab, left_on=[...], right_on=[...], how='left')

OLD回答:

您可以将预先计算的df_unique DF与原始df DF合并/加入:

df['new_column'] = df.merge(df_unique, on=['col_a','col_b'], how='left')['new_column']

答案 1 :(得分:1)

MaxU的回答可能已经是你想要的了。但我会展示另一种方法可能会更快(我没有测量)。

我认为:

  1. df[['col_a', 'col_b']]已排序,以便所有相同的条目都在连续的行中(这很重要)

  2. df有一个唯一索引(如果没有,您可以创建一些临时唯一索引)。

  3. 我会使用df_unique.indexdf.index的子集这一事实。

    # (keep='first' is actually default)
    df_unique = df[['col_a', 'col_b']].drop_duplicates(keep='first').copy()
    
    # You may try .apply instead of np.vectorize (I think it may be faster):
    df_unique['result'] = df_unique.apply(fx, axis=1)
    
    # Main part:
    df['result'] = df_unique['result']                     # uses 2.
    df['result'].fillna(method='ffill', inplace=True)      # uses 1.