python - Pandas - 返回日期范围的单个日期并匹配工作日二进制值
问题描述
数据集:
下面的数据集应该复制旅行公司的时间表数据集(例如通过火车、公共汽车或飞机等的路线)
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 航空公司、DEF 火车公司
from
- 从伦敦、纽约、纳尼亚等地出发
to
- 目的地,例如巴黎
valid_from
- 日期范围的开始(可以是一周中的任何一天),其中路线可供运营商购买,例如2019-11-01
valid_to
- 可以为运营商购买路线的日期范围的结束(可以是一周中的任何一天),例如2019-11-12
day_of_week
- 二进制表示从周日到周六的可用性,例如0101010
意味着路线在日期范围内的周一、周三和周五可用
必需的:
一个输出数据集,可将日期范围转换为单个日期以及从该day_of_week
字段派生的可用性。主要目标是获得一个干净的数据集,然后将其加载到 Tableau 中,然后构建一个可以轻松显示路线可用性的报告。
期望的输出:
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
2018-11-13
2018-11-19
数据集很奇怪。日期范围可能非常随机,但day_of_week
始终会显示该日期范围内一周中的几天的可用性。一些相同的日期范围甚至可能有不同的day_of_week
二进制组合,但本质上,如果在任何时候day_of_week
指示给定日期范围、路线和运营商的可用性,那么它将被视为在该日期可用。
我试图做的事情:
使用以下帮助:Pandas:将日期范围解压缩为单个日期
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'))
结果看起来很有希望。我最后的想法是:
- 添加一
weekday
列,返回以asdate
开头的工作日Sunday
0
- 添加一个
available
返回二进制值的列,day_of_week
用作weekday
位置索引 - 最后,以某种方式删除重复
operator
的 ,from
和to
行并保留available
's1
并删除那些0
'1
/operators
'from
s/to
's 然后将可用的保留为0
...
疯狂...为啰嗦而道歉,我希望我能说得通。对此的任何帮助将不胜感激。
编辑:
- 更新了上面的“我尝试做的事情”部分。
- 更新了数据集以在日期中包含更多种类(仍然是相同的数据集,只是调整了
valid_to
日期)
解决方案
如果你不太在意速度,可以使用 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
推荐阅读
- html - 构建下拉过滤器
- regex - 如何使用多行文本创建重复的非捕获组?
- html - 将 API 数据拉入具有嵌入标签,有没有办法将文本包装在 HTML 标签中?
- angular - 在 Angular 中创建 *with* 功能的动态按钮的正确方法
- code-analysis - 全局抑制 Stylecop 规则不起作用
- php - 选择父母的孩子时如何使父母处于活动状态
- reactjs - NextJS useEffect locaStorage 问题
- web-scraping - 如何从 scrapy 项目创建可执行文件?
- sql-server - 将 bcp 与取自另一列的文件名一起使用
- linux - API/CLI 触发 KVM 动作