将类别从因子更改为数据框中许多列的数值


82

从factor将大量列更改为数字的最快/最佳方法是什么?

我使用了以下代码,但它似乎已重新排序了我的数据。

> head(stats[,1:2])
  rk                 team
1  1 Washington Capitals*
2  2     San Jose Sharks*
3  3  Chicago Blackhawks*
4  4     Phoenix Coyotes*
5  5   New Jersey Devils*
6  6   Vancouver Canucks*

for(i in c(1,3:ncol(stats))) {
    stats[,i] <- as.numeric(stats[,i])
}

> head(stats[,1:2])
  rk                 team
1  2 Washington Capitals*
2 13     San Jose Sharks*
3 24  Chicago Blackhawks*
4 26     Phoenix Coyotes*
5 27   New Jersey Devils*
6 28   Vancouver Canucks*

最好的方法是,不按以下方式命名每列:

df$colname <- as.numeric(ds$colname)

4
没有通用的解决方案吗?这里提出的某些解决方案仅适用于因数,其他适用于除因数外的其他项,依此类推……
skan

Answers:


56

除了Ramnath的答案,您正在遇到的行为是由于as.numeric(x)返回x了R级因子的内部数字表示。如果要保留作为因子级别的数字(而不是其内部表示形式),则需要as.character()按照Ramnath的示例首先转换为字符。

您的for循环与apply调用一样合理,并且对于代码的意图可能更具可读性。只需更改此行:

stats[,i] <- as.numeric(stats[,i])

读书

stats[,i] <- as.numeric(as.character(stats[,i]))

这是R FAQ中的FAQ 7.10

高温超导


2
无需任何循环。只需使用索引和unlist()。编辑:我添加了一个答案来说明这一点。
Joris Meys 2010年

此方法仅在这种特定情况下有效。我试图用它来将列转换为factor,但它没有用。sapplymutate_if似乎是更普遍适用的解决方案。
Leo

@Leo Care扩大了,因为我知道这个有效。它与下面的Ramnath完全相同的解决方案,除了他用于apply运行循环并且OPfor显式使用了循环。实际上,所有极受好评的答案都使用了这种as.numeric(as.character())习语。
加文·辛普森

是的,它可以将多列的类更改为numeric,但不能反向工作(将多列的类更改为factor)。如果使用索引unlist(),则将其应用于带有字符的列时,它会取消列出每个单个字符,这使得将输出放回时不再起作用stats[,i]。在此处查看答案:stackoverflow.com/questions/45713473/…–
Leo

@Leo当然不能反向工作!到底给您的印象是什么?它从未设计过,OP也从未要求过。很难回答未提出的问题。如果要转换一个因素使用as.factor()代替as.numeric(as.character())这里,它会工作得很好。当然,如果您混合使用各列,则需要有选择i地进行选择,但是我也认为这很琐碎。
加文·辛普森

73

将因子更改为数值时,您必须小心。这是一行代码,它将一组列从因数更改为数值。我在这里假设要更改为数字的列分别为1、3、4和5。您可以相应地更改它

cols = c(1, 3, 4, 5);    
df[,cols] = apply(df[,cols], 2, function(x) as.numeric(as.character(x)));

3
这将无法正常工作。范例:x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x)。我认为这apply不适用于此类问题。
Marek 2010年

1
在这种情况下,套用非常完美。我的代码中的错误是使用margin = 1,而不是2,因为该功能需要按列应用。我已经相应地编辑了我的答案。
Ramnath 2010年

现在可以了。但是我认为没有它就可以做到apply。检查我的编辑。
Marek 2010年

2
...或Joris回答unlist。和as.character转换在您的解决方案,不需要原因apply转换df[,cols]character这样apply(df[,cols], 2, function(x) as.numeric(x))也能工作。
Marek 2010年

@Ramnath,为什么使用=?为什么不使用<-
kittygirl

40

这可以一行完成,不需要循环,无论是for循环还是Apply。使用unlist()代替:

# testdata
Df <- data.frame(
  x = as.factor(sample(1:5,30,r=TRUE)),
  y = as.factor(sample(1:5,30,r=TRUE)),
  z = as.factor(sample(1:5,30,r=TRUE)),
  w = as.factor(sample(1:5,30,r=TRUE))
)
##

Df[,c("y","w")] <- as.numeric(as.character(unlist(Df[,c("y","w")])))

str(Df)

编辑:对于您的代码,它变为:

id <- c(1,3:ncol(stats))) 
stats[,id] <- as.numeric(as.character(unlist(stats[,id])))

显然,如果您有一个单列数据框,并且不希望R的自动降维将其转换为向量,则必须添加该drop=FALSE参数。


1
小的改进,可以设置recursiveuse.names参数unlist都来FALSE
Marek

@Marek:是的。我喜欢这个游戏:-)
Joris Meys 2010年

我只是为将来寻找答案的人添加内容,如果数据框只有一列,这不等于op + gavin的方法。在这种情况下,它将转换为向量,而op仍将是一个数据帧。
themartinmcfly

1
对于使用tidyverse的用户:有趣的是,当对象也是小标题时,这似乎也不起作用:代码在Df <- tibble::as_tibble(Df)
Tjebo

1
@Tjebo具有更新的小标题以及小标题和数据帧之间的转移,这种旧方法的确不是tidyverse中的最佳选择。您最好将tidyselect函数与结合使用mutate_if。还是在下一次迭代中提供了新的方法dplyr
Joris Meys

30

我知道这个问题已经解决了很长时间,但是最近我遇到了一个类似的问题,尽管我需要magrittr软件包,但我认为我找到了一个更优雅,更实用的解决方案。

library(magrittr)
cols = c(1, 3, 4, 5)
df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))

%<>%运营商的管道重新分配,这是保持数据清洗和转换简单是非常有用的。现在,仅通过指定您希望应用的功能,列表应用功能就更容易阅读。


2
简洁的解决方案。您忘记了一个括号,但由于太短而无法进行编辑:df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
epo3

1
我不认为你甚至需要包装,在lappy上df[,cols] %<>% as.numeric(as.character(.))的工作方式相同
奈特

当我尝试此命令时,出现以下错误Error in [.data.table(Results, , cols) : j (the 2nd argument inside [...]) is a single symbol but column name 'cols' is not found. Perhaps you intended DT[,..cols] or DT[,cols,with=FALSE]. This difference to data.frame is deliberate and explained in FAQ 1.1.
Urvah Shabbir 17-10-11

代码如下:cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
Urvah Shabbir

托架现已添加。

9

以下是一些dplyr选项:

# by column type:
df %>% 
  mutate_if(is.factor, ~as.numeric(as.character(.)))

# by specific columns:
df %>% 
  mutate_at(vars(x, y, z), ~as.numeric(as.character(.))) 

# all columns:
df %>% 
  mutate_all(~as.numeric(as.character(.))) 

6

我认为ucfagls发现了您的循环无法正常工作的原因。

如果您仍然不想使用循环,这里是解决方案lapply

factorToNumeric <- function(f) as.numeric(levels(f))[as.integer(f)] 
cols <- c(1, 3:ncol(stats))
stats[cols] <- lapply(stats[cols], factorToNumeric)

编辑。我找到了更简单的解决方案。似乎as.matrix转化为性格。所以

stats[cols] <- as.numeric(as.matrix(stats[cols]))

应该做你想做的。


5

lapply就是为此而设计的

unfactorize<-c("colA","colB")
df[,unfactorize]<-lapply(unfactorize, function(x) as.numeric(as.character(df[,x])))

嗨@transcom,欢迎来到stackoverflow。请注意,这个问题是关于从一个因子转换成数字表示形式的,而不是相反的。请参阅Marek的解决方案。
亚伦(Aaron)

@亚伦,了解。由于OP的标题含糊不清,因此我发布了此答案,其假设是其他人可能会在这里寻找,以寻求一种轻松转换多列的方法,而无需考虑类别。无论如何,我已经编辑了答案以更恰当地解决这个问题:)
transcom 2014年

2

我在其他几个重复的线程上发现了此函数,并发现它是解决此问题的一种优雅而通用的方法。该主题首先出现在该主题的大多数搜索中,所以我在这里分享它是为了节省人们一些时间。我对此一无所获,因此请查看此处此处的原始帖子以获取详细信息。

df <- data.frame(x = 1:10,
                 y = rep(1:2, 5),
                 k = rnorm(10, 5,2),
                 z = rep(c(2010, 2012, 2011, 2010, 1999), 2),
                 j = c(rep(c("a", "b", "c"), 3), "d"))

convert.magic <- function(obj, type){
  FUN1 <- switch(type,
                 character = as.character,
                 numeric = as.numeric,
                 factor = as.factor)
  out <- lapply(obj, FUN1)
  as.data.frame(out)
}

str(df)
str(convert.magic(df, "character"))
str(convert.magic(df, "factor"))
df[, c("x", "y")] <- convert.magic(df[, c("x", "y")], "factor")

1

我想指出的是,如果您在任何列中都包含NA,则仅使用下标将不起作用。如果因素中包含NA,则必须使用Ramnath提供的apply脚本。

例如

Df <- data.frame(
  x = c(NA,as.factor(sample(1:5,30,r=T))),
  y = c(NA,as.factor(sample(1:5,30,r=T))),
  z = c(NA,as.factor(sample(1:5,30,r=T))),
  w = c(NA,as.factor(sample(1:5,30,r=T)))
)

Df[,c(1:4)] <- as.numeric(as.character(Df[,c(1:4)]))

返回以下内容:

Warning message:
NAs introduced by coercion 

    > head(Df)
       x  y  z  w
    1 NA NA NA NA
    2 NA NA NA NA
    3 NA NA NA NA
    4 NA NA NA NA
    5 NA NA NA NA
    6 NA NA NA NA

但:

Df[,c(1:4)]= apply(Df[,c(1:4)], 2, function(x) as.numeric(as.character(x)))

返回值:

> head(Df)
   x  y  z  w
1 NA NA NA NA
2  2  3  4  1
3  1  5  3  4
4  2  3  4  1
5  5  3  5  5
6  4  2  4  4

1

您可以使用unfactor()CRAN形式的“ varhandle”包中的函数:

library("varhandle")

my_iris <- data.frame(Sepal.Length = factor(iris$Sepal.Length),
                      sample_id = factor(1:nrow(iris)))

my_iris <- unfactor(my_iris)

1

我喜欢这段代码,因为它非常方便:

  data[] <- lapply(data, function(x) type.convert(as.character(x), as.is = TRUE)) #change all vars to their best fitting data type

它不完全是要求的(转换为数字),但在许多情况下甚至更合适。


1

df$colname <- as.numeric(df$colname)

我尝试过这种方式来更改一种列类型,如果您不打算更改所有列类型,我认为它比许多其他版本要好

df$colname <- as.character(df$colname)

反之亦然。


0

我在通过apply()调用将所有列转换为数字时遇到问题:

apply(data, 2, as.numeric)

问题出在这是因为某些字符串中包含逗号(例如,“ 1,024.63”而不是“ 1024.63”),并且R不喜欢这种格式化数字的方式。所以我删除了它们然后运行as.numeric()

data = as.data.frame(apply(data, 2, function(x) {
  y = str_replace_all(x, ",", "") #remove commas
  return(as.numeric(y)) #then convert
}))

请注意,这需要加载stringer程序包。



0

根据@SDahm的回答,这是我的“最佳”解决方案tibble

data %<>% lapply(type.convert) %>% as.data.table()

这需要dplyrmagrittr


0

我在类似的问题上尝试了一堆,并不断获得NA。Base R具有一些真正令人讨厌的强制行为,通常在Tidyverse程序包中已解决。我曾经避免使用它们,因为我不想创建依赖关系,但是它们使生活变得如此轻松,以至于现在我什至不必在大多数时间试图弄清楚Base R解决方案。

这是Tidyverse解决方案,它非常简单而优雅:

library(purrr)

mydf <- data.frame(
  x1 = factor(c(3, 5, 4, 2, 1)),
  x2 = factor(c("A", "C", "B", "D", "E")),
  x3 = c(10, 8, 6, 4, 2))

map_df(mydf, as.numeric)

大多数答案(至少是所有最重要的答案)都确保进行as.numeric(as.character())转换,以避免整数级(而不是数值)的通用转换。如果您显示该选项,我会很乐意赞成。
格雷戈尔·托马斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.