【问题标题】:more efficient way to get monthly counts in Python/Pyspark在 Python/Pyspark 中获取每月计数的更有效方法
【发布时间】:2021-08-04 01:39:08
【问题描述】:

我有一个表格 DF,如下所示

ID   Days
1    30
2    55
3    32
4    12
5    100
.....

我想得到如下计数:

month                           count
30 days and greater             20,000
60 days and greater             15,323
90 days and greater             11,232
.....
3600 days and greater           55

我的代码非常简单明了,我只是为每个月应用过滤器,并获取计数,然后复制并粘贴到 Excel 中,如下所示:

month1 = df.filter("Days >= 30").agg(countDistinct('ID')).show() 
month2 = df.filter("Days>= 60").agg(countDistinct('ID')).show() 
month3 = df.filter("Days >= 90").agg(countDistinct('ID')).show() ....

确实效率不高。

我想知道是否有更简单的方法可以做到这一点?并像这样创建一个表。

感谢高级!

【问题讨论】:

    标签: python sql apache-spark pyspark apache-spark-sql


    【解决方案1】:

    您可以额外制作一个month 列来捕获每个ID 的月份取决于天数,按该月聚合以获得每个月的计数

    (df
        .withColumn('month', F
            .when(F.col('days') >= 90, 3)
            .when(F.col('days') >= 60, 2)
            .when(F.col('days') >= 30, 1)
            .otherwise(0)
        )
        .groupBy('month')
        .agg(F.countDistinct('ID').alias('count_distinct_id'))
        .show()
    )
    
    # +-----+-----------------+
    # |month|count_distinct_id|
    # +-----+-----------------+
    # |    1|                3|
    # |    3|                1|
    # |    0|                1|
    # +-----+-----------------+
    

    【讨论】:

    • 感谢您的回答,但它只在第 1 个月回来。
    • 你可以在.when(F.col('days') >= 90, 3)上面添加更多条件,记住大的天在前,小的天在后面。
    【解决方案2】:

    您需要先将天数除以 30,然后对值进行分组:

    import pyspark.sql.functions as f
    from pyspark.shell import spark
    from pyspark.sql import Row
    from pyspark.sql.window import Window
    
    df = spark.createDataFrame([
        Row(ID=1, Days=30),
        Row(ID=2, Days=55),
        Row(ID=3, Days=32),
        Row(ID=4, Days=12),
        Row(ID=5, Days=100),
        Row(ID=6, Days=3600)
    ])
    
    # Calculating quantity of months
    df = df.withColumn('total_months', f.floor(f.col('Days') / f.lit(30)))
    
    # Group and count distinct
    df = df.groupBy('total_months').agg(f.countDistinct('ID').alias('count'))
    
    # Adding description
    df = df.withColumn('month', f.concat(f.col('total_months') * f.lit(30), f.lit(' days and greater')))
    
    # Cumulative sum
    window = Window.orderBy(f.col('total_months').desc()).rangeBetween(Window.unboundedPreceding, Window.currentRow)
    df = df.withColumn('count', f.sum('count').over(window))
    
    # Selecting only required columns and sorting asc
    (df
     .select('month', 'count')
     .sort('total_months')
     .show(truncate=False))
    

    输出

    +---------------------+-----+
    |month                |count|
    +---------------------+-----+
    |0 days and greater   |6    |
    |30 days and greater  |5    |
    |90 days and greater  |2    |
    |3600 days and greater|1    |
    +---------------------+-----+
    

    【讨论】:

    • 您好,对于您的回答,如果天数 = 3600,它只会包含在 3600 天或更长的时间内,但在我的逻辑中,它应该从整个月中包含在内。所以 3600 天 ID 应该在 0 天及以上、30 天及以上、90 天及以上、3600 天及以上计算。不只是在 3600 天及以上 1 类。
    • 嗨 Kafels,感谢您的更新。它仍然不符合我的逻辑。所以对于你的例子。我们有 6 行。对于 0 天及以上,计数应为 6,因为所有 6 个 ID 的天数 >= 0。对于 30 天及以上,计数将为 5,因为 ID 1、2、3.5.6 的天数 >= 30 ;对于 90 天及以上,计数将为 2,因为只有 ID 5 和 6 的天数 >= 90。对于最后一个,3600 天及以上,计数将为 1,因为只有 ID 6 的天数 >=3600 .很抱歉造成混乱!
    • @yokielove 更新,只是改变窗口顺序
    • 非常感谢!
    猜你喜欢
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多