问题描述
我有两个带有时间序列数据的CSV。一张桌子是连续的,从01.01.2017 00:00开始。从那里开始,每一行代表一个小时(1.表)。数据看起来像这样:
- 表aka df1:
Date,Volume
2017-02-03 12-PM,9787.51
2017-02-03 01-PM,9792.01
2017-02-03 02-PM,9803.94
2017-02-03 03-PM,9573.99
另一个表包含发生的事件,并由UNIX日期时间以秒为单位进行了序列化。我可以使用以下代码将其转换为日期时间并按小时进行分组:
df['datetime'] = pd.to_datetime(df['created_utc'],unit='s')
df['datetime'] = pd.to_datetime(df['datetime'],format="%Y-%m-%d %I-%p")
df['date_by_hour'] = df['datetime'].apply(lambda x: x.strftime('%Y-%m-%d %H:00'))
这产生了以下数据:
- 表aka df2:
created_utc,score,compound,datetime,date_by_hour
1486120391,156,0.125,2017-02-03 12:13:11,2017-02-03 12:00:00
1486125540,1863,0.475,2017-02-03 13:39:00,2017-02-03 13:00:00
1486126013,863,0.889,2017-02-03 13:46:53,2017-02-03 13:00:00
1486130203,23,0.295,2017-02-03 14:56:43,2017-02-03 14:00:00
现在,我需要将事件(2.table)映射到1. Table的时间序列。如果在一小时内发生了多个事件,我需要加分并计算化合物的平均数。最后,我想要一个这样的数据框:
- 最终数据框
Date,Volume,2017-02-03 12-PM,9787.51,2017-02-03 01-PM,9792.01,2726,0.682,2017-02-03 02-PM,9803.94,2017-02-03 03-PM,9573.99,
我知道下面的代码行不通并且是错误的,但是我想表明我在想如何实现此目标。我以为可以遍历事件表df2的每一行,并比较日期时间是否匹配。如果是这样,我将计算分数和复合值。问题是我知道一个人不应该循环访问一个数据框,也不知道如何同时循环通过另一个数据框,并根据前面的行执行正确的计算...
for index,row in df2.iterrows():
memory_score = 0
memory_compound = 0
if df1['Date'] == df2['date_by_hour']:
df1['score'] = row['score'] + memory_score
df1['compound'] = (row['compound'] + memory_compound) / 2
如何获得最终数据框?我必须使用一些熊猫魔术来完成这项工作,并将时间序列数据映射到正确的时间。
预先感谢和问候
编辑:在一小时内事件可能是不确定的事件数。在这个简单的示例中,我只是选择了2,但在某些情况下可能是5000左右或0。
解决方法
# import necessary packages,set seed
import pandas as pd
import datetime
import random
random.seed(42)
设置
让我们使用this创建一个示例df1
:
numdays=5
base = datetime.datetime(2017,2,3,12)
date_list = [base + datetime.timedelta(hours=x) for x in range(numdays)]
然后,using:
df1 = pd.DataFrame.from_dict({'Date': date_list,'Volume': [random.randint(9000,11000) for _ in range(len(date_list))]})
这给我们:
+----+---------------------+----------+
| | Date | Volume |
|----+---------------------+----------|
| 0 | 2017-02-03 12:00:00 | 9228 |
| 1 | 2017-02-03 13:00:00 | 9051 |
| 2 | 2017-02-03 14:00:00 | 10518 |
| 3 | 2017-02-03 15:00:00 | 9563 |
| 4 | 2017-02-03 16:00:00 | 9501 |
+----+---------------------+----------+
我们也创建df2
:
random_date_list = [base + datetime.timedelta(hours=x*random.uniform(0,2)) for x in range(7)]
df2 = pd.DataFrame({'datetime':random_date_list,'score':[random.randint(20,200) for _ in range(len(random_date_list))],'compound': [random.uniform(0,1) for _ in range(len(random_date_list))]},index=[x for x in range(len(random_date_list))])
给出:
+----+----------------------------+---------+------------+
| | datetime | score | compound |
|----+----------------------------+---------+------------|
| 0 | 2017-02-03 12:00:00 | 75 | 0.71602 |
| 1 | 2017-02-03 13:28:22.592742 | 79 | 0.701325 |
| 2 | 2017-02-03 14:42:24.472619 | 149 | 0.41952 |
| 3 | 2017-02-03 17:21:11.078662 | 174 | 0.449209 |
| 4 | 2017-02-03 12:41:43.838380 | 26 | 0.278191 |
| 5 | 2017-02-03 16:13:09.185509 | 163 | 0.8693 |
| 6 | 2017-02-03 12:21:27.239880 | 70 | 0.758807 |
+----+----------------------------+---------+------------+
实际计算
让我们在df2中创建一个带有日期时间对象以达到小时分辨率的列:
df2['Date'] = df2['datetime'].apply(lambda x: x.replace(minute=0,second=0,microsecond=0))
我们可以merge df1
和df2
,replace NaNs with 0s:
merged = pd.merge(df1,df2,on='Date',how='outer')
merged.fillna(0,inplace=True)
现在计算所需的新列:
newscoredf=merged.groupby('Date')[['score']].agg('sum')
newcompounddf=merged.groupby('Date')[['compound']].agg('mean')
让我们吸引他们,并添加我们留下的Volume
列:
final = pd.concat([df1.set_index('Date')[['Volume']],newscoredf,newcompounddf],axis=1)
为您提供想要的东西。
final
:
+---------------------+----------+---------+------------+
| Date | Volume | score | compound |
|---------------------+----------+---------+------------|
| 2017-02-03 12:00:00 | 9228 | 171 | 0.584339 |
| 2017-02-03 13:00:00 | 9051 | 79 | 0.701325 |
| 2017-02-03 14:00:00 | 10518 | 149 | 0.41952 |
| 2017-02-03 15:00:00 | 9563 | 0 | nan |
| 2017-02-03 16:00:00 | 9501 | 163 | 0.8693 |
| 2017-02-03 17:00:00 | nan | 174 | 0.449209 |
+---------------------+----------+---------+------------+
检查:考虑从12开始一个小时内发生的行。得分:得分:75 + 26 + 70 =171。复合:(0.71602 + 0.278191 + 0.758807)/ 3 = 0.584339。两者都与我们结果的第一行一致。