Pandas 预处理:使用另外 2 个文件标记时间序列数据

Pandas Preprocessing: Labelling time series data using 2 other files

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

这几天一直困扰着我。

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

1。数据

Here 是我需要标记的数据框的样子。数据每秒收集 40 次,持续数月。我也有不同个人的所有这些数据,所以我将它们分成每个人,然后分成包含 1000000 行的文件。 CSV 文件最初有日期、小时、秒、小数秒的单独列,但我将其制成一个时间戳,希望我可以将其用作索引,然后执行类似的操作:

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

我也在考虑使用 unix 时间戳作为索引。 timestamp 的 dtype 是 object.

用于标记的文件

2。行为状态

Here 是带有每个标签的文件的样子。所以我们可以看到修饰从 11:26:48 开始并在 11:28:32 结束。然而,与未观察动物时的数据存在很大差距。所以我们不能总是从下面取结束时间。这是我们最终数据框的来源。

3。观察的开始和结束时间。

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

我试过的。

我最初认为嵌套 for 循环是可行的方法。从需要标记的文件中复制时间戳数据,制作标签列然后将其设置为 0.

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

我打算像上面显示的那样使用时间索引,但是由于文件 1 是 40hz,我不能只在文件 2 和 3 的时间戳上附加一些零,因为开始时可能没有读数每秒。我知道 pandas has a between time function,但是这需要我将我的数据分成每一天,我不知道该怎么做。

经过数周的努力解决这个问题,我设法想出了一个解决方案。但是,此解决方案仅使用 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'])

创建包含标签的新列

确保将我的数据集的类别列设置为 None,这样我就可以看到有多少数据被标记了。

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 现在是一个带标签的数据集。您可能会发现查看有多少数据标有什么很有用,我这样做是使用:

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。将开始-结束时间分成 seconds/deci-seconds

此函数只是从您的事件标记数据框中获取持续时间,并将其转换为 seconds/deci-seconds 列(取决于您在步骤 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 的列

对于这个函数,我们需要先创建一个包含时间序列数据的“时间”列的可迭代变量:

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