簡體   English   中英

基於另一個 dataframe 計算 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

解釋:

  1. df1中的2016-10-08小於df22016-11-122017-01-12 所以乘以因子2*3*qty
  2. 但是, df1中的日期2016-12-08大於df22016-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 的最大日期)。
這種方法明顯快於 Ch3steR 和 sammywemmy 的解決方案。

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


較大數據幀的時間(df1 200 行,df2 100 行

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM