【问题标题】:R: fill in cells with values from different rowsR:用来自不同行的值填充单元格
【发布时间】:2021-10-24 18:15:49
【问题描述】:

我正在尝试用来自不同行的值填充一行中的 NA。这些行通过案例编号“链接”。我想编写一个遍历整个数据帧并执行此操作的 if 循环。但我认为我对 R 语言的掌握不够好。有人可以帮帮我吗?

数据框:

CASE <- c(1, 2, 3, 4, 5, 6)
SERIAL <-c("AB",NA, NA, "CD", NA, NA)
REF <- c(NA, 1, 1, NA, 4, 4)
PA <- c(4, NA, NA, 2, NA, NA)
PE <- c(NA, 2, NA, NA, 1, NA)
PE2 <- c(NA, NA, 3, NA, NA, 3)

df <- data.frame (CASE, SERIAL, REF, PA, PE, PE2)

  CASE SERIAL REF PA PE PE2
    1     AB  NA  4  NA  NA
    2   <NA>   1 NA   2  NA
    3   <NA>   1 NA  NA   3
    4     CD  NA  2  NA  NA
    5   <NA>   4 NA   1  NA
    6   <NA>   4 NA  NA   3

在 CASE = 1 行中,我想用下面行中的值填充空的 PE 和 PE2,这些行引用了该行(通过 REF = 1)。在 CASE = 4 行中,我想用下面行中的值填充空的 PE 和 PE2,这些行引用了该行(通过 REF = 4)。可以这么说,没有序列号的行仅用于填充第 1 行和第 4 行。没有办法将数据直接收集到相应的行中。我试过这个 for 循环,但不知道如何正确引用这些值?

for (i in 1:dim(df)[1]{
  if (data$SERIAL[i]==NA){
    [data$CASE[data$REF[i]],PE] <- data$PE[i]
    [data$CASE[data$REF[i]],PE2] <- data$PE2[i]}
}
)

预期输出:

  CASE SERIAL REF PA PE PE2
1    1     AB  NA  4  2   3
2    2   <NA>   1 NA  2  NA
3    3   <NA>   1 NA NA   3
4    4     CD  NA  2  1   3
5    5   <NA>   4 NA  1  NA
6    6   <NA>   4 NA NA   3

【问题讨论】:

  • 你能为你的示例输入提供一个预期的输出吗?谢谢。

标签: r loops for-loop if-statement


【解决方案1】:

更新:感谢 Martin Gal 添加了 library(tidyr),并添加了 Martin Gal 建议的替代代码:

这是另一种dplyr方式:

  1. 填写SERIAL
  2. 在 grouped_columns 中使用 lead
  3. 仅保留带有slice(1) 的组的第一行
library(dplyr)
library(tidyr)

  df %>% 
    fill(SERIAL, .direction = "down") %>% 
    group_by(SERIAL) %>% 
    mutate(PE = lead(PE),
            PE2 = lead(PE2,2)) %>% 
    slice(1)

# Alternative and better (suggested by Martin Gal):
df %>% fill(-c(CASE, SERIAL), .direction = "up") %>% drop_na()

   CASE SERIAL   REF    PA    PE   PE2
  <dbl> <chr>  <dbl> <dbl> <dbl> <dbl>
1     1 AB        NA     4     2     3
2     4 CD        NA     2     1     3

【讨论】:

  • (1) 为什么不简单地df %&gt;% fill(-c(CASE, SERIAL), .direction = "up") %&gt;% drop_na()? (2) 你错过了library(tidyr)。 :-)
  • 谢谢马丁盖尔。我错过了你的评论。现在我更新了!!!
【解决方案2】:
withSerial = subset(df, !is.na(SERIAL))

withSerial
#  CASE SERIAL REF PA PE PE2
#1    1     AB  NA  4 NA  NA
#4    4     CD  NA  2 NA  NA

noSerialwithRef = subset(df, is.na(SERIAL) & !is.na(REF))

noSerialwithRef
#  CASE SERIAL REF PA PE PE2
#2    2   <NA>   1 NA  2  NA
#3    3   <NA>   1 NA NA   3
#5    5   <NA>   4 NA  1  NA
#6    6   <NA>   4 NA NA   3

withSerial$PE = subset(noSerialwithRef, !is.na(PE))$PE
withSerial$PE2 = subset(noSerialwithRef, !is.na(PE2))$PE2

withSerial
#  CASE SERIAL REF PA PE PE2
#1    1     AB  NA  4  2   3
#4    4     CD  NA  2  1   3

【讨论】:

    【解决方案3】:

    这是一个dplyr 解决方案,但也许它会起作用:

    df %>%
      mutate(REF = ifelse(is.na(REF), CASE, REF)) %>% 
      group_by(REF) %>% 
      summarise(SERIAL = first(SERIAL), 
                across(c(PA, PE, PE2), ~sum(.x, na.rm=TRUE))) %>% 
      rename("CASE" = "REF")
    
    # # A tibble: 2 x 5
    #   CASE SERIAL    PA    PE   PE2
    #   <dbl> <chr>  <dbl> <dbl> <dbl>
    # 1     1 AB         4     2     3
    # 2     4 CD         2     1     3
    
    
    

    【讨论】:

    • 哇,谢谢,这是最好的解决方案!因为它也适用于“混合”行,即如果引用 AB 的行出现较晚,位于 CD 下方。你能给我解释一下,“summarise(SERIAL = first(SERIAL)”和“~sum(.x, na.rm=TRUE)”是做什么的吗?
    • first(SERIAL) 取组中 serial 的第一个值,~sum(.x, na.rm=TRUE) 计算 PAPEPE2 列的总和并按列表删除。由于每个只有一个非缺失数字,因此它只返回该数字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-19
    • 2021-09-27
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    相关资源
    最近更新 更多