Pythonic способ преобразовать вектор даты в диапазонах? - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть пандас DataFrame с одной строкой в ​​день и некоторыми логическими столбцами. Я хотел бы преобразовать их в DataFrame, который содержит диапазоны , где эти столбцы имеют значение True.

Пример запуска DF:

import pandas as pd

t = True
f = False

df = pd.DataFrame(
    {'indic': [f, f, t, t, t, f, f, f, t, f, f, t, t, t, t]},
    index=pd.date_range("2018-01-01", "2018-01-15")
)

print(df)

            indic
2018-01-01  False
2018-01-02  False
2018-01-03   True
2018-01-04   True
2018-01-05   True
2018-01-06  False
2018-01-07  False
2018-01-08  False
2018-01-09   True
2018-01-10  False
2018-01-11  False
2018-01-12   True
2018-01-13   True
2018-01-14   True
2018-01-15   True

Этот столбец DataFrame имеет значение True с 2018-01-03 по 2018-01-05, затем с 2018-01-09 (только один день), а затем снова с 2018-01-12 по 2018-01-15.

Вывод, который я ищу в этом примере, - это DF (объекты даты вместо строк тоже подойдут, даже предпочтительнее):

desired_result = pd.DataFrame({
    'from': ["2018-01-03", "2018-01-09", "2018-01-12"],
    'to': ["2018-01-05", "2018-01-09", "2018-01-15"]
})

print(desired_result)

         from          to
0  2018-01-03  2018-01-05
1  2018-01-09  2018-01-09
2  2018-01-12  2018-01-15

В качестве расширения на следующем этапе я бы хотел, чтобы это работало для нескольких столбцов, например ::

.
df = pd.DataFrame(
    {
        'indic_A': [f, f, t, t, t, f, f, f, t, f, f, t, t, t, t],
        'indic_B': [f, f, f, f, f, f, f, f, t, t, t, t, t, f, f]
    },
    index=pd.date_range("2018-01-01", "2018-01-15")
)

desired_result = pd.DataFrame({
    'from': ["2018-01-03", "2018-01-09", "2018-01-12", "2018-01-09"],
    'to': ["2018-01-05", "2018-01-09", "2018-01-15", "2018-01-13"],
    'what': ["indic_A", "indic_A", "indic_A", "indic_B"]
})

print(desired_result)

         from          to     what
0  2018-01-03  2018-01-05  indic_A
1  2018-01-09  2018-01-09  indic_A
2  2018-01-12  2018-01-15  indic_A
3  2018-01-09  2018-01-13  indic_B

Есть ли питонский, элегантный способ сделать это - может быть, даже функция панды?

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Вы можете попробовать pd.DataFrame.shift

Сначала сделайте 2 новых столбца смещения вверх и вниз

df['down_shift'] = df['indic'].shift()
df['up_shift'] = df['indic'].shift(-1)

И df будет как

            indic down_shift up_shift
2018-01-01  False        NaN    False
2018-01-02  False      False     True
2018-01-03   True      False     True
2018-01-04   True       True     True
2018-01-05   True       True    False
2018-01-06  False       True    False
2018-01-07  False      False    False
2018-01-08  False      False     True
2018-01-09   True      False    False
2018-01-10  False       True    False
2018-01-11  False      False     True
2018-01-12   True      False     True
2018-01-13   True       True     True
2018-01-14   True       True     True
2018-01-15   True       True      NaN

Идея здесь

  • вариант 1: (указатель, смещение вниз) = (истина, ложь) - начало
  • вариант 2: (индикатор, up_shift) = (True, False) - конец
  • вариант 3: случаются как случай 1, так и случай 2 - начало и конец

Итак, мы используем трюк

  • True - False = 1
  • False - True = -1
  • True - True = 0
  • Ложь - Ложь = 0

Код:

case_start = df['indic'] - df['down_shift']
case_end = df['indic'] - df['up_shift']

start_date_list = df[case_start == 1].index
end_date_list = df[case_end == 1].index

Затем мы проверяем start_date_list

DatetimeIndex(['2018-01-03', '2018-01-09', '2018-01-12'], dtype='datetime64[ns]', freq=None)

Затем мы проверяем end_date_list

DatetimeIndex(['2018-01-05', '2018-01-09'], dtype='datetime64[ns]', freq='4D')

Последняя дата не меняется с True на False, поэтому нам нужно добавить ее вручную.

0 голосов
/ 14 ноября 2018

Сначала используйте melt для изменения формы, затем создайте вспомогательный столбец для уникальных групп по cumsum, только фильтр True s по boolean indexing и совокупность agg по функциям first и last:

df = df.rename_axis('date').reset_index().melt('date', var_name='ind', value_name='boolean')
df['new'] = (~df['boolean']).cumsum()
df = (df[df['boolean']]
         .groupby('new')
         .agg({'date':['first','last'], 'ind':'first'})
         .reset_index(drop=True))
df.columns = df.columns.map('_'.join)
print (df)
  date_first  date_last ind_first
0 2018-01-03 2018-01-05   indic_A
1 2018-01-09 2018-01-09   indic_A
2 2018-01-12 2018-01-15   indic_A
3 2018-01-09 2018-01-13   indic_B
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...