![](/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.