熊猫数据框:省略假期附近的周末和日期

时间:2018-02-16 00:54:10

标签: python pandas

我有一个带有DataTimeIndex和其他一些列的Pandas数据框,类似于:

import pandas as pd
import numpy as np

range = pd.date_range('2017-12-01', '2018-01-05', freq='6H')
df = pd.DataFrame(index = range)

# Average speed in miles per hour
df['value'] = np.random.randint(low=0, high=60, size=len(df.index))

df.info()
# DatetimeIndex: 141 entries, 2017-12-01 00:00:00 to 2018-01-05 00:00:00
# Freq: 6H
# Data columns (total 1 columns):
# value    141 non-null int64
# dtypes: int64(1)
# memory usage: 2.2 KB

df.head(10)
#                      value
# 2017-12-01 00:00:00     15
# 2017-12-01 06:00:00     54
# 2017-12-01 12:00:00     19
# 2017-12-01 18:00:00     13
# 2017-12-02 00:00:00     35
# 2017-12-02 06:00:00     31
# 2017-12-02 12:00:00     58
# 2017-12-02 18:00:00      6
# 2017-12-03 00:00:00      8
# 2017-12-03 06:00:00     30

如何选择或过滤以下条目:

  1. 仅限工作日(即星期六或星期日不是周末)

  2. 不在列表中日期的N天内(例如美国假期,如'12 -25'或'01 -01')?

  3. 我希望有类似的东西:

    df = exclude_Sat_and_Sun(df)
    
    omit_days = ['12-25', '01-01']
    N = 3 # days near the holidays
    df = exclude_days_near_omit_days(N, omit_days)
    

    我正在考虑创建一个新列来分解月和日,然后将它们与上面的1和2的标准进行比较。但是,我希望使用DateTimeIndex更多Pythonic。

    感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

第一部分可以使用Pandas DatetimeIndex.dayofweek属性轻松完成,该属性开始计算工作日,周一为0,周日为6。

df[df.index.dayofweek < 5]只会给你工作日。


对于第二部分,您可以使用datetime模块。下面我将举例说明一个日期,即2017-12-25。您可以轻松地将其概括为日期列表,例如通过定义辅助函数。

from datetime import datetime, timedelta

N = 3

df[abs(df.index.date - datetime.strptime("2017-12-25", '%Y-%m-%d').date()) > timedelta(N)]

这将提供距离2017-12-25超过N=3天的所有日期。也就是说,它将在2017-12-22至2017-12-28之间排除7天的间隔。


最后,您可以使用&运算符组合这两个条件,您可能知道。

df[
   (df.index.dayofweek < 5) 
   & 
   (abs(df.index.date - datetime.strptime("2017-12-25", '%Y-%m-%d').date()) > timedelta(N))
  ]

答案 1 :(得分:0)

我按照@Bahman Engheta的回答创建了一个函数来从数据框中省略日期。

import pandas as pd
from datetime import datetime, timedelta

def omit_dates(df, list_years, list_dates, omit_days_near=3, omit_weekends=False):
    '''
    Given a Pandas dataframe with a DatetimeIndex, remove rows that have a date
    near a given list of dates and/or a date on a weekend.

    Parameters:
    ----------

    df : Pandas dataframe

    list_years : list of str
        Contains a list of years in string form
    list_dates : list of str
        Contains a list of dates in string form encoded as MM-DD
    omit_days_near : int
        Threshold of days away from list_dates to remove. For example, if
        omit_days_near=3, then omit all days that are 3 days away from 
        any date in list_dates.
    omit_weekends : bool
        If true, omit dates that are on weekends.

    Returns:
    -------
    Pandas dataframe
        New resulting dataframe with dates omitted.
    '''

    if not isinstance(df, pd.core.frame.DataFrame):
        raise ValueError("df is expected to be a Pandas dataframe, not %s" % type(df).__name__)

    if not isinstance(df.index, pd.tseries.index.DatetimeIndex):
        raise ValueError("Dataframe is expected to have an index of DateTimeIndex, not %s" %
                         type(df.index).__name__)

    if not isinstance(list_years, list):
        list_years = [list_years]

    if not isinstance(list_dates, list):
        list_dates = [list_dates]

    result = df.copy()

    if omit_weekends:
        result = result.loc[result.index.dayofweek < 5]

    omit_dates = [ '%s-%s' % (year, date) for year in list_years for date in list_dates ]

    for date in omit_dates:
        result = result.loc[abs(result.index.date - datetime.strptime(date, '%Y-%m-%d').date()) > timedelta(omit_days_near)]

    return result

以下是示例用法。假设您有一个具有DateTimeIndex和其他列的数据框,如下所示:

import pandas as pd
import numpy as np

range = pd.date_range('2017-12-01', '2018-01-05', freq='1D')
df = pd.DataFrame(index = range)

df['value'] = np.random.randint(low=0, high=60, size=len(df.index))

结果数据框如下所示:

            value
2017-12-01     42
2017-12-02     35
2017-12-03     49
2017-12-04     25
2017-12-05     19
2017-12-06     28
2017-12-07     21
2017-12-08     57
2017-12-09      3
2017-12-10     57
2017-12-11     46
2017-12-12     20
2017-12-13      7
2017-12-14      5
2017-12-15     30
2017-12-16     57
2017-12-17      4
2017-12-18     46
2017-12-19     32
2017-12-20     48
2017-12-21     55
2017-12-22     52
2017-12-23     45
2017-12-24     34
2017-12-25     42
2017-12-26     33
2017-12-27     17
2017-12-28      2
2017-12-29      2
2017-12-30     51
2017-12-31     19
2018-01-01      6
2018-01-02     43
2018-01-03     11
2018-01-04     45
2018-01-05     45

现在,让我们指定要删除的日期。我想删除日期'12 -10','12 -25','12 -31'和'01 -01'(遵循MM-DD表示法)以及这些日期后2天内的所有日期。此外,我想从2016年和2017年两年中删除这些日期。我也想删除周末日期。

我会像这样调用我的函数:

years = ['2016', '2017']
holiday_dates = ['12-10', '12-25', '12-31', '01-01']
omit_dates(df, years, holiday_dates, omit_days_near=2, omit_weekends=True)

结果是:

            value
2017-12-01     42
2017-12-04     25
2017-12-05     19
2017-12-06     28
2017-12-07     21
2017-12-13      7
2017-12-14      5
2017-12-15     30
2017-12-18     46
2017-12-19     32
2017-12-20     48
2017-12-21     55
2017-12-22     52
2017-12-28      2
2018-01-03     11
2018-01-04     45
2018-01-05     45

答案是否正确?以下是2017年12月和2018年1月的日历:

   December 2017      
Su Mo Tu We Th Fr Sa  
                1  2  
 3  4  5  6  7  8  9  
10 11 12 13 14 15 16  
17 18 19 20 21 22 23  
24 25 26 27 28 29 30  
31   

    January 2018      
Su Mo Tu We Th Fr Sa  
    1  2  3  4  5  6  
 7  8  9 10 11 12 13  
14 15 16 17 18 19 20  
21 22 23 24 25 26 27  
28 29 30 31   

看起来很有效。