【问题标题】:Pandas Remove Duplicate Rows Based on ConditionPandas 根据条件删除重复行
【发布时间】:2021-04-02 08:10:12
【问题描述】:

我有一个熊猫数据框如下

+---------+------------+------------+-------+--------+
| Product |    Date    |  Adj_Date  | Price | Factor |
+---------+------------+------------+-------+--------+
| A       | 01-06-2020 | 01-07-2020 |   100 |     10 |
| A       | 01-06-2020 | 01-08-2020 |   200 |     20 |
| B       | 15-07-2020 | 01-07-2020 |   400 |     10 |
| B       | 15-07-2020 | 01-08-2020 |   800 |     10 |
| C       | 01-09-2020 | 01-07-2020 |  1000 |     10 |
| C       | 01-09-2020 | 01-08-2020 |  1200 |     10 |
| D       | 01-10-2020 | 01-11-2020 |  1400 |     10 |
| E       | 01-10-2020 | 01-09-2020 |  1600 |     10 |
+---------+------------+------------+-------+--------+

上面生成数据框的代码:

data = {'Product':['A', 'A', 'B', 'B', 'C', 'C', 'D', 'E'],
        'Date':['01-06-2020', '01-06-2020', '15-07-2020', '15-07-2020', '01-09-2020', '01-09-2020', '01-10-2020', '01-10-2020'],
        'Adj_Date':['01-07-2020', '01-08-2020', '01-07-2020', '01-08-2020', '01-07-2020', '01-08-2020', '01-11-2020', '01-09-2020'],
        'Price':[100, 200, 400, 800, 1000, 1200, 1400, 1600],
        'Factor':[10,20, 10, 10, 10, 10, 10, 10]}
df =pd.DataFrame(data)

期望的输出:

+---------+------------+------------+-------+--------+--------------+
| Product |    Date    |  Adj_Date  | Price | Factor | Actual Price |
+---------+------------+------------+-------+--------+--------------+
| A       | 01-06-2020 | 01-07-2020 |   100 |     10 |           10 |
| B       | 15-07-2020 | 01-08-2020 |   800 |     10 |           80 |
| C       | 01-09-2020 | 01-07-2020 |  1000 |     10 |         1000 |
| D       | 01-10-2020 | 01-11-2020 |  1400 |     10 |          140 |
| E       | 01-10-2020 | 01-09-2020 |  1600 |     10 |         1600 |
+---------+------------+------------+-------+--------+--------------+

以上结果基于比较DateAdj_Date 两列。如果对于任何产品有 2 行,我们选择其中Date 小于Adj_Date 并且Adj_Date 之间的差异最小的行。正如我们在产品 A 中看到的那样,我们在两行中都有 date = 01-06-2020 这个日期小于 Adj_Date,但我们选择 Adj_Date = 01-07-2020 因为与这个日期的差异是最小的。使用相同的逻辑,我们在产品 B 的情况下选择第 2 行。 如果Date 在所有情况下都大于Adj_Date,则我们保留第一行。

下一部分是创建列Actual Price。一旦我们为每个产品设置了单行,我们将PriceFactor 相除以创建Actual Price,前提是Date 小于Adj_Date。否则Actual Price 等于Price

怎样才能达到这个结果?

【问题讨论】:

  • 请向我们展示您到目前为止所做的尝试。我们可以通过这种方式更好地为您提供帮助。

标签: pandas dataframe group-by duplicates


【解决方案1】:

首先,将DateAdj_Date 转换为时间戳。这将使您的生活更轻松:

for col in ['Date', 'Adj_Date']:
    df[col] = pd.to_datetime(df[col], dayfirst=True)

然后:

# Pick one row for each product
def pick_one(group):
    if len(group) == 1:
        return group

    diff = (group['Date'] - group['Adj_Date']).dt.days
    if (diff < 0).any():
        cond = group.index == diff[diff < 0].idxmax()
    else:
        cond = group.index == group.index[0]

    return group.loc[cond]

result = df.groupby('Product', as_index=False).apply(pick_one).droplevel(0)

# Calculate the Actual Price
result['Actual Price'] = np.where(result['Date'] < result['Adj_Date'], result['Price'] / result['Factor'], result['Price'])

【讨论】:

    【解决方案2】:

    您可以使用duplicatedgroupby.idxmin 过滤您的数据框,然后应用布尔值来获取您的ActualPrice 列。

    import numpy as np
    # ensure you have valid datetime objects.
    #df[['Date','Adj_Date']] = df[['Date','Adj_Date']].apply(pd.to_datetime)
    df1 = df.loc[
        df.assign(
            delta=np.where(
                df.duplicated(subset=["Product"], keep=False),
                (df["Date"] - df["Adj_Date"]).abs(),
                0,
            )
        )
        .groupby("Product")["delta"]
        .idxmin()
    ]
    

    df1['ActualPrice'] = np.where(
        df1['Date'] <= df1['Adj_Date'],
        df1['Price'].div(df1['Factor']),
        df1['Price']
    )
    

    print(df1)
    
      Product       Date   Adj_Date  Price  Factor  ActualPrice
    0       A 2020-01-06 2020-01-07    100      10         10.0
    3       B 2020-07-15 2020-01-08    800      10        800.0
    5       C 2020-01-09 2020-01-08   1200      10       1200.0
    6       D 2020-01-10 2020-01-11   1400      10        140.0
    7       E 2020-01-10 2020-01-09   1600      10       1600.0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-14
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      相关资源
      最近更新 更多