熊猫:选择一周中最高的一天(周末除外),除非有一条记录

时间:2018-11-27 18:22:49

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

我有一个带有日期的数据框,我想选择每周中除周末之外的最高日期(因此,星期五,如果有的话),除非没有周一至周五的数据,而只有周六/周日

可以像这样设置样本数据:

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]

2 个答案:

答案 0 :(得分: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归为一组。

答案 1 :(得分:0)

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

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. 如果我获得了包含星期六和星期日数据(两天)的记录,则需要添加检查recs[recs['weekday'] <= 4]是否为空,然后仅使用.nlargest(1, 'weekday')而不会过滤掉{{1 }};但这不是问题的重点。