在 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

有点复杂,但我认为这是最好的方法。虽然我不知道效率如何,但看起来还不错。