熊猫预处理:使用其他 2 个文件标记时间序列数据



我正在尝试使用另一个文件中的类别对时间序列数据进行分类。 我已经上传了三个文件的图片,照片链接如下。

这几天一直是我生活的祸根。

我正在尝试创建一个用于行为识别的标记数据集。我有 2 个 CVS 文件来帮助我标记我的 CSV 文件。

1. 数据

以下是我需要标注的数据框的外观。数据每秒收集40次,持续数月。我也有不同个人的所有这些数据,所以我将它们拆分为每个个人,然后拆分为具有 1000000 行的文件。CSV文件最初有单独的日期,小时,秒,十进制秒列,但我将其制作成一个时间戳,希望我可以将其用作索引,然后执行以下操作:

df['Label'].iloc[starttime:endtime] = "Grooming"

我也在考虑使用 unix 时间戳作为索引。时间戳的 dtype 是对象。

要在标记中使用的文件

2. 行为状态

以下是带有每个标签的文件的外观。所以我们可以看到梳理从 11:26:48 开始,到 11:28:32 结束。然而,与未观察到动物时的数据存在很大差距。因此,我们不能总是从下面获取末日。这就是我们最终数据帧的用武之地。

3. 观测的开始和结束时间。

这是我们的最终数据帧。我们有每个焦点观察的开始和结束时间,为我们的循环提供了边界。

我试过了。

我最初认为嵌套循环是要走的路。从需要标记的文件复制时间戳数据,创建标签列,然后将其设置为 0。

然后,我将从文件 3 中获取开始和结束时间,然后当文件 2 中的时间介于这些时间之间时,对于文件 2 中的每个活动,设置我的系列的标签列。

我打算像索引一样使用时间,如上所示,但是由于文件 1 是 40hz,我不能只是从文件 2 和 3 在我的时间戳上附加一些零,因为可能没有读取每秒的开始。我知道熊猫有一个时间之间的功能,但这需要我将我的数据分成每天,我不确定如何做到这一点。

经过数周的解决这个问题,我设法提出了一个解决方案。但是,此解决方案仅使用pandas,因此,我将在许多较小的文件上运行它,然后在最后将它们组合在一起。

加载数据

首先,我加载数据:

path = '/media/peter/ElementsSE/Labelling3/Small/prepared_collar3_0.csv'
times = pd.read_csv(path)
path = '/media/peter/Elements SE/Labelling3/Small/3labels.csv'
labels = pd.read_csv(path)
path = '/media/peter/ElementsSE/Labelling3/Small/3TimesForLabels.csv'
label_times = pd.read_csv(path)

准备工作

加载数据后,我为每个数据制作时间戳:

labels['Date'] = pd.to_datetime(labels['Date(M/DD/YY)'])
labels['Timestamp'] = labels.apply(lambda row: str(row['Date'].date()) + ' ' + str(row['Time']),axis=1) 
#Casting from string to timestamp
labels['Timestamp'] = pd.to_datetime(labels['Timestamp'])
label_times['Date'] = pd.to_datetime(label_times['Date(M/DD/YY)'])
label_times['Timestamp'] = label_times.apply(lambda row: str(row['Date'].date()) + ' ' + str(row['Time']),axis=1) 
#Casting from string to timestamp
label_times['Timestamp'] = pd.to_datetime(label_times['Timestamp'])

制作包含标签的新列

确保将数据集的类别列设置为"无",以便我可以看到标记了多少数据。

times['behav'] = None
times.Timestamp = pd.to_datetime(times.Timestamp)
times.head()

循环浏览文件

在此之后,我们需要遍历 3 个文件并标记它们:

#Iterating though labeltimes to get the right start and end times
for i in range(len(label_times)-1):
date = label_times.loc[i,'Date']
focal_start_time = label_times.loc[i,'Timestamp']
focal_end_time = label_times.loc[i+1,'Timestamp']

#Now we iterate though the labels
day_labels = labels.loc[labels.Date == date].reset_index()
for j in range(len(day_labels)-1):
time = day_labels.loc[j,'Timestamp']
next_time = day_labels.loc[j+1,'Timestamp']
behav = day_labels.loc[j,'Focal Behavioral States']
if(time.date() == focal_start_time.date()):
#We have data of the same date 
if(time.time() <= focal_end_time.time()):
if(time.time() >= focal_start_time.time()):
#Our start time is in the time range, now we need to check the end time
if(next_time.time() <= focal_end_time.time()):
if(next_time.time() >= focal_start_time.time()):
times.behav.loc[(times.Timestamp >= time) & (times.Timestamp <= next_time)] = behav

时代现在是一个标记的数据集。您可能会发现查看有多少数据被标记为什么很有用,我使用:

times.behav.value_counts()

抱歉,这非常特定于我的问题,希望这会帮助其他人解决类似的问题,如果他们像我一样,请为他们节省几周的时间。

我最近为这种类型的问题编写了一个脚本,当时我标记了 112,00 行神经时间序列数据,并记录了每个事件的开始和结束时间。

1. 将数据加载到脚本中

加载 (1) 您的时间序列数据,(2) 您的事件标记数据,以及 (3) 时间序列数据的采样率(即您是否以分秒、秒等为单位记录)。

# Your time series data file name
data_file_name = 'data_time_series.csv'
# Your time series data csv
data = pd.read_csv(data_file_name, encoding='utf-8', skiprows=0)
# Your events data
events = pd.read_csv("event_durations.csv", encoding='utf-8', skiprows=1)
sample_rate = 0.1  #Deci-seconds

2. 将开始-结束时间分成秒/分贝一列

此函数只是从事件标记数据帧中获取持续时间,并将其转换为秒/分秒列(具体取决于您在步骤 1 中输入的采样率)。

def addRange (events):
global events_split 
events_split = pd.DataFrame()
events = np.array(events)
row = 0
for _row in events:
x = round(events[row,0],1) # Start time
y = round(events[row,1],1) # End time
events_split = events_split.append(pd.DataFrame([x]))
while x < y:
x = round((x + sample_rate),1)
events_split = events_split.append(pd.DataFrame([x]))
row = row + 1
return events_split
addRange(events)

3. 将该列表转换为包含 1 和 0 的列

对于此函数,我们需要首先创建一个可迭代变量,其中包含时间序列数据的"time"列:

data_time_col = pd.DataFrame([data.iloc[:,0]]) data_time_col = data_time_col.T

然后,下面的函数使用此变量以及新细分的事件标记数据来创建与时间序列数据上的行完全匹配的 1 和 0 列。

def addEvents(data_time_col):
global labels_01_df
labels_01_df = pd.DataFrame() 
for i in data_time_col.values:
if i in events_split.values:
labels_01_df = labels_01_df.append([1], ignore_index=True)
else:
labels_01_df = labels_01_df.append([0], ignore_index=True)
return labels_01_df
addEvents(data_time_col)

4. 将 1 和 0 列写入数据文件中的"标签"列

最后,我们需要将 0 和 1 的列表插入到时间序列数据帧中,并将其导出为 csv。(新列将插入索引 [0] 中并推动其他列)。新的标记文件将显示在您的目录中,其名称与原始文件相同,但末尾带有"LABELLED"。

data.insert(loc=0, column="labels", value=labels_01_df)
data.to_csv(data_file_name + " - LABELLED.csv", index=False)

我希望这是有帮助的。这里还有一些关于这个过程的进一步信息:https://medium.com/@lucy.m.rothwell/labelling-time-series-data-in-python-af62325e8f60

最新更新