![](/img/trans.png)
[英]Computing column in dataframe based on nearest value in another dataframe
[英]Computing values of a column in dataframe based on another dataframe
考虑以下两个数据帧(长度不等)。
df1 = pd.DataFrame({'date': ['2016-10-08', '2016-11-08','2016-12-08','2017-01-08'], 'qty': [1,8,2,4]})
df2 = pd.DataFrame({'date': ['2016-11-12', '2017-01-12'], 'factor': [2,3]})
>>> df1
date qty
0 2016-10-08 1
1 2016-11-08 8
2 2016-12-08 2
3 2017-01-08 4
>>> df2
date factor
0 2016-11-12 2
1 2017-01-12 3
我想在df1
中计算一个名为factored_quantity
的新列,如下所示。
选择
df2
中所有日期大于df1
日期的因子,并将qty
乘以相同的数量以到达factored_qty
所以我最终的 dataframe 看起来像
>>> df1
date qty factored_qty
0 2016-10-08 1 6
1 2016-11-08 8 48
2 2016-12-08 2 6
3 2017-01-08 4 12
解释:
df1
中的2016-10-08
小于df2
的2016-11-12
和2017-01-12
。 所以乘以因子2*3*qty
df1
中的日期2016-12-08
大于df2
的2016-11-12
并且小于2017-01-12
。 所以只乘以因子3*qty
我看过的大部分内容都与
1.合并两个dataframe。
2.比较两个等长的dataframe
3.比较两个不等长的dataframe
但是,当前问题与基于另一个 dataframe 的收集(因子相乘)值计算值有关 - 其中外键将不相等。
可能不是大型数据帧的最快解决方案,但它有效。 我们在满足条件的df2
的所有行上使用prod
。
df1['factored_qty'] = df1.apply(lambda x: df2[df2.date>x.date].factor.prod() * x.qty,axis=1)
结果:
date qty factored_qty
0 2016-10-08 1 6
1 2016-11-08 8 48
2 2016-12-08 2 6
3 2017-01-08 4 12
merge_asof
。
我们计算反向cumprod
,即从最后一行到第一行。
不幸的是,如果 df2 中的最后一个日期小于 df1 中的最后一个日期,它会变得有点复杂,因为在这种情况下,我们必须向 df2 添加一个标记(因子 1 的 df1 的最大日期)。
method_apply 220 ms ± 5.99 ms per loop
method_numpy 86.7 ms ± 2.51 ms per loop
method_dict 80.7 ms ± 436 µs per loop
method_merge 8.87 ms ± 68.1 µs per loop
import pandas as pd import numpy as np n = 100 np.random.seed(0) df1_ = pd.DataFrame({'date': [(pd.Timestamp('2020-06-01') - pd.Timedelta(x,'D')).strftime('%Y-%m-%d') for x in np.sort(np.random.choice(200*n, 2*n, False))[::-1]], 'qty': np.random.randint(1, 20, 2*n)}) df2_ = pd.DataFrame({'date': [(pd.Timestamp('2020-06-01') - pd.Timedelta(x,'D')).strftime('%Y-%m-%d') for x in np.sort(np.random.choice(100*n, n, False))[::-1]], 'factor': np.random.randint(1, 10, n)}) def setup(): global df1, df2 df1 = df1_.copy(True) df2 = df2_.copy(True) def method_apply(): df1['factored_qty'] = df1.apply(lambda x: df2[df2.date>x.date].factor.prod() * x.qty,axis=1) return df1 def method_merge(): df3 = pd.merge_asof(df1.assign(date=pd.to_datetime(df1.date)), df2.assign(date=pd.to_datetime(df2.date), factor=df2.factor.iloc[::-1].cumprod().iloc[::-1]) if(df1.date.max()<df2.date.max()) else df2.assign(date=pd.to_datetime(df2.date), factor=df2.factor.iloc[::-1].cumprod().iloc[::-1]).append({'date': pd.to_datetime(df1.date.max()), 'factor': 1}, ignore_index=True), 'date', direction='forward') df3.factor *= df3.qty df3.rename(columns={'factor': 'factored_qty'}, inplace=True) return df3 from itertools import product from collections import defaultdict def method_dict(): d = defaultdict(list) df1['date'] = pd.to_datetime(df1['date']) df2['date'] = pd.to_datetime(df2['date']) for (date1, qty), (date2, factor) in product(df1.to_numpy(),df2.to_numpy()): if date1 < date2: d[(date1, qty)].append(factor) outcome = {k:[s,np.prod((s,*v))] for (k,s),v in d.items()} return pd.DataFrame.from_dict(outcome, orient='index', columns=['qty','factored_qty']).reset_index() def method_numpy(): mask = df1.date.to_numpy()[:,None] < df2.date.to_numpy() it = iter(mask) def mul(x): val = np.prod(df2.loc[next(it),'factor']) return x*val df1['factored_qty'] = df1['qty'].map(mul) return df1
结果:
method_apply 220 ms ± 5.99 ms per loop method_numpy 86.7 ms ± 2.51 ms per loop method_dict 80.7 ms ± 436 µs per loop method_merge 8.87 ms ± 68.1 µs per loop
根据 df2 中的随机因素,他们的产品可能会导致溢出,这里忽略了这一点。 仅当 df2 中的最后一个日期大于 df1 的最后一个日期时, method_dict
才能正常工作,这对于计时也被忽略了。
确保您的date
dtype 是有效的 datetime 否则使用pd.to_datetime
。 然后您可以使用pd.Series.to_numpy
并在此处使用broadcasting
来比较和构建 boolean 数组以用于 boolean 索引。 然后使用pd.Series.map
,使用np.prod
得到整个阵列的产品。
mask = df1.date.to_numpy()[:,None] < df2.date.to_numpy() # `_.values` should be avoided instead use `_.to_numpy()`
it = iter(mask)
def mul(x):
val = np.prod(df2.loc[next(it),'factor'])
return x*val
df1['factored_qty'] = df1['qty'].map(mul)
df1
date qty factored_qty
0 2016-10-08 1 6
1 2016-11-08 8 48
2 2016-12-08 2 6
3 2017-01-08 4 12
或者
mask = df1.date.to_numpy()[:,None] < df2.date.to_numpy()
l = [np.prod(df2.loc[idx,'factor']) for idx in mask]
# df2.loc[idx,'factor'].prod()
df['factored_qty'] = df1.qty.mul(l)
时间分析:
# My answer
In [163]: %%timeit
...: mask = df1.date.to_numpy()[:,None] < df2.date.to_numpy() # `_.values` should be avoided instead use `_.to_num
...: py()`
...: it = iter(mask)
...:
...: def mul(x):
...: val = np.prod(df2.loc[next(it),'factor'])
...: return x*val
...: df1['factored_qty'] = df1['qty'].map(mul)
...:
...:
1.31 ms ± 28.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# Stef's answer.
In [164]: %%timeit
...: df1['factored_qty'] = df1.apply(lambda x: df2[df2.date>x.date].factor.cumprod().values[-1] * x.qty,axis=1)
...:
...:
3.65 ms ± 194 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
#sammy's answer
In [180]: %%timeit
...: d = defaultdict(list)
...: #iterate through data and append factors that meet criteria
...: for (date1, qty), (date2, factor) in product(df1.to_numpy(),df2.to_numpy()) :
...: if date1 < date2 :
...: d[(date1, qty)].append(factor)
...: outcome = {k:[s,np.prod((s,*v))] for (k,s),v in d.items()}
...: pd.DataFrame.from_dict(outcome, orient='index', columns=['qty','factored_qty']).reset_index()
...:
...:
1.49 ms ± 47.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
转换为日期时间:
df1['date'] = pd.to_datetime(df1['date'])
df2['date'] = pd.to_datetime(df2['date'])
将计算移至字典; 我想相信这样的计算在字典中会更快——这是一个假设; 希望有人进行测试并证明或揭穿这一点。
from itertools import product
from collections import defaultdict
d = defaultdict(list)
#iterate through data and append factors that meet criteria
for (date1, qty), (date2, factor) in product(df1.to_numpy(),df2.to_numpy()) :
if date1 <= date2 :
d[(date1, qty)].append(factor)
else:
d[(date1, qty)].append(1)
让我们看看d的内容:
print(d)
defaultdict(list,
{(Timestamp('2016-10-08 00:00:00'), 1): [2, 3],
(Timestamp('2016-11-08 00:00:00'), 8): [2, 3],
(Timestamp('2016-12-08 00:00:00'), 2): [1, 3],
(Timestamp('2017-01-08 00:00:00'), 4): [1, 3]})
得到过滤后的数据与数量的乘积:
outcome = {k:[s,np.prod((s,*v))] for (k,s),v in d.items()}
创建 dataframe:
pd.DataFrame.from_dict(outcome, orient='index', columns=['qty','factored_qty'])
qty factored_qty
2016-10-08 1 6
2016-11-08 8 48
2016-12-08 2 6
2017-01-08 4 12
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.