【问题标题】:Replace <NA> in a factor column替换因子列中的 <NA>
【发布时间】:2016-12-31 18:42:27
【问题描述】:

我想用有效值替换因子列中的&lt;NA&gt; 值。但我找不到办法。此示例仅用于演示。原始数据来自我必须处理的一个外部 csv 文件。

df <- data.frame(a=sample(0:10, size=10, replace=TRUE),
                 b=sample(20:30, size=10, replace=TRUE))
df[df$a==0,'a'] <- NA
df$a <- as.factor(df$a)

可能是这样的

      a  b
1     1 29
2     2 23
3     3 23
4     3 22
5     4 28
6  <NA> 24
7     2 21
8     4 25
9  <NA> 29
10    3 24

现在我想用数字替换 &lt;NA&gt; 值。

df[is.na(df$a), 'a'] <- 88
In `[<-.factor`(`*tmp*`, iseq, value = c(88, 88)) :
  invalid factor level, NA generated

我想我错过了关于因子的基本 R 概念。我是吗? 我不明白为什么它不起作用。我认为invalid factor level 意味着88 不是该因素的有效水平,对吧?所以我必须告诉因子列还有另一个层次?

【问题讨论】:

  • 我不明白为什么你有这行代码,df$a
  • @buhtz:如果在data.frame 调用中没有对0 的值进行采样,将无法复制您的问题,也许更好的是set.seed()
  • @000andy8484 感谢您的提示。下次我会把它钉在我的笔记上。
  • @user1945827 这只是为了模仿我的真实数据(来自外国csv文件的commin)和真实情况,并提供一个最小的例子。
  • 我建议这个因素是一个红鲱鱼。当您使用 read.csv() 函数导入数据时,您需要设置 stringsAsFactors=F,这将删除结果 data.frame 中的任何因素。

标签: r dataframe replace na


【解决方案1】:

因子变量的基本概念是它只能取特定值,即levels。不在levels 中的值无效。

你有两种可能:

如果您有一个遵循此概念的变量,请确保在创建它时定义所有级别,即使是那些没有相应值的级别。

或者将变量设为字符变量并使用它。

PS:这些问题通常是由数据导入引起的。例如,您在此处显示的内容看起来应该是数字变量而不是因子变量。

【讨论】:

  • 很难决定把绿色标记放在哪里! ;) 您的回答为我提供了有关我之前错过的基本概念的背景信息。非常感谢。
【解决方案2】:

1) addNA 如果fac 是一个因子addNA(fac) 是相同的因子,但将NA 添加为一个级别。见?addNA

强制 NA 级别为 88:

facna <- addNA(fac)
levels(facna) <- c(levels(fac), 88)

给予:

> facna
 [1] 1  2  3  3  4  88 2  4  88 3 
Levels: 1 2 3 4 88

1a) 这可以写在一行中,如下所示:

`levels<-`(addNA(fac), c(levels(fac), 88))

2) 因素 也可以使用factor 的各种参数在一行中完成,如下所示:

factor(fac, levels = levels(addNA(fac)), labels = c(levels(fac), 88), exclude = NULL)

2a) 或等效:

factor(fac, levels = c(levels(fac), NA), labels = c(levels(fac), 88), exclude = NULL)

3) ifelse 另一种方法是:

factor(ifelse(is.na(fac), 88, paste(fac)), levels = c(levels(fac), 88))

4) forcats forcats 包有一个功能:

library(forcats)

fct_explicit_na(fac, "88")
## [1] 1  2  3  3  4  88 2  4  88 3 
## Levels: 1 2 3 4 88

注意:我们使用以下输入fac

fac <- structure(c(1L, 2L, 3L, 3L, 4L, NA, 2L, 4L, NA, 3L), .Label = c("1", 
"2", "3", "4"), class = "factor")

更新:改进了 (1) 并添加了 (1a)。后来添加(4)。

【讨论】:

  • 嘿 :) 我为 data.frame 中的一列做了 1a。水平出现,但如果我想计算特定条件的平均值,假设上面示例中所有具有水平 NA 的 b 我得到 NaN。我试过mean(df$b[df$a==NA]) 还有str(df) 给了我:Factor w/ 3 levels "1", "2", "3", NA:... 我想我需要的是"1", "2", "3", "NA"right?
  • 选项 3) 对我有用,我可以用管道正确应用它。我在 ifelse 语句中使用和不使用 paste(fac) 进行了测试,并且对我来说都很好。为什么需要包含粘贴的任何具体原因?
  • 这样从头开始重建因子。
【解决方案3】:

问题是NA 不是那个因素的水平:

> levels(df$a)
[1] "2"  "4"  "5"  "9"  "10"

您不能立即更改它,但以下方法可以解决问题:

df$a <- as.numeric(as.character(df$a))
df[is.na(df$a),1] <- 88
df$a <- as.factor(df$a)
> df$a
 [1] 9  88 3  9  5  9  88 8  3  9 
Levels: 3 5 8 9 88
> levels(df$a)
[1] "3"  "5"  "8"  "9"  "88"

【讨论】:

  • df$a &lt;- as.numeric(levels(df$a))[df$a]as.numeric(as.character()) 的一个更有效的变体。
【解决方案4】:

其他方法是:

#check levels
levels(df$a)
#[1] "3"  "4"  "7"  "9"  "10"

#add new factor level. i.e 88 in our example
df$a = factor(df$a, levels=c(levels(df$a), 88))

#convert all NA's to 88
df$a[is.na(df$a)] = 88

#check levels again
levels(df$a)
#[1] "3"  "4"  "7"  "9"  "10" "88"

【讨论】:

    【解决方案5】:

    使用factor 函数,我的方式会有点传统:

    a <- factor(a, 
                exclude = NULL, 
                levels = c(levels(a), NA),
                labels = c(levels(a), "None"))
    

    您可以将“无”替换为您想要的适当替换(例如 0L)

    【讨论】:

    • 我认为这是最简洁的答案,只需一个基本功能即可完成。这应该得到更多的支持。
    【解决方案6】:

    我有类似的问题,我想添加我认为最实用(也最整洁)的解决方案:

    将该列转换为character 列,使用mutate 和一个简单的ifelse-语句将NA 值更改为您想要的因子水平(我选择了“无”),将其转换回factor 列:

    df %>% mutate(
    a = as.character(a),
    a = ifelse(is.na(a), "None", a),
    a = as.factor(a)
    )
    

    干净且无痛,因为当 factor 列中出现 NA 值时,您实际上不必涉足这些值。你绕过了怪异,最终得到了一个干净的 factor 变量。

    此外,针对以下关于多列的评论:您可以将语句包装在一个函数中并使用mutate_if 选择所有因子变量,或者,如果您知道关注的列的名称,mutate_at应用该功能:

    replace_factor_na <- function(x){
      x <- as.character(x)
      x <- if_else(is.na(x), "None", x)
      x <- as.factor(x)
    }
    
    df <- df %>%
      mutate_if(is.factor, replace_factor_na)
    

    【讨论】:

    • 它有效,我认为这是最好的答案。
    • 如何使用 mutate_at 来实现。想象一个人想要为多个列做这件事
    • Moj 的问题是有效的,尤其是对于大型数据集,因此我扩展了我的答案,使其更加灵活并一次性修复多个列。
    猜你喜欢
    • 2021-03-13
    • 2017-12-07
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多