【问题标题】:Conditionally fill column with value from another DataFrame based on row match in Pandas根据 Pandas 中的行匹配,有条件地用来自另一个 DataFrame 的值填充列
【发布时间】:2017-10-07 12:02:25
【问题描述】:

我发现自己在尝试解决这个问题时迷失了方向(自动化税务文书工作)。我有两个数据框:一个是欧元/美元汇率的季度历史记录,另一个是我自己的发票,例如:

import pandas as pd
import numpy as np

usdeur = [(pd.Timestamp('20170705'),1.1329),
          (pd.Timestamp('20170706'),1.1385),
          (pd.Timestamp('20170707'),1.1412),
          (pd.Timestamp('20170710'),1.1387),
          (pd.Timestamp('20170711'),1.1405),
          (pd.Timestamp('20170712'),1.1449)]
labels = ['Date', 'Rate']
rates = pd.DataFrame.from_records(usdeur, columns=labels)

transactions = [(pd.Timestamp('20170706'), 'PayPal',     'USD', 100, 1),
                (pd.Timestamp('20170706'), 'Fastspring', 'USD', 200, 1),
                (pd.Timestamp('20170709'), 'Fastspring', 'USD', 100, 1),
                (pd.Timestamp('20170710'), 'EU',         'EUR', 100, 1),
                (pd.Timestamp('20170710'), 'PayPal',     'USD', 200, 1)]
labels = ['Date', 'From', 'Currency', 'Amount', 'Rate']
sales =pd.DataFrame.from_records(transactions, columns=labels)

导致:

我需要在sales['Rate'] 列中填充来自rates['Rate'] 的正确汇率,也就是说:

  • 如果 sales['Currency'] 是 'EUR',别管它。
  • 对于sales的每一行,找到rates中匹配'Date'的行;抓住rates['Rate'] 的值并将其放入sales['Rate']
  • 奖励:如果没有匹配的'Date'(例如在假期期间,交易所休市),请检查上一行,直到找到合适的值。

完整结果应如下所示(注意第 2 行的汇率为 2017-07-07):

我尝试从其他问题中遵循几个建议的解决方案,但没有成功。 提前非常感谢您

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    您可以更改您的费率数据框以包含所有日期,然后向前填充,在您的费率数据框中创建一个名为“货币”的列,然后在日期和货币列上加入两个 df。

    idx = pd.DataFrame(pd.date_range('2017-07-05', '2017-07-12'),columns=['Date'])
    rates = pd.merge(idx,rates,how="left",on="Date")
    rates['Currency'] = 'USD'
    rates['Rate'] = rates['Rate'].ffill()           
    
         Date   Rate    Currency
    0   2017-07-05  1.1329  USD
    1   2017-07-06  1.1385  USD
    2   2017-07-07  1.1412  USD
    3   2017-07-08  1.1412  USD
    4   2017-07-09  1.1412  USD
    5   2017-07-10  1.1387  USD
    6   2017-07-11  1.1405  USD
    7   2017-07-12  1.1449  USD
    

    然后进行左连接将给出:

    result = pd.merge(sales,rates,how="left",on=["Currency","Date"])
    result['Rate'] = np.where(result['Currency'] == 'EUR', 1, result['Rate_y'])
    result = result.drop(['Rate_x','Rate_y'],axis =1)
    

    会给:

         Date         From      Currency    Amount  Rate
    0   2017-07-06  PayPal           USD    100 1.1385
    1   2017-07-06  Fastspring       USD    200 1.1385
    2   2017-07-09  Fastspring       USD    100 1.1412
    3   2017-07-10  EU               EUR    100 1.0000
    4   2017-07-10  PayPal           USD    200 1.1387
    

    【讨论】:

    • 这是一个很好的进步,谢谢!它仍然在第 2 行留下 NaN,而在这种情况下,它应该用 rates['Rate'][2] 填充它
    • 是的,谢谢 - 我已经看到修改后的答案,谢谢。巧妙的技巧,填写缺少的日期,然后ffill
    【解决方案2】:

    我使用pd.merge_asof分解步骤

    sales=pd.merge_asof(sales,rates,on='Date',direction='backward',allow_exact_matches =True)
    sales.loc[sales.From=='EU','Rate_y']=sales.Rate_x
    
    sales
    Out[748]: 
            Date        From Currency  Amount  Rate_x  Rate_y
    0 2017-07-06      PayPal      USD     100       1  1.1385
    1 2017-07-06  Fastspring      USD     200       1  1.1385
    2 2017-07-09  Fastspring      USD     100       1  1.1412
    3 2017-07-10          EU      EUR     100       1  1.0000
    4 2017-07-10      PayPal      USD     200       1  1.1387
    

    然后

    sales.drop('Rate_x',1).rename(columns={'Rate_y':'Rate'})
    Out[749]: 
            Date        From Currency  Amount    Rate
    0 2017-07-06      PayPal      USD     100  1.1385
    1 2017-07-06  Fastspring      USD     200  1.1385
    2 2017-07-09  Fastspring      USD     100  1.1412
    3 2017-07-10          EU      EUR     100  1.0000
    4 2017-07-10      PayPal      USD     200  1.1387
    

    【讨论】:

    • 这太棒了!
    • @Gayatri 注意你的回答也很好:)
    • @DavideBarranca yw:-)
    【解决方案3】:

    这是我在不合并的情况下的做法。 1. 填写缺失日期的比率,并像其他答案一样填写,但将日期保留为索引。 2. 将此数据框映射到销售,使用 loc 不包括带有 EUR 的行

    idx = pd.date_range(rates['Date'].min(), rates['Date'].max())
    rates = rates.set_index('Date').reindex(idx).ffill()
    sales.loc[sales['Currency'] != 'EUR','Rate'] = sales.loc[sales['Currency'] != 'EUR','Date'].map(rates['Rate'])
    
        Date        From        Currency    Amount  Rate
    0   2017-07-06  PayPal      USD         100     1.1385
    1   2017-07-06  Fastspring  USD         200     1.1385
    2   2017-07-09  Fastspring  USD         100     1.1412
    3   2017-07-10  EU          EUR         100     1.0000
    4   2017-07-10  PayPal      USD         200     1.1387
    

    或者你甚至可以在不改变数据帧速率的情况下做到这一点

    mapper = rates.set_index('Date').reindex(sales['Date'].unique()).ffill()['Rate']
    
    sales.loc[sales['Currency'] != 'EUR','Rate'] = sales.loc[sales['Currency'] != 'EUR','Date'].map(mapper)
    

    时间测试:

    wen:       0.011892538983374834
    gayatri:   0.13312408898491412
    vaishali : 0.009498710976913571
    

    【讨论】:

      猜你喜欢
      • 2021-11-23
      • 2019-12-10
      • 1970-01-01
      • 2017-10-12
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多