【问题标题】:How can I filter each subset of a dataframe for 2 conditions?如何针对 2 个条件过滤数据帧的每个子集?
【发布时间】:2021-02-17 15:22:58
【问题描述】:

我需要检查销售数据的 2 个条件:

  1. 在特定年份出售
  2. 在特定天数出售,金额 > 0

数据框df:

date       | id  | qual | amount
2020-09-01 | 123 | A    | 100
2020-09-02 | 123 | A    | 0
2020-09-03 | 123 | A    | 90
2020-09-04 | 123 | A    | 80
2020-09-01 | 123 | B    | 8
2020-09-02 | 123 | B    | 6
2020-09-03 | 123 | B    | 4
2020-09-04 | 123 | B    | 2
2021-02-01 | 123 | B    | 18
2020-02-01 | 456 | A    | 96
2021-02-02 | 456 | A    | 90
2021-01-01 | 789 | A    | 30
2021-01-02 | 789 | A    | 31
2021-01-03 | 789 | A    | 32
2021-01-04 | 789 | A    | 29

数据框有 10_000 个 ID,每个 ID 大约有 1000 个日期,每个 ID 有 1 或 2 个质量(质量)级别。

需要对ID + qual level的每个组合进行检查。

检查每个 ID + Qual 后,我想过滤我的数据框,使其仅包含通过该检查的 ID + Qual 组合。

ID:123,质量:A

  • 有 2020 年和 2021 年的销售量❌
  • 至少有 4 行数量 > 0 ❌
  • ->不通过

ID:123,质量:B

  • 有 2020 年和 2021 年的销售量✅
  • 至少有 4 行数量 > 0 ✅
  • -> 确实通过了

ID:456,质量:A

  • 有 2020 年和 2021 年的销售量✅
  • 至少有 4 行数量 > 0 ❌
  • ->不通过

ID:789,质量:A

  • 有 2020 年和 2021 年的销售量❌
  • 至少有 4 行数量 > 0 ✅
  • ->不通过

因此结果应如下所示:

date       | id  | qual | amount
2020-09-01 | 123 | B    | 8
2020-09-02 | 123 | B    | 6
2020-09-03 | 123 | B    | 4
2020-09-04 | 123 | B    | 2
2021-02-01 | 123 | B    | 18

到目前为止我的代码:

required_sales_years= [2020, 2021]
required_sales_days = 4

has_required_sales = []
for id in df["id"].unique().tolist():
    for qual in df["qual"].unique().tolist():
        temp = df.query(
            "id== @id and qual == @qual and amount > 0"
        )
        sales_years = temp["date"].dt.year.unique().tolist()
        check_sales_year = all(item in sales_years for item in required_sales_years)
        check_sales_days = len(temp.index) >= required_sales_days
        if check_sales_year and check_sales_days:
            has_required_sales.append((id, qual))

我该怎么做?

【问题讨论】:

    标签: python pandas date


    【解决方案1】:

    使用groupby().transform统计有效销售额:

    required_sale_years = [2020, 2021]
    required_sales_days = 4
    
    # intermediate variables
    df['year'] = df.date.dt.year
    df['valid'] = df['year'].isin(required_sales_years) & df['amount'].gt(0)
    
    # groupby
    groups = df.groupby(['id','qual'])
    
    has_years = groups['year'].transform(lambda x: set(required_sales_years).issubset(set(x)))
    valid_sales = groups['valid'].transform('sum') >= required_sales_days
    
    output = df[has_years & valid_sales]
    

    输出:

            date   id qual  amount  year  valid
    4 2020-09-01  123    B       8  2020   True
    5 2020-09-02  123    B       6  2020   True
    6 2020-09-03  123    B       4  2020   True
    7 2020-09-04  123    B       2  2020   True
    8 2021-02-01  123    B      18  2021   True
    

    【讨论】:

    • 这很好,但您缺少日期字段中的条件以检查年份是否为 2020 年
    • 我认为您需要以某种方式将年份包含在组中,并将原始日期返回到索引中
    • @Robert 我明白了,错过了那部分。更新了答案。谢谢。
    • 谢谢,但您只检查 test["date"].dt.year.isin(required_sale_years),如果 2020 年只有销售,则它会通过。
    • 我不明白你的意思?你不是只计算那些年的(正)销售额吗?还是其他年份的正销售额也算在内?
    【解决方案2】:

    在 pandas 中你可以更轻松地做到这一点。

    要根据条件选择子集,您可以这样做:

    subset = df[df["Column"] == value]
    

    您还可以使用 & (AND) 和 | 将条件与 OR、AND 链接起来(或)

    所以这看起来像这样:

    subset = df[(df["Column"] == value) & (df["OtherColumn"] == othervalue)]
    

    ID: 123 with qual: A 有 2020 年的销售额,但只有 3 行金额 > 0 -> 不通过

    ID: 123 with qual: B has year 2020 in sales and >= 4 rows with amount > 0 -> 确实通过了

    有了上面提到的这两个条件,你可以这样做:

     df[(df["ID"] == 123) & (df["qual"] == "A") & ("2020" in df["date"]) & (df["amount"] >= 4)]
    

    【讨论】:

    • 谢谢,但我不知道哪个 ID 符合条件(来自 10_000 个 ID),所以我不能只做 (df["ID"] == 123)?
    • 好吧,我没有理解正确的问题,它需要通过的具体条件是什么?您帖子开头的两个条件有点模糊
    • 需要通过的条件是:amount>3?如果是这样,请从我的答案中删除一些条件
    • 条件 2 是:销售天数,数量 > 0。因此,如果行数 >= required_sales_days 数字,则需要检查 date+id+qual 数量 > 0 的每个组合。跨度>
    猜你喜欢
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-24
    • 2020-11-07
    • 2018-06-07
    • 1970-01-01
    相关资源
    最近更新 更多