Thay đổi lớp từ hệ số thành số của nhiều cột trong khung dữ liệu


82

Cách nhanh nhất / tốt nhất để thay đổi một số lượng lớn cột thành số từ thừa số là gì?

Tôi đã sử dụng mã sau nhưng có vẻ như nó đã sắp xếp lại dữ liệu của tôi.

> 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*

Cách tốt nhất là gì, ngắn gọn là đặt tên cho mọi cột như trong:

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

4
Không có bất kỳ giải pháp chung chung nào ?. Một số giải pháp được đề xuất ở đây chỉ hoạt động với các yếu tố, các công việc khác luôn ngoại trừ các yếu tố, v.v.
skan

Câu trả lời:


56

Ngoài câu trả lời của Ramnath, hành vi bạn đang gặp phải là do as.numeric(x)trả về biểu diễn số bên trong của yếu tố xở mức R. Nếu bạn muốn bảo toàn các số là cấp của nhân tố (chứ không phải là đại diện bên trong của chúng), trước tiên bạn cần phải chuyển đổi thành ký tự thông qua as.character()theo ví dụ của Ramnath.

forVòng lặp của bạn cũng hợp lý như một applycuộc gọi và có thể dễ đọc hơn một chút so với mục đích của mã là gì. Chỉ cần thay đổi dòng này:

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

đọc

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

Đây là Câu hỏi thường gặp 7.10 trong Câu hỏi thường gặp về R.

HTH


2
Không cần bất kỳ loại vòng lặp nào. Chỉ cần sử dụng các chỉ số và unlist (). Chỉnh sửa: Tôi đã thêm một câu trả lời minh họa điều này.
Joris Meys

Cách tiếp cận này chỉ hoạt động trong trường hợp cụ thể này. Tôi đã cố gắng sử dụng nó để chuyển đổi các cột thành factorvà nó không hoạt động. sapplyhoặc mutate_ifdường như là các giải pháp áp dụng chung hơn.
Leo

@Leo Care để mở rộng, vì tôi biết thực tế là điều này hoạt động. Đó chính xác là giải pháp giống như Ramnath bên dưới ngoại trừ việc anh ta sử dụng applyđể chạy vòng lặp và OP đang sử dụng một forvòng lặp rõ ràng. Trên thực tế, tất cả các câu trả lời được bình chọn cao đều sử dụng as.numeric(as.character())thành ngữ.
Gavin Simpson

Có, nó hoạt động để thay đổi lớp của nhiều cột thành numeric, nhưng nó không hoạt động ngược lại (để thay đổi lớp của nhiều cột thành factor). Nếu bạn sử dụng các chỉ mục bạn cần unlist()và khi áp dụng cho các cột có ký tự, nó không giới hạn từng ký tự, điều này khiến nó không hoạt động nữa khi đưa đầu ra trở lại stats[,i]. Kiểm tra câu trả lời tại đây: stackoverflow.com/questions/45713473/…
Leo

@Leo tất nhiên nó không hoạt động ngược lại! Điều gì đã cho bạn ấn tượng rằng nó sẽ? Nó chưa bao giờ được thiết kế và OP không bao giờ yêu cầu điều đó. Khó trả lời những câu hỏi không được hỏi. Nếu bạn muốn chuyển đổi sang sử dụng hệ số as.factor()thay thế as.numeric(as.character())ở đây và nó sẽ hoạt động tốt. Tất nhiên, nếu bạn có một kết hợp các cột, bạn sẽ cần phải chọn imột cách có chọn lọc, nhưng điều đó cũng không quan trọng.
Gavin Simpson

73

Bạn phải cẩn thận trong khi thay đổi các yếu tố thành số. Đây là một dòng mã sẽ thay đổi một tập hợp các cột từ hệ số thành số. Tôi giả sử ở đây rằng các cột được thay đổi thành số lần lượt là 1, 3, 4 và 5. Bạn có thể thay đổi nó cho phù hợp

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

3
Điều này sẽ không hoạt động chính xác. Ví dụ: x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x). Tôi không nghĩ rằng điều đó applylà phù hợp với loại vấn đề này.
Marek

1
áp dụng hoạt động hoàn hảo trong những tình huống này. lỗi trong mã của tôi đã sử dụng margin = 1, thay vì 2 vì hàm cần được áp dụng cột khôn ngoan. tôi đã chỉnh sửa câu trả lời của mình cho phù hợp.
Ramnath

Bây giờ nó hoạt động. Nhưng tôi nghĩ rằng nó có thể được thực hiện mà không cần apply. Kiểm tra chỉnh sửa của tôi.
Marek

2
... hoặc Joris trả lời với unlist. Và as.characterchuyển đổi trong giải pháp của bạn là không cần thiết vì các applychuyển đổi df[,cols]thành characternhư vậy apply(df[,cols], 2, function(x) as.numeric(x))cũng sẽ hoạt động.
Marek

@ Ramnath , tại sao bạn sử dụng =? Tại sao không <-?
kittygirl

40

Điều này có thể được thực hiện trong một dòng, không cần vòng lặp, có thể là vòng lặp for hoặc vòng lặp áp dụng. Sử dụng unlist () để thay thế:

# 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)

Chỉnh sửa: đối với mã của bạn, điều này trở thành:

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

Rõ ràng, nếu bạn có khung dữ liệu một cột và bạn không muốn việc giảm thứ nguyên tự động của R để chuyển nó thành vectơ, bạn sẽ phải thêm drop=FALSEđối số.


1
Cải tiến nhỏ có thể là cài đặt recursiveuse.namescác thông số của unlistcả hai FALSE.
Marek

@Marek: đúng. Tôi thích trò chơi này :-)
Joris Meys 27/09/10

Tôi sẽ bổ sung cho những người đang tìm kiếm câu trả lời trong tương lai, điều này không tương đương với phương pháp của op + gavin nếu khung dữ liệu chỉ có một cột. Nó sẽ chuyển đổi thành một vectơ trong trường hợp đó, trong khi op vẫn sẽ là một khung dữ liệu.
themartinmcfly

1
đối với những người làm việc với ngăn nắp: thật thú vị, điều này dường như không hoạt động khi đối tượng cũng là một mảnh vụn: Mã lỗi sauDf <- tibble::as_tibble(Df)
Tjebo

1
@Tjebo với các bản cập nhật của tibble và sự chuyển hướng giữa các tibble và khung dữ liệu, cách tiếp cận cũ này thực sự không phải là lựa chọn tốt nhất trong ngăn nắp. Tốt hơn bạn nên sử dụng các chức năng tidyselect kết hợp với mutate_if. Hoặc bất kỳ cách tiếp cận mới nào được cung cấp trong lần lặp lại tiếp theo của dplyr...
Joris Meys

30

Tôi biết câu hỏi này đã được giải quyết từ lâu, nhưng gần đây tôi đã gặp vấn đề tương tự và nghĩ rằng tôi đã tìm thấy một giải pháp thanh lịch và chức năng hơn một chút, mặc dù nó yêu cầu gói magrittr.

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

Các %<>%đường ống điều hành chỉ định lại, điều này rất hữu ích để giữ cho việc dọn dẹp và chuyển đổi dữ liệu đơn giản. Bây giờ, chức năng áp dụng danh sách dễ đọc hơn nhiều, bằng cách chỉ xác định chức năng bạn muốn áp dụng.


2
giải pháp gọn gàng. bạn quên một khung nhưng tôi không thể làm cho chỉnh sửa này bởi vì nó quá ngắn:df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
epo3

1
Tôi không nghĩ rằng bạn thậm chí cần phải bọc nó trong những df[,cols] %<>% as.numeric(as.character(.))hoạt động vui vẻ như vậy
Nate

khi tôi thử lệnh này tôi nhận được lỗi sauError 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

Mã là như sau:cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
Urvah Shabbir

Đã thêm dấu ngoặc.
Joe

9

Dưới đây là một số dplyrtùy chọn:

# 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

Tôi nghĩ rằng ucfagls đã tìm thấy lý do tại sao vòng lặp của bạn không hoạt động.

Trong trường hợp bạn vẫn không muốn sử dụng vòng lặp, đây là giải pháp với lapply:

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

Biên tập. Tôi tìm thấy giải pháp đơn giản hơn. Có vẻ như as.matrixchuyển đổi thành nhân vật. Vì thế

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

nên làm những gì bạn muốn.


5

lapply được thiết kế khá nhiều cho việc này

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

Xin chào @transcom và chào mừng bạn đến với stackoverflow. Lưu ý rằng câu hỏi này là về việc chuyển đổi sang biểu diễn số từ một nhân tố, chứ không phải ngược lại. Xem giải pháp của Marek.
Aaron rời khỏi Stack Overflow

@Aaron, đã hiểu. Tôi đã đăng câu trả lời này do tiêu đề của OP không rõ ràng, hoạt động dưới giả định rằng những người khác có thể đến đây để tìm cách chuyển đổi nhiều cột một cách dễ dàng, bất kể lớp. Dù sao, tôi đã chỉnh sửa câu trả lời của mình để giải quyết câu hỏi một cách thích hợp hơn :)
transcom 18/02

2

Tôi đã tìm thấy hàm này trên một vài chủ đề trùng lặp khác và đã tìm thấy nó là một cách đơn giản và chung chung để giải quyết vấn đề này. Chủ đề này hiển thị đầu tiên trên hầu hết các tìm kiếm về chủ đề này, vì vậy tôi chia sẻ nó ở đây để tiết kiệm thời gian cho mọi người. Tôi không có tín dụng cho điều này chỉ vì vậy hãy xem các bài viết gốc ở đâyở đây để biết chi tiết.

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

Tôi muốn chỉ ra rằng nếu bạn có NA trong bất kỳ cột nào, chỉ cần sử dụng subscript sẽ không hiệu quả. Nếu có NA trong phần tử, bạn phải sử dụng tập lệnh áp dụng do Ramnath cung cấp.

Ví dụ

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)]))

Trả về như sau:

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

Nhưng:

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

Lợi nhuận:

> 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

bạn có thể sử dụng unfactor()hàm từ CRAN dạng gói "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

Tôi thích mã này vì nó khá tiện dụng:

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

Nó không phải là chính xác những gì được yêu cầu (chuyển đổi sang số), nhưng trong nhiều trường hợp thậm chí còn thích hợp hơn.


1

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

Tôi đã thử cách này để thay đổi một loại cột và tôi nghĩ nó tốt hơn nhiều phiên bản khác, nếu bạn không thay đổi tất cả các loại cột

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

cho ngược lại.


0

Tôi đã gặp sự cố khi chuyển đổi tất cả các cột thành số với một apply()lệnh gọi:

apply(data, 2, as.numeric)

Vấn đề hóa ra là do một số chuỗi có dấu phẩy trong đó - ví dụ: "1,024,63" thay vì "1024,63" - và R không thích cách định dạng số này. Vì vậy, tôi đã loại bỏ chúng và sau đó chạy as.numeric():

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

Lưu ý rằng điều này yêu cầu gói stringr được tải.


0

Đó là những gì đã làm việc cho tôi. Các apply()cố gắng chức năng để ép buộc df để ma trận và nó sẽ trả về NA của.

numeric.df <- as.data.frame(sapply(df, 2, as.numeric))


0

Dựa trên câu trả lời của @ SDahm, đây là giải pháp "tối ưu" cho tôi tibble:

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

Điều này yêu cầu dplyrmagrittr.


0

Tôi đã thử một loạt những điều này cho một vấn đề tương tự và tiếp tục nhận được NAs. Cơ sở R có một số hành vi ép buộc thực sự khó chịu, thường được sửa trong gói Tidyverse. Tôi đã từng tránh chúng vì tôi không muốn tạo ra sự phụ thuộc, nhưng chúng làm cho cuộc sống dễ dàng hơn rất nhiều mà bây giờ tôi thậm chí không buồn cố gắng tìm ra giải pháp Cơ sở R hầu hết thời gian.

Đây là giải pháp Tidyverse, cực kỳ đơn giản và thanh lịch:

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)

Hầu hết các câu trả lời (ít nhất là tất cả các câu trả lời hàng đầu) đảm bảo thực hiện as.numeric(as.character())chuyển đổi để tránh chuyển đổi quá phổ biến của cấp số nguyên thay vì giá trị thành số. Tôi rất vui lòng tán thành câu trả lời này nếu bạn hiển thị tùy chọn đó.
Gregor Thomas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.