Мое решение основано на Pandas, без использования баз данных.
Идея состоит в том, чтобы переиндексировать исходный Dataframe, используя «полный» индекс (с
все даты из диапазона года). Для этого теста я использовал даты
с 2016 и 2017 года.
Затем мы оставляем только «только что добавленные» строки с датами для «отсутствующих» измерений.
Остальные операции:
- Группировка по месяцам с применением функции, генерирующей дневные диапазоны.
- Преобразовать в фрейм данных с "извлеченными" годом и месяцем.
- Поворот фрейма данных (месяц как индекс, год как столбцы).
- Добавьте названия месяцев и установите их в качестве индекса.
Таким образом, весь сценарий может быть следующим:
import pandas as pd
import calendar
# Function to be applied to date groups for each month
def fun(x):
dt = x.result
day = pd.Timedelta('1d')
startDates = dt[dt.diff() != day]
if startDates.size > 0:
endDates = dt[(dt - dt.shift(-1)).abs() != day]
return '&'.join([(f'{s.day}-{e.day}') for s, e in zip(startDates, endDates)])
else:
return 'OK'
# Source dates
dates = pd.date_range('2016-01-01', '2016-01-13')\
.append(pd.date_range('2016-01-20', '2016-01-29'))\
.append(pd.date_range('2016-02-10', '2016-02-20'))\
.append(pd.date_range('2016-03-11', '2017-11-20'))\
.append(pd.date_range('2017-11-25', '2017-12-31'))
# Source DataFrame with random results for dates given
df = pd.DataFrame(data={ 'result': np.random.randint(10, 30, len(dates))},
index=dates)
# Index for full range of dates
idxFull = pd.date_range('2016-01-01', '2017-12-31')
# "Expand" to all dates
df2 = df.reindex(idxFull)
# Leave only "empty" rows
df2.drop(df2[df2.result.notna()].index, inplace=True)
# Copy index to result
df2.result = df2.index
# Group by months
gr = df2.groupby(pd.Grouper(freq='M'))
# Result - Series
res = gr.apply(fun)
# Result - DataFrame with year/month "extracted" from date
res2 = pd.DataFrame(data={'res': res, 'year': res.index.year,
'month': res.index.month })
# Result - pivot'ed res2
res3 = res2.pivot(index='month', columns='year').fillna('OK')
# Add month names
res3['MonthName'] = list(calendar.month_name)[1:]
# Set month names as index
res3.set_index('MonthName', inplace=True)
Когда вы print(res3)
, результат будет:
res
year 2016 2017
MonthName
January 14-19&30-31 OK
February 1-9&21-29 OK
March 1-10 OK
April OK OK
May OK OK
June OK OK
July OK OK
August OK OK
September OK OK
October OK OK
November OK 21-24
December OK OK