data.frame 中的子集特定日期(年和月)

Subset specific dates (year and month) from data.frame

这是我的 data.frame:

df = read.table(text = 'ID   Date
1      1975-01-01
2      1980-02-01
3      1985-05-01
4      1990-07-01
5      1990-08-01
6      1993-01-01
7      1993-09-01', header = TRUE)

我需要通过选择特定日期(年和月)从中创建一个子集。

我感兴趣的日期是:

dates = c('1980-02', '1990-07', '1993-09')

因此我的输出应该是:

ID     Date
2      1980-02-01
4      1990-07-01
7      1993-09-01

是否有可能编写一个独特的代码来完成从 dfdates 开始的技巧,而无需在代码中手动添加日期?

谢谢

####更新

如果我在 df 的同一个月内有多个观察结果怎么办,例如:

df2 = read.table(text = 'ID   Date
1      1975-01-01
2      1980-02-01
9      1980-02-01
3      1985-05-01
4      1990-07-01
12     1990-07-01
16     1990-07-01
5      1990-08-01
6      1993-01-01
7      1993-09-01
67     1993-09-01', header = TRUE)

新输出:

ID     Date
2      1980-02-01
9      1980-02-01
4      1990-07-01
12     1990-07-01
16     1990-07-01
7      1993-09-01
67     1993-09-01

谢谢

根据您共享的数据,Date 列的 class 是因素。我们将它们转换为 Date class 并提取月份和年份部分并将其与 dates 向量匹配以从 df 中获取匹配的行号。

df[match(dates, format(as.Date(df$Date), "%Y-%m")), ]  

#   ID       Date
#2  2 1980-02-01
#4  4 1990-07-01
#7  7 1993-09-01

根据更新后的问题,如果我们有多个日期要匹配,我们可以使用 %in% 这将为您提供所需的输出。

df2[format(as.Date(df2$Date), "%Y-%m") %in% dates, ]

#   ID       Date
#2   2 1980-02-01
#3   9 1980-02-01
#5   4 1990-07-01
#6  12 1990-07-01
#7  16 1990-07-01
#10  7 1993-09-01
#11 67 1993-09-01

尝试

    S = sapply(dates, function(d) { grep(d, df[,2]) })
    df[S,]

@eipi10 在评论中指出:

df[df$Date %in% as.Date(paste0(dates,"-01")), ]

这对我更新的问题很有效。

谢谢

这里有一些解决方案。它们 (i) 使用任何日期,而不仅仅是月份的第一天,(ii) 在输出中保留 df2 的顺序,(iii) 是紧凑的,即每行一行并且不需要多次提及 df2

1) substr 这不使用包。

subset(df2, substr(Date, 1, 7) %in% dates)

给予:

   ID       Date
2   2 1980-02-01
3   9 1980-02-01
5   4 1990-07-01
6  12 1990-07-01
7  16 1990-07-01
10  7 1993-09-01
11 67 1993-09-01

2) zoo::as.yearmon 另一种可能性是将 Datedates 都转换为 "yearmon" class 给出相同的结果。这段代码更好一些,但确实需要一个包。

library(zoo)
subset(df2, as.yearmon(Date) %in% as.yearmon(dates))

您的日期格式为字符串,因此使用起来有些困难。通常,最好将它们格式化为实际的日期对象。这可以使用 the lubridate package 来完成。这就是我会做的。我使用 readr 包进行自动类型检测,purrr 进行函数式编程。

library(pacman)
p_load(lubridate, readr, purrr)

df = read_table(
'ID   Date
1      1975-01-01
2      1980-02-01
9      1980-02-01
3      1985-05-01
4      1990-07-01
12     1990-07-01
16     1990-07-01
5      1990-08-01
6      1993-01-01
7      1993-09-01
67     1993-09-01'
)

dates = parse_date_time(c('1980-02', '1990-07', '1993-09'), orders = "Y-m")

#subset
df[year(df$Date) %in% year(dates) & month(df$Date) %in% month(dates), ]

其输出为:

# A tibble: 7 × 2
     ID       Date
  <int>     <date>
1     2 1980-02-01
2     9 1980-02-01
3     4 1990-07-01
4    12 1990-07-01
5    16 1990-07-01
6     7 1993-09-01
7    67 1993-09-01

因此,我们像您一样加载数据,但使用 readr 进行加载,以便自动识别日期。然后我们将年份与 dates 对象中的任何年份相匹配的行进行子集化,月份与 dates 对象中的任何月份相匹配。这给出了你想要的输出。

但是,也许您只想要您提供的组合。所以例如1980 年只有在第 2 个月出现时才可以。如果是这样,那就有点复杂了。这可以通过多种方式完成,但我选择了一种功能性方法。这执行起来不是最快的,但是编写起来很快而且非常灵活。

#subset stricter
inclusion_func = function(x, desired_dates) {
  #loop over each date
  map_lgl(x, function(date) {
    any(map_lgl(desired_dates, function(desired_date) {
      year(date) == year(desired_date) && month(date) == month(desired_date)
    })
    )
  })
}

df[inclusion_func(df$Date, dates), ]

输出相同:

# A tibble: 7 × 2
     ID       Date
  <int>     <date>
1     2 1980-02-01
2     9 1980-02-01
3     4 1990-07-01
4    12 1990-07-01
5    16 1990-07-01
6     7 1993-09-01
7    67 1993-09-01

函数的作用是遍历数据框中的每个日期,并遍历每个 year/month 组合。然后它会检查年份和月份是否与该特定组合相匹配。如果三个组合中的任何一个匹配(因此 any),则该行 returns TRUE