Pandas - возвращение отдельных дат для диапазона дат и совпадение двоичных значений дня недели - PullRequest
0 голосов
/ 15 ноября 2018

Набор данных:

Ниже набор данных должен копировать набор данных расписания для туристической компании (например, маршруты на поезде, автобусе или самолете и т. Д.)

df = pd.DataFrame({'operator': ['op_a', 'op_a', 'op_a', 'op_a', 'op_b', 'op_b', 'op_b', 'op_b', 'op_c', 'op_c', 'op_c', 'op_c', 'op_d', 'op_d'],
                   'from': ['a', 'a', 'a', 'a', 'c', 'c', 'c', 'c', 'a', 'a', 'a', 'a', 'x', 'x'], 
                   'to': ['b', 'b', 'b', 'b', 'd', 'd', 'd', 'd', 'b', 'b', 'b', 'b', 'y', 'y'], 
                   'valid_from': ['13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '15/02/2019', '15/02/2019', '15/02/2019', '15/02/2019', '20/05/2019', '21/05/2019'],
                   'valid_to': ['20/11/2018', '20/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '21/11/2018', '21/11/2018', '21/02/2019', '21/02/2019', '20/02/2019', '20/02/2019', '30/05/2019', '29/05/2019'], 
                   'day_of_week': ['0101010', '0100010', '0111100', '1101100', '0101010', '0100010', '0111100', '1101100', '0001101', '1110000', '0000000', '0000001', '1000000', '1000001']})
    print(df)

operator - управляющая компания, например, ABC Airlines, DEF Train Company

from - отправляется, например, из Лондона, Нью-Йорка, Нарнии

to - пункт назначения, например, Париж

valid_from - начало диапазона дат (может быть любой день недели), в котором для оператора доступен маршрут для покупки, например: 2019-11-01

valid_to - конец диапазона дат (можетбыть любым днем ​​недели), где маршрут доступен для покупки для оператора, например, 2019-11-12

day_of_week - двоичный файл, представляющий доступность для ВС до Сб, например, 0101010 означает, что маршрут доступен в понедельник, среду,и пт в диапазоне дат

Обязательно:

Выходной набор данных, который преобразует диапазон дат в отдельные даты и их доступность определяется на основе поля day_of_week.Основная цель - получить чистый набор данных, который затем можно загрузить в Таблицу, чтобы затем создать отчет, который бы легко отображал доступность маршрута.

Желаемый результат:

dfout = pd.DataFrame({'operator': ['op_a', 'op_a', 'op_a', 'op_a', 'op_a', 'op_a', 'op_a'], 'from': ['a', 'a', 'a', 'a', 'a', 'a', 'a'], 'to': ['b', 'b', 'b', 'b', 'b', 'b', 'b'], 'date': ['13/11/2018', '14/11/2018', '15/11/2018', '16/11/2018', '17/11/2018', '18/11/2018', '19/11/2018'], 'available': [1, 1, 1, 1, 0, 1, 1]})
print(dfout)

Таким образом, это будет вывод для op_a для маршрута от a до b для диапазона дат * от 1044 * до 2018-11-19.

Набор данных выглядит странно.Диапазоны дат могут быть довольно случайными, но day_of_week всегда будет отображать доступность для дней недели в этом диапазоне дат.Некоторые из одних и тех же диапазонов дат могут даже иметь различные двоичные комбинации day_of_week, но, по сути, если в какой-то момент day_of_week указывает на доступность для данного диапазона дат, маршрута и оператора, то он будет считаться доступным для даты.

То, что я пытался сделать:

Использование следующего для справки: Панды: декомпрессировать диапазон дат до отдельных дат

import pandas as pd

df = pd.DataFrame({'operator': ['op_a', 'op_a', 'op_a', 'op_a', 'op_b', 'op_b', 'op_b', 'op_b', 'op_c', 'op_c', 'op_c', 'op_c', 'op_d', 'op_d'],
                   'from': ['a', 'a', 'a', 'a', 'c', 'c', 'c', 'c', 'a', 'a', 'a', 'a', 'x', 'x'], 
                   'to': ['b', 'b', 'b', 'b', 'd', 'd', 'd', 'd', 'b', 'b', 'b', 'b', 'y', 'y'], 
                   'valid_from': ['13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '15/02/2019', '15/02/2019', '15/02/2019', '15/02/2019', '20/05/2019', '21/05/2019'],
                   'valid_to': ['20/11/2018', '20/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '21/11/2018', '21/11/2018', '21/02/2019', '21/02/2019', '20/02/2019', '20/02/2019', '30/05/2019', '29/05/2019'], 
                   'day_of_week': ['0101010', '0100010', '0111100', '1101100', '0101010', '0100010', '0111100', '1101100', '0001101', '1110000', '0000000', '0000001', '1000000', '1000001']})

df.set_index(['operator', 'from','to'], inplace=True)

df['valid_from'] = pd.to_datetime(df['valid_from'])
df['valid_to'] = pd.to_datetime(df['valid_to'])

df['row'] = range(len(df))
starts = df[['valid_from', 'day_of_week', 'row']].rename(columns={'valid_from': 'date'})
ends = df[['valid_to', 'day_of_week', 'row']].rename(columns={'valid_to':'date'})

df_decomp = pd.concat([starts, ends])
df_decomp = df_decomp.set_index('row', append=True)
df_decomp.sort_index()

df_decomp = df_decomp.groupby(level=[0,1,2,3]).apply(lambda x: x.set_index('date').resample('D').fillna(method='pad'))

Результат выглядит многообещающим.Мои последние мысли:

  1. добавить столбец weekday, который возвращает день недели date, начиная с Sunday как 0
  2. и добавляя availableстолбец, который возвращает двоичное значение в day_of_week, используя weekday в качестве индекса позиции
  3. , наконец, чтобы каким-то образом удалить дубликаты operator, from и to и сохранить available, чтоиметь 1 и отбрасывать те, которые 0 или если * * нет 1082 * для этих operators '/ from' / to, то оставьте доступным 0 ...

безумие ... извиняюсь за многословность, и я надеюсь, что у меня есть смысл.Любая помощь по этому вопросу будет принята с благодарностью.

Редактировать:

  • Обновлена ​​часть "Что я пытался сделать" выше.
  • Обновлен набор данных, теперь он включает в себя немного большее разнообразие дат (все тот же набор данных, только что скорректированный valid_to даты)

Ответы [ 2 ]

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

Это сделано трюк:

import pandas as pd
import numpy as np

# dataset
df = pd.DataFrame({'operator': ['op_a', 'op_a', 'op_a', 'op_a', 'op_b', 'op_b', 'op_b', 'op_b', 'op_c', 'op_c', 'op_c', 'op_c', 'op_d', 'op_d'],
                   'from': ['a', 'a', 'a', 'a', 'c', 'c', 'c', 'c', 'a', 'a', 'a', 'a', 'x', 'x'], 
                   'to': ['b', 'b', 'b', 'b', 'd', 'd', 'd', 'd', 'b', 'b', 'b', 'b', 'y', 'y'], 
                   'valid_from': ['13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '15/02/2019', '15/02/2019', '15/02/2019', '15/02/2019', '20/05/2019', '21/05/2019'],
                   'valid_to': ['20/11/2018', '20/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '21/11/2018', '21/11/2018', '21/02/2019', '21/02/2019', '20/02/2019', '20/02/2019', '30/05/2019', '29/05/2019'], 
                   'day_of_week': ['0101010', '0100010', '0111100', '1101100', '0101010', '0100010', '0111100', '1101100', '0001101', '1110000', '0000000', '0000001', '1000000', '1000001']})

# set operator, from, to as index
df.set_index(['operator', 'from','to'], inplace=True)

# convert date ranges to datetime types
df['valid_from'] = pd.to_datetime(df['valid_from'])
df['valid_to'] = pd.to_datetime(df['valid_to'])

# bring individual dates in date ranges and stack
df['row'] = range(len(df))
starts = df[['valid_from', 'day_of_week', 'row']].rename(columns={'valid_from': 'date'})
ends = df[['valid_to', 'day_of_week', 'row']].rename(columns={'valid_to':'date'})

df_decomp = pd.concat([starts, ends])
df_decomp = df_decomp.set_index('row', append=True)
df_decomp.sort_index()

df_decomp = df_decomp.groupby(level=[0,1,2,3]).apply(lambda x: x.set_index('date').resample('D').fillna(method='pad'))

# remove indexes
df_decomp.reset_index(level=3, drop=True, inplace=True)
df_decomp.reset_index(inplace=True)

# create weekday column
df_decomp['weekday'] = np.where(df_decomp['date'].dt.weekday == 6, 
                            df_decomp['date'].dt.weekday - 6, 
                            df_decomp['date'].dt.weekday + 1)

# use weekday to extract availability in day_of_week
df_decomp['available'] = [b[a] for a, b in zip(df_decomp['weekday'], df_decomp['day_of_week'])]
df_decomp['available'] = df_decomp['available'].astype('int')

# sort values and remove duplicates with available=1 taking priority
df_decomp = df_decomp.sort_values('available', ascending=False).drop_duplicates(['operator','from','to','date'])
df_decomp = df_decomp.sort_values(['operator','from','to','date'])

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

Если вы не слишком заботитесь о скорости, вы можете использовать iterrows () и df.at []:

import pandas as pd

df = pd.DataFrame({'operator': ['op_a', 'op_a', 'op_a', 'op_a', 'op_b', 'op_b', 'op_b', 'op_b', 'op_c', 'op_c', 'op_c', 'op_c', 'op_d', 'op_d'], 'from': ['a', 'a', 'a', 'a', 'c', 'c', 'c', 'c', 'a', 'a', 'a', 'a', 'x', 'x'], 'to': ['b', 'b', 'b', 'b', 'd', 'd', 'd', 'd', 'b', 'b', 'b', 'b', 'y', 'y'], 'valid_from': ['13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '13/11/2018', '15/02/2019', '15/02/2019', '15/02/2019', '15/02/2019', '01/05/2019', '01/05/2019'], 'valid_to': ['19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '19/11/2018', '21/02/2019', '21/02/2019', '21/02/2019', '21/02/2019', '10/05/2019', '11/05/2019'], 'day_of_week': ['0101010', '0100010', '0111100', '1101100', '0101010', '0100010', '0111100', '1101100', '0001101', '1110000', '0000000', '0000001', '1000000', '1000001']})

df['valid_from'] = pd.to_datetime(df['valid_from'])
df['valid_to'] = pd.to_datetime(df['valid_to'])
df['day'] = (df['valid_from']+pd.to_timedelta(1, unit='d')).dt.weekday # gives weekdays : ) = Sunday
print df.head()


df_out = pd.DataFrame(columns=['available', 'date', 'from', 'operator', 'to'])

idx = 0
for i, row in df.iterrows():
    daterange = row['valid_to'] - row['valid_from']
    print daterange.days

    daystring = 52 * (row['day_of_week'])  # extend string to allow going through multiple weeks

    for j in range(daterange.days+1):
        df_out.at[idx, ['available', 'date', 'from', 'operator', 'to']] = [ # replaced set_value with df.at[]
            int(daystring[j + row['day']]), # use day of the week as starting position
            row['valid_from']+pd.to_timedelta(j, unit='d'),
            row['from'],
            row['operator'],
            row['to']
            ]

        # row['day_of_week'][j]
        idx += 1

df_out.drop_duplicates(inplace=True) # drop all duplicates
df_0 = df_out[df_out['available']==0]
df_1 = df_out[df_out['available']==1]
df_out = df_0.merge(df_1, how='outer', left_on=['date', 'from', 'operator', 'to'], right_on=['date', 'from', 'operator', 'to'])
df_out.fillna(0, inplace=True)

df_out['available'] = df_out['available_x'] + df_out['available_y']
df_out.drop(['available_x', 'available_y'], axis=1, inplace=True)
df_out.sort_values(by='date',inplace=True)
print df_out
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...