检查 pandas python 中一年中是否存在所有日期

问题描述 投票:0回答:3

我有一个如下所示的数据列,其中缺少一些日期。

obstime

2012-01-01

2012-01-02

2012-01-03

2012-01-04

....

2016-12-28

2016-12-29

2016-12-30

2016-12-31

我想检查可用年份的每个月的所有日期。就像下图一样

database python-3.x postgresql pandas pandas-groupby
3个回答
0
投票

用途:

#sample data
df = pd.DataFrame({'obstime':pd.date_range('2012-01-01', '2016-12-31')})
removed = ['2013-09-01', '2013-09-02', '2013-09-03','2014-10-09','2016-12-30']
removed1 = pd.date_range('2016-12-16', '2016-12-22')
removed2 = pd.date_range('2016-10-10', '2016-12-03')

df = df[~df['obstime'].isin(pd.to_datetime(removed).append(removed1).append(removed2))]
#print (df)

#add missing values
df1 = df.set_index('obstime', drop=False).reindex(pd.date_range('2012-01-01', '2016-12-31'))

#create mask for start and end missing values and for start and end months with NaT
m = df1['obstime'].isnull()
start_NaT = m.ne(m.shift())
end_NaT = m.ne(m.shift(-1))
start_months = df1.index.day == 1
end_months = df1.index.isin(df1.index + pd.offsets.MonthEnd(0))
mask = (start_NaT | end_NaT | start_months | end_months) & m

#mask for separated missing values
s = start_NaT.cumsum()
m1 = s.map(s.value_counts()) == 1

#for start and end days join -
df2 = df1[mask & ~m1].reset_index().rename(columns={'index':'date'})
df2['day'] = df2['date'].dt.day.astype(str)
df2 = df2.groupby(np.arange(len(df2.index)) // 2).agg({'date':'first', 'day':'-'.join})

#separate days
df3 = df1[mask & m1].copy()
df3['day'] = df3.index.day.astype(str)

#join together
df3 = pd.concat([df2.set_index('date'), df3])

#join days by , add missing months and years
df4 = (df3.groupby([df3.index.month, df3.index.year])['day']
          .agg(','.join)
          .unstack(fill_value='yes')
          .reindex(index=range(1, 13), columns=range(2008, 2017),fill_value='yes'))

print (df4)
   2008 2009 2010 2011 2012 2013 2014 2015          2016
1   yes  yes  yes  yes  yes  yes  yes  yes           yes
2   yes  yes  yes  yes  yes  yes  yes  yes           yes
3   yes  yes  yes  yes  yes  yes  yes  yes           yes
4   yes  yes  yes  yes  yes  yes  yes  yes           yes
5   yes  yes  yes  yes  yes  yes  yes  yes           yes
6   yes  yes  yes  yes  yes  yes  yes  yes           yes
7   yes  yes  yes  yes  yes  yes  yes  yes           yes
8   yes  yes  yes  yes  yes  yes  yes  yes           yes
9   yes  yes  yes  yes  yes  1-3  yes  yes           yes
10  yes  yes  yes  yes  yes  yes    9  yes         10-31
11  yes  yes  yes  yes  yes  yes  yes  yes          1-30
12  yes  yes  yes  yes  yes  yes  yes  yes  1-3,16-22,30

0
投票

我的解决方案基于 Pandas,没有使用任何数据库。

这个想法是使用“完整”索引(带有 年份范围内的所有日期)。为了这个测试目的,我使用了日期 从 2016 年和 2017 年开始。

然后我们只留下“刚刚添加”的行,以及“缺失”测量的日期。

剩下的操作是:

  • 按月分组,应用生成日期范围的函数。
  • 转换为具有“提取”年份和月份的 DataFrame。
  • 旋转 DataFrame(月份作为索引,年份作为列)。
  • 添加月份名称并将其设置为索引。

所以整个脚本可以如下:

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

0
投票

我无法测试,但这里是我认为最简单的方法的伪代码:

  1. 我会生成一个涵盖您需要的日期的日期范围:
    all_dates = pd.date_range(pd.Timestamp(f"{obstime[0].year}-01-01"), f"{obstime[-1].year}-12-31"), freq="D", inclusive="both")
  1. 使用集合,我会找到 all_dates 中存在但 obstime 中不存在的日期。
    missing_dates = list(set(all_dates) - set(obstime))
  1. 使用missing_dates,您可以轻松检测哪些年份缺少日期。
    years_with_missing = pd.unique(pd.Series(missing_dates).dt.year

这也可以轻松扩展到您的几个月。

© www.soinside.com 2019 - 2024. All rights reserved.