通过数据循环设置值>;或<;变量为R中的NA

通过数据循环设置值>;或<;变量为R中的NA,r,loops,na,R,Loops,Na,我有一个包含整数、字符和数字的列的数据框。实际的数据集比下面给出的例子要大得多,但下面的是一个可以接受的小得多的模拟 我正在尝试循环遍历数据,并将任何大于平均值+(3*标准偏差)且小于平均值-(3*标准偏差)的值更改为NA,仅在数字列中。如果一列包含整数或字符,循环应跳过它并继续到下一列。此外,大多数列已经包含一些NA值,并且将有许多值属于平均值+/-(3*sd)。这些价值观需要保持现状 此脚本的最终目标是在具有相同结构的未来数据集上使用它,虽然我对包的建议持开放态度,但如果可能,我希望使用循环

我有一个包含整数、字符和数字的列的数据框。实际的数据集比下面给出的例子要大得多,但下面的是一个可以接受的小得多的模拟

我正在尝试循环遍历数据,并将任何大于
平均值+(3*标准偏差)
且小于
平均值-(3*标准偏差)
的值更改为
NA
,仅在数字列中。如果一列包含整数或字符,循环应跳过它并继续到下一列。此外,大多数列已经包含一些
NA
值,并且将有许多值属于
平均值+/-(3*sd)
。这些价值观需要保持现状

此脚本的最终目标是在具有相同结构的未来数据集上使用它,虽然我对包的建议持开放态度,但如果可能,我希望使用循环。然而,我远非R方面的专家,我很乐意接受任何人给我的建议

我已经为整个脚本制定了一个结构,但它在第一个
next
语句之后停止

剧本:

data = data.frame(test_data)

for (i in colnames(data)){
  if (class(data$i) == "numeric"){
    m = mean(data$i, na.rm=TRUE)
    sd = sd(data$i, na.rm=TRUE)
  }
    else
      next
  for (j in 1:nrow(data)){
    if (data$i[j,] > (m + 3*sd)){
      data$i[j,] <- NA
    }
    else if (data$i[j,] < (m - 3*sd)){
      data$i[j,] <- NA
    }
    else 
      next
    }
}

提前感谢您提供的任何帮助,我非常感谢

使用
dplyr
并使用
scale()
将数值变量转换为z分数,这可以简化为:

library(dplyr)

test_data %>% 
  mutate_if(is.numeric, ~replace(.x, abs(scale(.x)) > 3, NA))

下面是一个解决方案,使用
purrr
包中的
map\u df
函数,没有任何循环(抱歉:):

library(purrr)

Trait1 = c(1.1, 1.2, 1.35, 1.1, 1.2, NA, 1000, 1.5, 1.4, 1.6)
Trait2 = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J")
Trait3 = c(125.1, 119.3, 118.4, NA, 1.1, 122.3, 123.4, 125.7, 121.5, 121.7)
test_data = data.frame(Trait1, Trait2, Trait3)

map_df(test_data,function(x) {
  if(class(x) == "numeric"){
    x[x <= (mean(x,na.rm = T) - 3*sd(x,na.rm = T)) | x>= (mean(x,na.rm = T) + 3*sd(x,na.rm = T))] = NA      
  }
  return(x)
}
)
库(purrr)
Trait1=c(1.1,1.2,1.35,1.1,1.2,NA,1000,1.5,1.4,1.6)
Trait2=c(“A”、“B”、“c”、“D”、“E”、“F”、“G”、“H”、“I”、“J”)
Trait3=c(125.1193118.4,NA,1.1122.3123.4125.7121.5121.7)
测试数据=数据帧(Trait1、Trait2、Trait3)
地图测向(测试数据,功能(x){
如果(类别(x)=“数值”){
x[x=(平均值(x,na.rm=T)+3*sd(x,na.rm=T))]=na
}
返回(x)
}
)
如果您希望您的
平均值
sd
计算与
NA
,请将
NA.rm=T
更改为
NA.rm=F


NB:请注意,在这种情况下,没有任何值大于或小于平均值减去或加上三个标准偏差。如果您认为列
Trait1
中的
1000
是您的“可疑”点,请重新考虑,因为它不大于
mean+3*sd
。我建议在不同的数据集上进行测试。

对于这类事情,我一直在使用
base::ifelse()
,并结合使用:

库(tidyverse)
图书馆(magrittr)
图书馆(tidylog)
测试数据%%
#当(且仅当)变量为数值时对其进行变异。。。
如果(是数字,
#…然后,如果它符合以下标准。。。
~z~如果还有其他人(
测试=.x>平均值(.x,na.rm=TRUE)+3*sd(.x,na.rm=TRUE)|
.x<平均值(.x,na.rm=TRUE)-3*sd(.x,na.rm=TRUE)|
.x%>%为.na,
#…替换为NA。如果没有。。。
是=不适用,
#……照原样走!
否=.x
))
注意上面的lambda函数,使用
~
.x

与Vitali上面所说的相呼应,该代码没有改变虚拟数据中的任何内容。为了确保绝对可靠,我加载了
tidylog
,这是一个整洁的包,每当运行tidyverse函数时,它都会打印数据帧更改


编辑:感谢Vitali指出原始代码无法推广。我还去除了很多绒毛。

如果需要使用循环,以下方法应该可以:

for (i in colnames(data)){
  if (class(data[,i]) == "numeric"){
    m = mean(data[,i], na.rm=TRUE)
    sd = sd(data[,i], na.rm=TRUE)
    for (j in 1:nrow(data)){
      if (is.na(data[j,i])==F&(data[j,i] > (m + 3*sd)|data[j,i] < (m - 3*sd))){
        data[j,i] <- NA
      }
    }
  }
}
for(i在colnames(数据)中){
if(类(数据[,i])=“数值”){
m=平均值(数据[,i],na.rm=真)
sd=sd(数据[,i],na.rm=TRUE)
适用于(j/1:nrow(数据)){
如果(is.na(data[j,i])==F&(data[j,i]>(m+3*sd)| data[j,i]<(m-3*sd))){

数据[j,i]不幸的是,您的代码在将来是不可推广的(而且是未知的)数据集,因为对于未知数据,您不会知道
Trait2
不是
numeric
scale
,这是关键。@LateMail-您是对的,值得一提。已编辑的.Base R翻译为:
nums 3]我非常喜欢这个解决方案!您的示例值中没有一个使用该标准替换据我所知。
library(tidyverse)
library(magrittr)
library(tidylog)

test_data %<>%

  # Mutate any variable if (and only if) it's numeric...
  mutate_if(is.numeric,

            # ...then, if it meets the following criteria...
            ~ ifelse(
              test = .x > mean(.x, na.rm = TRUE) + 3 * sd(.x, na.rm = TRUE) |
                     .x < mean(.x, na.rm = TRUE) - 3 * sd(.x, na.rm = TRUE) |
                     .x %>% is.na,

              # ...replace with NA. If it doesn't...
              yes = NA,

              # ...leave as is!
              no  = .x

            ))

for (i in colnames(data)){
  if (class(data[,i]) == "numeric"){
    m = mean(data[,i], na.rm=TRUE)
    sd = sd(data[,i], na.rm=TRUE)
    for (j in 1:nrow(data)){
      if (is.na(data[j,i])==F&(data[j,i] > (m + 3*sd)|data[j,i] < (m - 3*sd))){
        data[j,i] <- NA
      }
    }
  }
}