Python: фильтровать фрейм данных в Pandas по часам, дням и месяцам, сгруппированным по годам

#python #datetime #pandas #dataframe

#python #datetime #pandas #фрейм данных

Вопрос:

Будучи новичком в Pandas, мне пришлось много копать, чтобы найти решение этой проблемы. Я хотел бы знать лучший способ решить эту проблему, принимая во внимание, что мне все еще нужно решить пограничные проблемы.

У меня есть набор из 10 минутных показателей «мощности» с 2009 по 2012 год, и я хочу получить окно часов и дней / месяцев для всех лет (т. Е. Фильтровать по часам, дням и месяцам, сгруппированным по годам).

К чему я пришел, так это к следующему:

 import pandas as pd
import numpy as np
import datetime

dates = pd.date_range(start="08/01/2009",end="08/01/2012",freq="10min")
df = pd.DataFrame(np.random.rand(len(dates), 1)*1500, index=dates, columns=['Power'])

def filter(df, day, month, hour, daysWindow, hoursWindow):
    """
    Filter a Dataframe by a date window and hour window grouped by years

    @type df: DataFrame
    @param df: DataFrame with dates and values

    @type day: int
    @param day: Day to focus on

    @type month: int
    @param month: Month to focus on

    @type hour: int
    @param hour: Hour to focus on

    @type daysWindow: int
    @param daysWindow: Number of days to perform the days window selection

    @type hourWindow: int
    @param hourWindow: Number of hours to perform the hours window selection

    @rtype: DataFrame
    @return: Returns a DataFrame with the
    """
    df_filtered = None
    grouped = df.groupby(lambda x : x.year)
    for year, groupYear in grouped:
        groupedMonthDay = groupYear.groupby(lambda x : (x.month, x.day))
        for monthDay, groupMonthDay in groupedMonthDay:
            if monthDay >= (month,day - daysWindow) and monthDay <= (month,day   daysWindow):
                new_df = groupMonthDay.ix[groupMonthDay.index.indexer_between_time(datetime.time(hour - hoursWindow), datetime.time(hour   hoursWindow))]
                if df_filtered is None:
                    df_filtered = new_df
                else:
                    df_filtered = df_filtered.append(new_df)
    return df_filtered

df_filtered = filter(df,day=8, month=10, hour=8, daysWindow=1, hoursWindow=1)
print len(df)
print len(df_filtered)
  

Который возвращается в качестве выходных данных:

 >>> 
157825
117
  

Конечно, этот код нуждается в улучшении в отношении проблем с границами при выборе часа, такого как 1 и hoursWindow 2. т.е.:

 >>> filter(df,day=8, month=10, hour=1, daysWindow=1, hoursWindow=2)
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "D:tmptest_filtro.py", line 40, in filter
    new_df = groupMonthDay.ix[groupMonthDay.index.indexer_between_time(datetime.time(hour - hoursWindow), datetime.time(hour   hoursWindow))]
ValueError: hour must be in 0..23
  

Аналогичная проблема может возникнуть при выборе дня, например, 1 или 30.

Как можно улучшить этот код?

Ответ №1:

Обновленный код для filter функции гарантирует отсутствие проблем с границами:

 import pandas as pd
import numpy as np
import datetime

dates = pd.date_range(start="08/01/2009",end="08/01/2012",freq="10min")
df = pd.DataFrame(np.random.rand(len(dates), 1)*1500, index=dates, columns=['Power'])

def filter(df, day, month, hour, minute=0, daysWindow=1, hoursWindow=1):
    """
    Filter a Dataframe by a date window and hour window grouped by years

    @type df: DataFrame
    @param df: DataFrame with dates and values

    @type day: int
    @param day: Day to focus on

    @type month: int
    @param month: Month to focus on

    @type hour: int
    @param hour: Hour to focus on

    @type daysWindow: int
    @param daysWindow: Number of days to perform the days window selection

    @type hoursWindow: int
    @param hourWindow: Number of hours to perform the hours window selection

    @rtype: DataFrame
    @return: Returns a DataFrame with the
    """
    df_filtered = None
    grouped = df.groupby(lambda x : x.year)
    for year, groupYear in grouped:
        date = datetime.date(year, month, day)
        dateStart = date - datetime.timedelta(days=daysWindow)
        dateEnd = date   datetime.timedelta(days=daysWindow 1)
        df_filtered_days = df[dateStart:dateEnd]
        timeStart = datetime.time(0 if hour-hoursWindow < 0 else hour-hoursWindow, minute)
        timeEnd = datetime.time(23 if hour hoursWindow > 23 else hour hoursWindow, minute)
        new_df = df_filtered_days.ix[df_filtered_days.index.indexer_between_time(timeStart, timeEnd)]
        if df_filtered is None:
            df_filtered = new_df
        else:
            df_filtered = df_filtered.append(new_df)
    return df_filtered

df_filtered = filter(df,day=8, month=10, hour=1, daysWindow=1, hoursWindow=2)
print len(df)
print len(df_filtered)
  

Вывод:

 >>> 
157825
174