我想知道是否有一种方法可以优化我正在执行的搜索。我有一个多索引(3级)数据框df
,如下所示:
IndexID IndexDateTime IndexAttribute ColumnA ColumnB
1 2015-02-05 8 A B
1 2015-02-05 7 C D
1 2015-02-10 7 X Y
我的问题是我想知道某个给定的日期,例如2015-02-10
是否在ColumnA
中有数据,而IndexID
和IndexAttribute
相同,给定的一天(在这种情况下为5)之前的日期(如果有的话),将其获取并添加到新列中,如下所示:
IndexID IndexDateTime IndexAttribute ColumnA ColumnB NewColumn
1 2015-02-05 8 A B -1
1 2015-02-05 7 C D -1
1 2015-02-10 7 X Y C
我想对数据帧中的每一行执行此搜索,该数据帧有1900万行。我这样做的方式是:
df['NewColumn'] = df.apply(lambda r: get_data(df, r.IndexID, r.IndexDateTime , r.IndexAttribute , 5), axis=1)
get_data
在哪里:
def get_data(df, IndexID, IndexDateTime , IndexAttribute , days_before):
idx = pd.IndexSlice
date = (IndexID - pd.to_timedelta(days_before, 'd'))
try:
res = df.loc[idx[IndexID, date, IndexAttribute ],'ColumnA']
return res
except KeyError:
return -1
这非常慢,耗时超过2个小时。我想知道这是否是更快的方法。 问题:
IndexDateTame
,我不知道有多少个IndexAttributes。
它们是整数,按降序排列。我无法进行转换,因为我不知道两行中间有多少数据。有什么想法吗?谢谢!
答案 0 :(得分:1)
这是O(m.n)解决方案,但是应该比原始解决方案快
l = []
for _, y in df.groupby(level=[0, 2], sort=False):
s = y.index.get_level_values(level=1).values
l.append(((s - s[:, None]) / np.timedelta64(1, 'D') == -5).dot(y.ColumnA.values))
df['NewCOL'] = np.concatenate(l)
df
Out[48]:
ColumnA ColumnB NewCOL
IndexID IndexDateTime IndexAttribute
1 2015-02-05 8 A B
7 C D
2015-02-10 7 X Y C
答案 1 :(得分:1)
使用numpy可以非常快。您只需要将数据框中的列作为numpy数组进行迭代。希望对您有所帮助:
%time
def myfunc(df, days_before=5):
# Fill A column witH -1s
result = -np.ones_like(df.values[:, -1:])
# Slice the first 3 columns and shift the dates
# to get the index that we are looking for
idx = np.array((df['IndexID'].values,
df['IndexDateTime'] - pd.to_timedelta(days_before, 'd'),
df['IndexAttribute'].values)).T
# Look for days matching in the first 3 columns
_idx_comp = df.values[:, :3][np.newaxis, :] == np.array(idx)[:, np.newaxis]
# Get the index where there is a match
# between the row of the dataframe and the desired searched rows
idx_found = np.where(np.all(_idx_comp, axis=-1))
# Assign the corresponding rows to its required value
result[idx_found[0]] = df['ColumnA'].values[idx_found[-1]]
return result
df.assign(NewColumn=myfunc(df))
CPU times: user 2 µs, sys: 1e+03 ns, total: 3 µs
Wall time: 5.96 µs
IndexID IndexDateTime IndexAttribute ColumnA ColumnB NewColumn
0 1 2015-02-05 8 A B -1
1 1 2015-02-05 7 C D -1
2 1 2015-02-10 7 X Y C