【问题标题】:Convert data frame columns to factor with indexing使用索引将数据框列转换为因子
【发布时间】:2017-08-16 12:24:54
【问题描述】:

我将一些结果放入数据框中。我有一些因子列和许多数字列。根据this问题的答案,我可以通过索引轻松地将数字列转换为数字。

#create example data
df = data.frame(replicate(1000,sample(1:10,1000,rep=TRUE)))
df$X1 = LETTERS[df$X1]
df$X2 = LETTERS[df$X2]
df$X3 = LETTERS[df$X3]
df[-1] <- sapply(df[-1], function(x) ifelse(runif(length(x)) < 0.1, NA, x))

#find columns that are factors
factornames = c("X1", "X2", "X3")
factorfilt = names(df) %in% factornames

#convert non-factor columns to numeric
df[, !factorfilt] = as.numeric(as.character(unlist(df[, !factorfilt])))

但是当我想对我的因子列做同样的事情时,我无法让相同的索引工作:

#convert factor columns to factor
df[, factorfilt] = as.factor(as.character(unlist(df[, factorfilt])))
class(df$X1)

[1] "character"

df[, factorfilt] = as.factor(as.character(df[, factorfilt]))
class(df$X1)

[1] "character"

df[, factorfilt] = as.factor(unlist(df[, factorfilt]))
class(df$X1)

[1] "character"

df[, factorfilt] = as.factor(df[, factorfilt]) 

Error in sort.list(y) : 'x' must be atomic for 'sort.list'
Have you called 'sort' on a list?

如果我调用class(df$X1),所有这些都返回"character",而如果我运行df$X1= as.factor(df$X1),它会返回"factor"

为什么当我调用 as.factor 时这种索引方式不起作用,但如果我调用 as.numeric 时会起作用?

【问题讨论】:

  • as.factoras.character 等适用于 vector 而不是 data.frame。您需要遍历列,然后执行factor
  • 根据 akrun 的评论,使用 lapply 遍历选定的列并执行强制转换:df[, factorfilt] &lt;- lapply(df[, factorfilt], as.factor)
  • numeric 是一种原子数据类型,而 factor 是一种将标签映射到整数并具有自己的类的特殊数据类型。 data.frame 有自己的类,尝试将 as.factor(unlist(df[, factorfilt])) 中的值(这是一个因素)重新分配到 data.frame 的多个列中,导致函数在重新分配之前将未列出的向量转换为字符。涉及的功能比较复杂,输入`[&lt;-.data.frame`查看。剥离因子类并转换为字符的行可能是value &lt;- matrix(value, n, p),因为矩阵有自己的类。
  • 一般来说,mutate_if 是将一种类型的所有列转换为另一种类型的好方法。
  • 关于尝试使用因子矩阵的非常详细的回复:stackoverflow.com/questions/28723059/…。从那个答案来看,当您使用matrix(...) 时,它会在构建矩阵之前对数据使用as.vector()。这就是将因子转换为字符的原因(试试class(as.vector(factor(c(1,2,3))))

标签: r dataframe indexing types


【解决方案1】:

您应该观察您正在做的事情的一些行为方面。像你一样定义你的数据:

df = data.frame(replicate(1000,sample(1:10,1000,rep=TRUE)))
df$X1 = LETTERS[df$X1]
df$X2 = LETTERS[df$X2]
df$X3 = LETTERS[df$X3]
df[-1] <- sapply(df[-1], function(x) ifelse(runif(length(x)) < 0.1, NA, x))

factornames = c("X1", "X2", "X3")
factorfilt = names(df) %in% factornames
df[, !factorfilt] = as.numeric(as.character(unlist(df[, !factorfilt])))

现在让我们看看像您一样制作X1X2X3 因子的结果,但我们先不要重新分配它。

test <- as.factor(as.character(df[, factorfilt]))
class(test) # "factor"
length(test) # 3

这里要注意的重要一点是test 不是数据框。它是一个向量,您正试图保存数据框的三列。我认为我们应该质疑将数据帧转换为向量以存储在数据帧中的智慧。

然后考虑你的第二个任务:

test2 <- as.factor(as.character(unlist(df[, factorfilt])))
class(test2) # factor
length(test2) # 3000

同样,这是一个因素,但它的长度与test 完全不同。 R 允许您将其重新分配回 df 是一种善意,并且这样做只是因为它认识到它可以协调维度。但是当您尝试将因子推入X1X2X3 时,关于如何处理因子水平存在一个大问题。所有三个变量都应该具有相同的水平吗?每个变量是否应该仅具有其自身存在的级别? R 没有尝试声明“适当的”选择是什么,而是忽略它并将其转换回一个字符供您自己处理。

以这种方式操作列可能会意外更改类,这是不这样做的一个很好的理由。这在您对NAs 的分配中很明显。让我们重温一下:

df = data.frame(replicate(1000,sample(1:10,1000,rep=TRUE)))
df$X1 = LETTERS[df$X1]
df$X2 = LETTERS[df$X2]
df$X3 = LETTERS[df$X3]

此时,X4X1000 都是integer 类列。当你运行时

df[-1] <- sapply(df[-1], function(x) ifelse(runif(length(x)) < 0.1, NA, x))

它们现在都是characters,您继续将它们转换为numeric。他们甚至不再是原来的班级了。

如果我们使用lapply

df[-1] <- lapply(df[-1], function(x) ifelse(runif(length(x)) < 0.1, NA, x))

原始类被保留,无需将它们转换回数字类。同样,我们可以轻松地将X1X3 转换为具有

df[, factorfilt] <- lapply(df[, factorfilt], as.factor)

作为一般规则,最好将列中的数据作为不同的列进行处理。一旦你开始在多列上分配一个向量,你就进入了一个恶作剧的黑暗世界。

【讨论】:

  • 嗯,我根本不知道这些事情,谢谢。阅读 sapplylapply 似乎它们是一样的?
  • 返回的内容有所不同。 sapply 返回向量或矩阵(在这种特殊情况下,我认为它是一个非常长的向量)。 lapply 返回一个列表,它将保持数据框的列正确分区。
猜你喜欢
  • 1970-01-01
  • 2018-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-16
  • 2016-11-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多