在 python 中抽取数据
decimate data in python
我在标题中使用了 decimate,但我不确定这正是我的意思。这是问题的完整描述。我有一个数据框,其中包含来自多个主题的数据。我想做的是分析相隔 X 天的数据。我的想法是,我只想考虑每隔 4 天从某个主题收集的数据。这里的问题是数据是为受试者并行收集的,所以我不能每 4 天跨受试者一次,而是需要为每个受试者做 decimation/downsampling/whatever。数据框中的两个关键列是 "subject" 和 "session_timestamp"。在后者中,日期和时间的格式如下例所示:2017-11-10 16:30:47。在 python 中有没有什么好的方法可以做到这一点?
编辑:
第一批评论者要求使用一些示例数据提供更具体的数据框示例。这是一个类似于我所拥有的并且应该易于使用的玩具数据框。下面的代码创建了一个包含 4 列的数据框:subjectID、date、score1 和 score2。请注意,一个受试者在给定日期可以有多个条目(基本上,这些是神经记录,数据框的每一行代表一个神经元,我们可以为每个受试者记录多个神经元)
import pandas as pd
import numpy as np
ab = pd.DataFrame()
ab["subjectID"] = np.random.randint(5, size=200)#random list of "subjects" from 0 to 4
ab["date"] = np.random.randint(20, size=200)#random list of "dates" from 0 to 19
ab["score1"] = np.random.randint(200, size=200)#meant to simulate one measurement from one subject
ab["score2"] = np.random.randint(400, size=200)#meant to simulate a second measurement
我想做的是过滤每个主题至少间隔 4 天收集的数据(score1 和 score2)。该代码可以非常简单,并且在主题有条目的第一天和之后的每 4 天。但是更好的解决方案是,如果它在第一天,然后是 3 天后的下一个,然后是 3 天后的那个(不是每个受试者都有每日样本,所以刚性 "every 4th day" 代码不会那么优雅)。应包括在允许的日期收集的所有数据。例如,应包括日期代码为 0 的所有数据(如果那是受试者的第一天)。
我相信您可能正在寻找一种对训练示例的总体进行子抽样的方法。为此,您可能需要使用一些 不平衡学习 方法,例如:ADASYN、SMOTE、Tomek link。随机 sub-/over-sampling,等等(关于 Oversampling and undersampling in data analysis gives a decent overview). There is a convenient implementation in imbalanced-learn 包的维基百科文章。
首先创建一个数据框(带有随机数据):
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
ab = pd.DataFrame()
ab["subjectID"] = np.random.randint(5, size=200)#random list of "subjects" from 0 to 4
ab["day_number"] = np.random.randint(50, size=200)#random list of "dates" from 0 to 50
ab['real_date'] = ab.day_number.apply(lambda d: datetime(2018, 1, 1) + timedelta(days=d)) #to simulate real dates
ab["score1"] = np.random.randint(200, size=200)#meant to simulate one measurement from one subject
ab["score2"] = np.random.randint(400, size=200)#meant to simulate a second measurement
min_day = ab.real_date.min()
ab = ab.groupby(['subjectID', 'real_date']).sum() #because some subjects have more than 1 score each day
print(ab.head(10))
day_number score1 score2
subjectID real_date
0 2018-01-01 0 306 273
2018-01-04 3 32 60
2018-01-05 4 61 135
2018-01-08 21 477 393
2018-01-09 8 22 341
2018-01-10 9 137 30
2018-01-11 30 281 674
2018-01-14 13 183 396
2018-01-15 14 41 337
2018-01-16 15 83 50
然后将没有数据的天数填入下一个已存在的天数:
df = ab.reset_index(level='subjectID').groupby('subjectID').resample('D').mean() #Complete missing dates with NaN
df = df.drop(columns='subjectID')
df = df.groupby(level='subjectID').fillna(method='bfill') #fills the NaN with the first next non NaN value
df = df.apply(pd.to_numeric, downcast='integer') #just to have ints, easier to read
print(df.head(10))
day_number score1 score2
subjectID real_date
0 2018-01-01 0 306 273
2018-01-02 3 32 60
2018-01-03 3 32 60
2018-01-04 3 32 60
2018-01-05 4 61 135
2018-01-06 21 477 393
2018-01-07 21 477 393
2018-01-08 21 477 393
2018-01-09 8 22 341
2018-01-10 9 137 30
下一个 4 天的重采样(分组依据)周期:
res = df.reset_index(level='subjectID').groupby('subjectID').resample('4D').first() #group by 4 days periods and keep only the first value
res = res.drop(columns='subjectID')
print(res.head(10))
day_number score1 score2
subjectID real_date
0 2018-01-01 0 306 273
2018-01-05 4 61 135
2018-01-09 8 22 341
2018-01-13 13 183 396
2018-01-17 18 91 46
2018-01-21 20 76 333
2018-01-25 48 131 212
2018-01-29 29 92 81
2018-02-02 32 172 55
2018-02-06 72 98 246
最后重置索引并处理超过4天没有数据的情况:
res = res.reset_index('real_date', drop=True) #the real_date has no meaning anymore
res['real_date'] = res.day_number.apply(lambda d: min_day + timedelta(days=d)) #good real_date based on the day_number
res = res.drop(columns='day_number')
res = res.set_index('real_date', append=True)
res = res.groupby(level=['subjectID', 'real_date']).first() #regroups periods with no data for more than 4 days
print(res.head(10))
score1 score2
subjectID real_date
0 2018-01-01 306 273
2018-01-05 61 135
2018-01-09 22 341
2018-01-14 183 396
2018-01-19 91 46
2018-01-21 76 333
2018-01-30 92 81
2018-02-02 172 55
2018-02-10 40 218
2018-02-15 110 112
有点复杂,但我认为这是最好的方法。虽然我不知道效率如何,但看起来还不错。
我在标题中使用了 decimate,但我不确定这正是我的意思。这是问题的完整描述。我有一个数据框,其中包含来自多个主题的数据。我想做的是分析相隔 X 天的数据。我的想法是,我只想考虑每隔 4 天从某个主题收集的数据。这里的问题是数据是为受试者并行收集的,所以我不能每 4 天跨受试者一次,而是需要为每个受试者做 decimation/downsampling/whatever。数据框中的两个关键列是 "subject" 和 "session_timestamp"。在后者中,日期和时间的格式如下例所示:2017-11-10 16:30:47。在 python 中有没有什么好的方法可以做到这一点?
编辑: 第一批评论者要求使用一些示例数据提供更具体的数据框示例。这是一个类似于我所拥有的并且应该易于使用的玩具数据框。下面的代码创建了一个包含 4 列的数据框:subjectID、date、score1 和 score2。请注意,一个受试者在给定日期可以有多个条目(基本上,这些是神经记录,数据框的每一行代表一个神经元,我们可以为每个受试者记录多个神经元)
import pandas as pd
import numpy as np
ab = pd.DataFrame()
ab["subjectID"] = np.random.randint(5, size=200)#random list of "subjects" from 0 to 4
ab["date"] = np.random.randint(20, size=200)#random list of "dates" from 0 to 19
ab["score1"] = np.random.randint(200, size=200)#meant to simulate one measurement from one subject
ab["score2"] = np.random.randint(400, size=200)#meant to simulate a second measurement
我想做的是过滤每个主题至少间隔 4 天收集的数据(score1 和 score2)。该代码可以非常简单,并且在主题有条目的第一天和之后的每 4 天。但是更好的解决方案是,如果它在第一天,然后是 3 天后的下一个,然后是 3 天后的那个(不是每个受试者都有每日样本,所以刚性 "every 4th day" 代码不会那么优雅)。应包括在允许的日期收集的所有数据。例如,应包括日期代码为 0 的所有数据(如果那是受试者的第一天)。
我相信您可能正在寻找一种对训练示例的总体进行子抽样的方法。为此,您可能需要使用一些 不平衡学习 方法,例如:ADASYN、SMOTE、Tomek link。随机 sub-/over-sampling,等等(关于 Oversampling and undersampling in data analysis gives a decent overview). There is a convenient implementation in imbalanced-learn 包的维基百科文章。
首先创建一个数据框(带有随机数据):
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
ab = pd.DataFrame()
ab["subjectID"] = np.random.randint(5, size=200)#random list of "subjects" from 0 to 4
ab["day_number"] = np.random.randint(50, size=200)#random list of "dates" from 0 to 50
ab['real_date'] = ab.day_number.apply(lambda d: datetime(2018, 1, 1) + timedelta(days=d)) #to simulate real dates
ab["score1"] = np.random.randint(200, size=200)#meant to simulate one measurement from one subject
ab["score2"] = np.random.randint(400, size=200)#meant to simulate a second measurement
min_day = ab.real_date.min()
ab = ab.groupby(['subjectID', 'real_date']).sum() #because some subjects have more than 1 score each day
print(ab.head(10))
day_number score1 score2
subjectID real_date
0 2018-01-01 0 306 273
2018-01-04 3 32 60
2018-01-05 4 61 135
2018-01-08 21 477 393
2018-01-09 8 22 341
2018-01-10 9 137 30
2018-01-11 30 281 674
2018-01-14 13 183 396
2018-01-15 14 41 337
2018-01-16 15 83 50
然后将没有数据的天数填入下一个已存在的天数:
df = ab.reset_index(level='subjectID').groupby('subjectID').resample('D').mean() #Complete missing dates with NaN
df = df.drop(columns='subjectID')
df = df.groupby(level='subjectID').fillna(method='bfill') #fills the NaN with the first next non NaN value
df = df.apply(pd.to_numeric, downcast='integer') #just to have ints, easier to read
print(df.head(10))
day_number score1 score2
subjectID real_date
0 2018-01-01 0 306 273
2018-01-02 3 32 60
2018-01-03 3 32 60
2018-01-04 3 32 60
2018-01-05 4 61 135
2018-01-06 21 477 393
2018-01-07 21 477 393
2018-01-08 21 477 393
2018-01-09 8 22 341
2018-01-10 9 137 30
下一个 4 天的重采样(分组依据)周期:
res = df.reset_index(level='subjectID').groupby('subjectID').resample('4D').first() #group by 4 days periods and keep only the first value
res = res.drop(columns='subjectID')
print(res.head(10))
day_number score1 score2
subjectID real_date
0 2018-01-01 0 306 273
2018-01-05 4 61 135
2018-01-09 8 22 341
2018-01-13 13 183 396
2018-01-17 18 91 46
2018-01-21 20 76 333
2018-01-25 48 131 212
2018-01-29 29 92 81
2018-02-02 32 172 55
2018-02-06 72 98 246
最后重置索引并处理超过4天没有数据的情况:
res = res.reset_index('real_date', drop=True) #the real_date has no meaning anymore
res['real_date'] = res.day_number.apply(lambda d: min_day + timedelta(days=d)) #good real_date based on the day_number
res = res.drop(columns='day_number')
res = res.set_index('real_date', append=True)
res = res.groupby(level=['subjectID', 'real_date']).first() #regroups periods with no data for more than 4 days
print(res.head(10))
score1 score2
subjectID real_date
0 2018-01-01 306 273
2018-01-05 61 135
2018-01-09 22 341
2018-01-14 183 396
2018-01-19 91 46
2018-01-21 76 333
2018-01-30 92 81
2018-02-02 172 55
2018-02-10 40 218
2018-02-15 110 112
有点复杂,但我认为这是最好的方法。虽然我不知道效率如何,但看起来还不错。