背景
最近一个读者问到了这样的一个问题:
- 有一个时间序列的数据。
- 每半小时记录一次数据,但有的地方有缺失,时间序列不完整。
- 需要对缺失的部分,填充为NA。
- 也就是缺少的时间要加到表哥里面,缺失的值是要填充为NA
好久没写R代码了,这里写一写吧。
先创建一个样本
library(tidyverse)
sample_data = tibble('mytime'=c(
lubridate::as_datetime("2022-01-01 12:30:00"),
lubridate::as_datetime("2022-01-01 13:30:00"),
lubridate::as_datetime("2022-01-01 14:00:00"),
lubridate::as_datetime("2022-01-01 15:30:00")
),
'value' = c(1,4,5,8),
'value2' = c(3,4,5,6),
'value3' = c(20, 20, 30, 40))
sample_data
# # A tibble: 4 × 4
# mytime value value2 value3
# <dttm> <dbl> <dbl> <dbl>
# 1 2022-01-01 12:30:00 1 3 20
# 2 2022-01-01 13:30:00 4 4 20
# 3 2022-01-01 14:00:00 5 5 30
# 4 2022-01-01 15:30:00 8 6 40
可以看出来上面的数据中:
- 数据的时间范围是从
2022-01-01 12:30到2022-01-01 15:30的。 - 每隔30分钟就记录一次。
- 但是缺少了
13:00、14:30、15:00时刻的数据。
解决办法
思路很简单:
- 就是创建一个连续的、每隔30分钟的、完整的时间列表。
- 然后把列表放到数据框里面。
- 然后使用left_join的特性,直接对表格进行关联即可,会自动填充NA
1.创建连续的时间列表
create_time_seq <- function(start_date, end_date, by){
n = lubridate::interval(start = start_date, end = end_date) / by
res <- start_date + by * c(0:n)
return(res)
}
time_seq <- create_time_seq(
start_date = sample_data %>% pull(mytime) %>% min(),
end_date = sample_data %>% pull(mytime) %>% max(),
by = lubridate::minutes(30)
)
time_seq
# [1] "2022-01-01 12:30:00 UTC" "2022-01-01 13:00:00 UTC" "2022-01-01 13:30:00 UTC"
# [4] "2022-01-01 14:00:00 UTC" "2022-01-01 14:30:00 UTC" "2022-01-01 15:00:00 UTC"
# [7] "2022-01-01 15:30:00 UTC"
这里的难点主要是在create_time_seq函数里面,这个函数的主要功能有:
- 这个函数计算了从
开始时间到结束时间,按照步长为by的时间节点的个数。 - 然后把这个范围内的节点都计算出来。保证一个节点都不少。
最后time_seq就是按照样本数据的时间范围,和要求的步长,生成了一串符合要求的时间序列列表。
2. 关联
tibble(
'true_time' = time_seq
) %>% left_join(
y=sample_data,
by=c('true_time' = 'mytime')
)
# # A tibble: 7 × 4
# true_time value value2 value3
# <dttm> <dbl> <dbl> <dbl>
# 1 2022-01-01 12:30:00 1 3 20
# 2 2022-01-01 13:00:00 NA NA NA
# 3 2022-01-01 13:30:00 4 4 20
# 4 2022-01-01 14:00:00 5 5 30
# 5 2022-01-01 14:30:00 NA NA NA
# 6 2022-01-01 15:00:00 NA NA NA
# 7 2022-01-01 15:30:00 8 6 40
把列表放到数据框里面,然后巧妙的使用left_join函数,对两个表进行关联,从而对缺失的节点直接进行填充。
总结
- 这个只是一个非常小的,对R语言的时间做处理的小案例。
- 建议大家在R中处理时间的时候,最好都用
lubridate包来处理。 - 巧妙的生成时间序列、然后使用数据框来关联,这个思想在python里面也是常见的。如果用python写的话,其实就是使用列表推导式、datetime包、pandas包来做。
我就知道你不信,我给你写过代码就知道了
python版本
# part1
from datetime import datetime,timedelta
import pandas as pd
# part 2
sample_date = pd.DataFrame({'mytime':[datetime.strptime(i,'%Y-%m-%d %X') for i in ['2022-01-01 12:30:00', '2022-01-01 13:30:00', '2022-01-01 14:00:00', '2022-01-01 15:30:00']],
'value1':[1,4,5,8], 'value2':[3,4,5,6], 'value3':[20, 20, 30, 40]})
sample_date
# mytime value1 value2 value3
# 0 2022-01-01 12:30:00 1 3 20
# 1 2022-01-01 13:30:00 4 4 20
# 2 2022-01-01 14:00:00 5 5 30
# 3 2022-01-01 15:30:00 8 6 40
# part 3
def create_time_seq(start_date, end_date, by):
n = (end_date - start_date ) / by
res = [start_date + by * i for i in range(int(n+1))]
return res
time_seq = create_time_seq(start_date=sample_date['mytime'].min(),
end_date=sample_date['mytime'].max(),
by=timedelta(minutes=30))
time_seq
# [Timestamp('2022-01-01 12:30:00'),
# Timestamp('2022-01-01 13:00:00'),
# Timestamp('2022-01-01 13:30:00'),
# Timestamp('2022-01-01 14:00:00'),
# Timestamp('2022-01-01 14:30:00'),
# Timestamp('2022-01-01 15:00:00'),
# Timestamp('2022-01-01 15:30:00')]
# part 4
pd.DataFrame({'true_time':time_seq}).pipe(
lambda x: x.merge(
right=sample_date,
how='left',
left_on=['true_time'],
right_on=['mytime']
)
).pipe(
lambda x: x.drop(columns=['mytime'])
)
# true_time value1 value2 value3
# 0 2022-01-01 12:30:00 1.0 3.0 20.0
# 1 2022-01-01 13:00:00 NaN NaN NaN
# 2 2022-01-01 13:30:00 4.0 4.0 20.0
# 3 2022-01-01 14:00:00 5.0 5.0 30.0
# 4 2022-01-01 14:30:00 NaN NaN NaN
# 5 2022-01-01 15:00:00 NaN NaN NaN
# 6 2022-01-01 15:30:00 8.0 6.0 40.0
最终
- 没想到吧,学R的时候,竟然还学了PYTHON
- 有学习交流群,如果想加入本群的,想办法找到我~
阅读更多
list
使用R和Python填充时间序列数据中的缺失值

本文介绍如何使用R和Python处理时间序列数据中的缺失值。通过创建连续的时间序列并使用`left_join`或`merge`函数,将缺失值自动填充为NA。在R中,利用`lubridate`包处理时间,并编写`create_time_seq`函数生成时间列表;在Python中,结合`datetime`和`pandas`库实现相同功能。

被折叠的 条评论
为什么被折叠?



