【问题标题】:R: Replace NA in for loop with respective but changed variable nameR:用相应但已更改的变量名称替换 for 循环中的 NA
【发布时间】:2021-01-21 16:19:10
【问题描述】:

我手头有一个多级数据集df,隶属于以下组织:

    
ID     Eye   Video_number  Time  Day   measurement1
40001   L         1         1     1        0.60 
40001   L         2         1     1        0.50 
40001   L         3         1     1        0.80 
40001   L         1         2     1        0.60 
40001   L         2         2     1        0.60 
40001   L         3         2     1        0.60 

目标我正在尝试用NA 替换方差系数大于 45 的测量值的单元格值,因为这些值可能不太精确,应该被排除在外。

分布的变异系数(有时表示为 CV)定义为标准差与平均值的比率,其中 $\mu$ 和 $\sigma$ 值从原始数据中获得

  1. 我使用以下函数和 for 循环按时间单位(一个时间单位中三个视频的平均测量值)获得了 CV 值。我从以下主题中获得了帮助:

How to correctly use group_by() and summarise() in a For loop in R

Append data frames together in a for loop

# Define function
cv <- function(x){
  sd(na.omit(x))/mean(na.omit(x))*100}


# Variables
vars <- c("measurement1", "measurement2", "measurement3")

# Create a table with all CV values by ID, Eye, Day, and Time
df_cv=data.frame()
for (i in vars){
  df<-df.m2
  df$values<-df[,which(colnames(df.m2)==i)]
  x<-df%>%
    group_by(ID,Eye,Day,Time) %>%
    summarise(Count = n(),
              Mean = mean(values, na.rm = TRUE),
              SD = sd(values, na.rm = TRUE),
              CV = cv(values))%>%
    mutate(Variable=paste(i,"cv",sep="_"))
  df_cv<-rbind(df_cv,x)
  df_cv$CV[is.nan(df_cv$CV)]<-0    # for 0/0 on CV formula giving NaN

}


  1. 结果如下表df_cv
 ID    Eye Day Time Count      Mean         SD        CV         Variable
40001   L   1   1   3       0.56666667  0.057735027 10.1885342  measurement1_cv
40001   L   1   2   3       0.36666667  0.404145188 110.2214150 measurement1_cv
40001   L   1   3   3       0.50000000  0.000000000 0.0000000   measurement1_cv
  1. 我将上面的 df_cv 重新格式化为宽格式(变量和 CV 跨行而不是列)。这使我能够将 CV 与原始 df 合并
df_cv<-dcast(df_cv,PIDN+Eye+Day+Time~Variable,value.var = "CV")
df<-merge(df,df_cv,by=c("PIDN","Eye","Day","Time"))
    
ID     Eye   Video_number  Time  Day   measurement1     measurement1_cv
40001   L         1         1     1        0.60           10.1885342
40001   L         2         1     1        0.50           10.1885342
40001   L         3         1     1        0.80           10.1885342
40001   L         1         2     1        0.80           110.2214150 
40001   L         2         2     1        0.30           110.2214150 
40001   L         3         2     1        0.00           110.2214150 
  1. 我知道想将 NA 输入到 CV>45 的测量 1 的单元格中。我知道如何通过测量来进行测量,但我想知道是否有一个 for 循环能够做到这一点,因为我要分析很多变量。
df$measurement1[df$measurement1_cv>45]<-NA
df$measurement2[df$measurement2_cv>45]<-NA
df$measurement3[df$measurement3_cv>45]<-NA

以下是我失败的尝试:

for (i in vars) {
  df<-df.m3
  df$i[df$i_cv>45]<-NA
}

Error in `$<-.data.frame`(`*tmp*`, "i", value = logical(0)) : 
  replacement has 0 rows, data has 609



for (i in vars) {
  df<-df.m3
  df$i[df$paste(i,"_cv")>45]<-NA
}

Error in df$paste(i, "_cv") : attempt to apply non-function


非常感谢任何帮助!

【问题讨论】:

  • df[,c(2,4,17,23)] &lt;- lapply(df[,c(2,4,17,23)], function(a) replace(a, a &gt; 45, NA)) 这里的数字向量是您需要的列。也可以是逻辑向量,可能来源于startsWith(colnames(df), "measurement")
  • 谢谢!但是,我需要按 Time 实例应用 CV 公式,这就是我使用 group_by(ID,Eye,Day,Time) 的原因我尝试了以下操作:lapply(df[,vars], function(cv) replace(cv,cv&gt;45,NA)) 并且我认为它不能识别时间单位,通过它来采取手段和 sd视频。它用NA 替换了许多值。

标签: r dataframe for-loop na


【解决方案1】:

两个想法。

但首先,稍微扩充您的数据。

df$measurement2 <- 0.9; df$measurement2_cv <- c(rep(46, 3), rep(44, 3))
library(dplyr)
  1. 一次只对一个组进行操作的内联函数(即,它忽略分组,因此它必须在dplyr::do 内)。

    myfunc <- function(x) {
      CV <- grep("^measurement.*_cv", colnames(x), value = TRUE)
      MEAS <- gsub("_cv$", "", CV)
      CV <- CV[MEAS %in% colnames(x)]
      MEAS <- MEAS[MEAS %in% colnames(x)]
      x[,MEAS] <- Map(function(meas, cv) replace(meas, cv > 45, NA), x[,MEAS], x[,CV])
      x
    }
    df %>%
      group_by(ID, Eye, Day, Time) %>%
      do(myfunc(.))
    # # A tibble: 6 x 9
    # # Groups:   ID, Eye, Day, Time [2]
    #      ID Eye   Video_number  Time   Day measurement1 measurement1_cv measurement2 measurement2_cv
    #   <int> <chr>        <int> <int> <int>        <dbl>           <dbl>        <dbl>           <dbl>
    # 1 40001 L                1     1     1          0.6            10.2         NA                46
    # 2 40001 L                2     1     1          0.5            10.2         NA                46
    # 3 40001 L                3     1     1          0.8            10.2         NA                46
    # 4 40001 L                1     2     1         NA             110.           0.9              44
    # 5 40001 L                2     2     1         NA             110.           0.9              44
    # 6 40001 L                3     2     1         NA             110.           0.9              44
    
  2. 使用tidyr 进行透视、计算,然后取消透视。

    library(tidyr) # pivot_*
    df %>%
      rename_with(.fn = ~ paste0(., "_val"), .cols = matches("^meas.*[^v]$")) %>%
      rename_with(.fn = ~ gsub("(.*)_(.*)", "\\2_\\1", .), .cols = starts_with("meas")) %>%
      pivot_longer(., matches("meas"), names_sep = "_", names_to = c(".value", "meas")) %>%
      mutate(val = if_else(cv > 45, NA_real_, val)) %>%
      pivot_wider(1:5, names_from = "meas", names_sep = "_", values_from = c("val", "cv"))
    # # A tibble: 6 x 9
    #      ID Eye   Video_number  Time   Day val_measurement1 val_measurement2 cv_measurement1 cv_measurement2
    #   <int> <chr>        <int> <int> <int>            <dbl>            <dbl>           <dbl>           <dbl>
    # 1 40001 L                1     1     1              0.6             NA              10.2              46
    # 2 40001 L                2     1     1              0.5             NA              10.2              46
    # 3 40001 L                3     1     1              0.8             NA              10.2              46
    # 4 40001 L                1     2     1             NA                0.9           110.               44
    # 5 40001 L                2     2     1             NA                0.9           110.               44
    # 6 40001 L                3     2     1             NA                0.9           110.               44
    

    (我承认这看起来/感觉 wonky,应该需要所有重命名。但现在您需要在最后一步取消重命名。)也许第一个解决方案更清洁/更好: -)

【讨论】:

    猜你喜欢
    • 2021-12-14
    • 1970-01-01
    • 2019-01-31
    • 1970-01-01
    • 2020-04-23
    • 2014-04-15
    • 1970-01-01
    • 2018-05-12
    相关资源
    最近更新 更多