【问题标题】:R: ifelse statement test involving multiple dataframesR:涉及多个数据帧的 ifelse 语句测试
【发布时间】:2026-01-08 08:45:01
【问题描述】:

我正在尝试通过组合来自两个data.frames 的数据来使用ifelse 创建一个新变量(类似于this 问题,但没有因素)。

我的问题是 df1 具有年度数据,而 df2 中的变量是时间聚合的:例如df1 有多个 obs (1997,1998,...,2005),df2 只有一个范围 (1900-2001)。

为了说明,一个 2x2 的例子看起来像

df1$id <- c("2","20")
df1$year <- c("1960","1870")

df2$id <- df1$id
df2$styear <- c("1800","1900")
df2$endyear <- c("2001","1950")

我想将两者结合起来,使 id(两者中都存在相同的变量)匹配,此外,df1 中的年份在df2 的范围内。我尝试了以下

df1$new.var <- ifelse(df1$id==df2$id & df1$year>=df2$styear & 
df1$year<df2$endyear,1,0)

理想情况下应该分别返回 1 和 0。

但我收到警告消息:

1:在 df1$id == df2$id :更长的对象长度不是的倍数 更短的物体长度

2:在 df1$year >= df2$styear :更长的对象长度不是 短物体长度的倍数

3:在 df1$year

为了记录,“真实的”df1 有 500 个 obs,df2 有 14 个。我怎样才能做到这一点?

编辑:我意识到df2 中的一些 obs 是重复的,有多个句点,例如

id    styear    endyear
1      1800      1915
1      1950      2002
2      1912      1988
3      1817      2000

所以,我相信我需要的是类似双 ifelse 的东西:

df1$new.var <- ifelse(df1$id==df2$id & df1$year>=df2$styear & 
df1$year<df2$endyear | df1$year>=df2$styear & 
df1$year<df2$endyear,1,0)

显然,这行不通,但它是摆脱重复问题的一种方法。

例如,如果id=1df1$year=1801 中,它将通过第一个年份范围测试(1801 在 1800-1915 之间),但第二个不通过(1801 不在 1950-2002 之间),所以它只编码一次,没有添加额外的行(当前重复添加额外的行)。

【问题讨论】:

标签: r if-statement dataframe dplyr


【解决方案1】:

这可以使用data.table devel version (1.9.7+) 中的非等连接轻松有效地解决:

library(data.table)
setDT(df1); setDT(df2) # converting to data.table in place

df1[, new.var := df2[df1, on = .(id, styear <= year, endyear >= year),
                     .N > 0, by = .EACHI]$V1]
df1
#   id year new.var
#1:  2 1960    TRUE
#2: 20 1870   FALSE

上述连接在df2 中为df1 (by = .EACHI) 的每一行查找匹配项,并检查匹配行数(.N)。

【讨论】:

  • 这听起来很棒,但是目前并没有给我同样的结果——我在我的实际数据和@sandipan 的 mwe 上都试过了,都给出了相同的警告:[.data.table( df2, df1, on = list(id, styear = : object 'styear' not found
  • @rfsrc 在新的 R 会话中重新开始(并确保您已安装开发版本)。
  • 好的,现在在删除软件包并安装开发后会好一点。所以,现在它肯定适用于 mwe,但它仍然会给出实际数据的错误: eval 中的错误(expr,envir,enclos):找不到对象 'styear'。我很确定我的 df2 有一个 styear,尝试换班但仍然没有区别。
  • @rfsrc 如果没有可重复的示例,我不知道如何帮助您
【解决方案2】:

好吧,我让它为自己工作。请注意,它非常复杂,可能包含一些冗余。简要查看数据争吵cheatsheet 后,假设您有df1df2 具有相同的vardf2 包含new.var,可以执行以下操作:

library(dplyr)
#Join everything, all values and rows
df3 <- full_join(df1,df2,by="id")
#filter out obs those year is greater than endyear
df3 <- filter(df3,df3$year<=df3$endyear)
#same, the other way around
df3 <- filter(df3,df3$year>=df3$styear) 
df3 <- distinct(df3) #remove duplicate rows (at least I had some)

据我通过查看最终结果可以看出,此方法仅从正确的时间段中提取信息,而在df2 中删除所有其他时间段。然后,就是和原来的data.frame(df1)合并,填入NAs:

df1 <- merge(df1,df3,by=(id),all.x=TRUE)
df1 <- distinct(df1) #just to make sure, I still had three
df1$new.var <- ifelse(is.na(df1$new.var),0,df1$new.var)

这正是我想要的。

【讨论】:

    【解决方案3】:
    df1$id <- c("2","20")
    df1$year <- c("1960","1870")
    
    df2$id <- df1$id
    df2$styear <- c("1800","1900")
    df2$endyear <- c("2001","1950")
    
    library(dplyr)
    df3 <- left_join(df1,df2,by = "id") %>% filter(year <= endyear,year >= startyear)
    

    我强烈推荐用于数据操作的 dplyr 包。

    【讨论】:

    • dplyr 确实很棒,希望我能做得更好。回复:你的答案,这确实有效,因为当它是“1”时识别被正确执行,但结果数据集只是原始数据集的一个子集 - “0”案例被逐列删除。有没有办法保持 df1 完好无损?
    • 奇怪的是,从 inner_join 到 left_join 没有效果 - 仍然丢失所有 0 的 obs。
    • 尝试翻转参数中数据帧的顺序
    • 更改顺序也没有任何效果,仍然损失了一半左右。然而,它让我更多地阅读了 dplyr,现在我找到了一种解决方案。
    【解决方案4】:

    使用基础 R:

    df1 <- data.frame(id=c(2,20,22), year=c(1960,1870, 2016))
    df2 <- data.frame(id=c(2,20,21), styear=c(1800,1900,2000), endyear=c(2001,1950,2016))
    
    df1
    id year
    1  2 1960
    2 20 1870
    3 22 2016
    
    df2
    
    id styear endyear
    1  2   1800    2001
    2 20   1900    1950
    3 21   2000    2016
    
    df1 <- merge(df1, df2, by='id', all.x = TRUE)
    df1$new.var <- !is.na(df1$styear) & df1$year>=df1$styear & df1$year< df1$endyear
    df1 <- df1[c('id', 'year', 'new.var')]
    
    df1
      id year new.var
    1  2 1960    TRUE
    2 20 1870   FALSE
    3 22 2016   FALSE
    

    【讨论】:

    • 这更符合我的想法。不过有一件事 - 我在 df1 中重复了 id-year 组合 - 当没有重复时,代码运行良好,但在某些情况下会导致旋转 TRUE/FALSE。想法?
    • 我们能否先从 df1 中选择唯一的 id-year 对,然后创建 new-var。一旦创建,来自 df1 的重复 id-years 被遗漏,将具有完全相同的 new.var 值,因此如果您计算了重复的 id-year 对,只需复制/添加它们(例如, 带 rbind)。
    • 我创建了 id-years,但仍然无法绕过匹配的问题。你看到我问题末尾的编辑了吗?我认为这就是原因。