根据另一个数据框计算数据框中的列值

时间:2020-06-14 10:40:38

标签: python pandas 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

我想在factored_quantity中计算一个名为df1的新列,如下所示。

df2中选择其日期大于df1中的日期并乘以qty的所有因子,得出factored_qty

所以我的最终数据帧看起来像

>>> 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

说明:

    2016-10-08中的
  1. df1小于2016-11-12中的2017-01-12df2。因此,乘以因子2*3*qty
  2. 不过,2016-12-08中的日期df1大于2016-11-12且小于2017-01-12的{​​{1}}。因此,只能乘以因子df2

我看过的大部分与
有关 1.合并两个数据框。
2.比较两个等长的数据帧
3.比较两个长度不相等的数据框

但是,当前问题与基于另一个数据帧的收集(乘以因子)的值(外键将不相等)计算值有关。

3 个答案:

答案 0 :(得分:2)

对于大型数据框而言,可能不是最快的解决方案,但它可以工作。我们在满足条件的prod的所有行上使用df2

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中添加一个前哨(df1的最大日期为1)。
该方法比Ch3steR和sammywemmy的解决方案要快得多。

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)


为较大的数据帧(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中的随机因素,其乘积可能会导致溢出,在此将其忽略。 method_dict仅在df2中的最后一个日期大于df1中的最后一个日期时才能正常工作,并且在计时中也将忽略此时间。

答案 1 :(得分:2)

确保您的 if let url = Bundle.main.url(forResource: "SomeAudioFile", withExtension: "m4a") { player.removeAllItems() player.insert(AVPlayerItem(url: url), after: nil) player.play() } dtype是有效的日期时间,否则请使用pd.to_datetime。然后,您可以使用pd.Series.to_numpy并在此处使用broadcasting来比较和构建用于布尔索引的布尔数组。然后使用pd.Series.map,使用np.prod获得整个数组的乘积。

date

OR

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)

答案 2 :(得分:1)

转换为日期时间:

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()}

创建数据框:

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