【问题标题】:Pandas: Select highest day of the week, excluding weekends, unless one recordPandas:选择一周中最高的一天,不包括周末,除非有一项记录
【发布时间】:2019-04-29 13:27:42
【问题描述】:

我有一个包含日期的数据框,我想选择除周末外的每周最高日期(所以周五,如果有的话),除非没有周一至周五的数据,并且周六/周日是唯一可用的日期.

样本数据可以这样设置:

dates = pd.Series(data=['2018-11-05', '2018-11-06', '2018-11-07', '2018-11-08', '2018-11-09',
                        '2018-11-12', '2018-11-13', '2018-11-14', '2018-11-15', '2018-11-17',
                        '2018-11-19',
                        '2018-12-01',
                        ])
nums = np.random.randint(50, 100, 12)
# nums
# array([95, 80, 81, 51, 98, 62, 50, 55, 59, 77, 69])

df = pd.DataFrame(data={'dates': dates, 'nums': nums})
df['dates'] = pd.to_datetime(df['dates'])

我想要的记录:

  • 2018-11-09 是星期五
  • 2018-11-15 是星期四(不是 2018-11-17 因为它是星期六)
  • 2018-11-19 是星期一,是该周的唯一记录
  • 2018-12-01 是星期六,但是那一周的唯一记录

我目前的解决方案在answer below 中,但我认为这并不理想,并且有一些我必须解决的问题。简单来说就是:

  1. 按周分组:df.groupby(df['dates'].dt.week).apply(some_function)
  2. 如果该周只有一条记录,则返回它
  3. 否则,选择天

理想情况下,我想要一种写作方式:

[latest Mon-Fri record] if [has Mon-Fri record] else [latest Sat-Sun record]

【问题讨论】:

    标签: python pandas pandas-groupby dayofweek week-number


    【解决方案1】:

    创建一个新的工作日层次结构,其中星期六和星期日的优先级最低。然后sort_values 在这个新排名上 + groupby + .tail(1)

    import numpy as np
    
    wd_map = dict(zip(np.arange(0,7,1), np.roll(np.arange(0,7,1),-2)))
    # {0: 2, 1: 3, 2: 4, 3: 5, 4: 6, 5: 0, 6: 1}
    df = df.assign(day_mapped = df.dates.dt.weekday.map(wd_map)).sort_values('day_mapped')
    
    df.groupby(df.dates.dt.week).tail(1).sort_index()
    

    输出

            dates  nums  day_mapped
    4  2018-11-09    57           6
    8  2018-11-15    83           5
    10 2018-11-19    96           2
    11 2018-12-01    66           0
    

    如果您的数据跨越多年,您将需要对Year + week 进行分组。

    【讨论】:

    • 谢谢!这比我所拥有的要简洁得多,而且“手写”逻辑要少得多,而且肯定要快得多。
    【解决方案2】:

    我写了一个函数来选择一周的有效最高记录,这需要在每周 groupby 上使用:

    def last_valid_report(recs):
        if len(recs) == 1:
            return recs
        recs = recs.copy()
        # recs = recs[recs['dates'].dt.weekday <= 4].nlargest(1, recs['dates'].dt.weekday)  # doesn't work
        recs['weekday'] = recs['dates'].dt.weekday  # because nlargest() needs a column name
        recs = recs[recs['weekday'] <= 4].nlargest(1, 'weekday')
        del recs['weekday']
        return recs
        # could have also done:
        # return recs[recs['weekday'] <= 4].nlargest(1, 'weekday').drop('weekday', axis=1)
    

    用正确的组调用它,我得到:

    In [155]: df2 = df.groupby(df['dates'].dt.week).apply(last_valid_report)
    
    In [156]: df2
    Out[156]:
                  dates  nums
    dates
    45    4  2018-11-09    63
    46    8  2018-11-15    90
    47    10 2018-11-19    80
    48    11 2018-12-01    94
    

    这有几个问题:

    1. 如果我不输入recs.copy(),我会得到ValueError: Shape of passed values is (3, 12), indices imply (3, 4)

    2. pandas' nlargest 只会使用列名,而不是表达式。

      • 所以我需要在函数中创建一个额外的列并在返回之前删除/删除它。 我也可以在原始 df 中创建它并将其放在 .apply() 之后。
    3. 我从groupby+apply 获得了一个额外的索引列“日期”,需要是explicitly dropped

      In [157]: df2.index = df2.index.droplevel(); df2
      Out[157]:
              dates  nums
      4  2018-11-09    63
      8  2018-11-15    90
      10 2018-11-19    80
      11 2018-12-01    94
      
    4. 如果我得到周六和周日数据的记录(2天),我需要添加一个检查recs[recs['weekday'] &lt;= 4]是否为空,然后只使用.nlargest(1, 'weekday')而不过滤掉weekday &lt;= 4;但这不是问题的重点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-06
      • 2021-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多