Zmień klasę z współczynnika na liczbową dla wielu kolumn w ramce danych


82

Jaki jest najszybszy / najlepszy sposób zmiany dużej liczby kolumn na liczbowe ze współczynnika?

Użyłem następującego kodu, ale wygląda na to, że uporządkowałem moje dane.

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

Jaki jest najlepszy sposób, poza nazwaniem każdej kolumny, jak w:

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

4
Czy nie ma żadnego ogólnego rozwiązania? Niektóre z proponowanych tutaj rozwiązań działają tylko z czynnikami, inne działają zawsze poza czynnikami itd.
skan

Odpowiedzi:


56

W nawiązaniu do odpowiedzi Ramnatha, zachowanie, którego doświadczasz, jest spowodowane as.numeric(x)zwróceniem wewnętrznej, liczbowej reprezentacji czynnika xna poziomie R. Jeśli chcesz zachować liczby, które są poziomami współczynnika (a nie ich wewnętrzną reprezentacją), musisz najpierw przekonwertować na znak przez, as.character()jak na przykładzie Ramnatha.

Twoja forpętla jest tak samo rozsądna jak applywywołanie i może być nieco bardziej czytelna, jeśli chodzi o intencję kodu. Po prostu zmień tę linię:

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

czytać

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

To jest FAQ 7.10 w R FAQ.

HTH


2
Nie ma potrzeby stosowania jakiejkolwiek pętli. Po prostu użyj indeksów i unlist (). Edycja: dodałem odpowiedź ilustrującą to.
Joris Meys

To podejście działa tylko w tym konkretnym przypadku. Próbowałem go użyć do konwersji kolumn na factori nie zadziałało. sapplylub mutate_ifwydają się bardziej powszechnie stosowanymi rozwiązaniami.
Leo

@Leo Dbaj o rozwój, bo wiem na pewno, że to działa. Jest to dokładnie to samo rozwiązanie, co Ramnath poniżej, z wyjątkiem tego, że używa go applydo uruchamiania pętli, a OP forjawnie używa pętli. W rzeczywistości wszystkie wysoko ocenione odpowiedzi używają as.numeric(as.character())idiomu.
Gavin Simpson

Tak, działa zmiana klasy wielu kolumn na numeric, ale nie działa odwrotnie (aby zmienić klasę wielu kolumn na factor). Jeśli używasz indeksów, których potrzebujesz, unlist()i po zastosowaniu do kolumn ze znakami, wyświetla on każdy pojedynczy znak, co sprawia, że ​​nie działa już podczas wstawiania wyniku z powrotem do stats[,i]. Sprawdź odpowiedź tutaj: stackoverflow.com/questions/45713473/…
Leo

@Leo oczywiście nie działa odwrotnie! Co u licha sprawiało ci wrażenie, że tak się stanie? Nigdy nie został zaprojektowany, a OP nigdy o to nie prosił. Trudno odpowiedzieć na pytania, które nie zostały zadane. Jeśli chcesz przekonwertować na czynnik, użyj as.factor()zamiast as.numeric(as.character())tutaj i będzie działać dobrze. Oczywiście, jeśli masz mieszankę kolumn, musisz wybierać iselektywnie, ale to również trywialne.
Gavin Simpson

73

Trzeba uważać przy zmianie czynników na numeryczne. Oto wiersz kodu, który zmieni zestaw kolumn ze współczynnika na liczbowy. Zakładam tutaj, że kolumny, które mają zostać zmienione na numeryczne, to odpowiednio 1, 3, 4 i 5. Możesz to odpowiednio zmienić

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

3
To nie zadziała poprawnie. Przykład: x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x). Myślę, że nie applyjest to właściwe dla tego rodzaju problemów.
Marek

1
Apply doskonale sprawdza się w takich sytuacjach. błąd w moim kodzie używał margin = 1, zamiast 2, ponieważ funkcja musi być zastosowana w kolumnach. odpowiednio zredagowałem odpowiedź.
Ramnath

Teraz działa. Ale myślę, że można by to zrobić bez apply. Sprawdź moją zmianę.
Marek

2
... lub Joris odpowiada unlist. A as.characterkonwersja w twoim rozwiązaniu nie jest potrzebna, ponieważ applykonwersja df[,cols]na charactertak apply(df[,cols], 2, function(x) as.numeric(x))też zadziała.
Marek

@ Ramnath , dlaczego używasz =? Dlaczego nie <-?
kittygirl

40

Można to zrobić w jednej linii, nie ma potrzeby tworzenia pętli, czy to pętli for, czy aplikacji. Zamiast tego użyj 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)

Edytuj: dla twojego kodu wygląda to następująco:

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

Oczywiście, jeśli masz ramkę danych z jedną kolumną i nie chcesz, aby automatyczna redukcja wymiaru R przekształciła ją w wektor, musisz dodać drop=FALSEargument.


1
Niewielką poprawą mogłoby być ustawienie recursivei use.namesparametry unlistobu na FALSE.
Marek

@Marek: prawda. Uwielbiam tę grę :-)
Joris Meys

Dodam tylko, że dla tych, którzy szukają odpowiedzi w przyszłości, nie jest to równoważne z metodą op + gavin, jeśli ramka danych ma tylko jedną kolumnę. W takim przypadku zostanie przekonwertowany na wektor, podczas gdy op's nadal będzie ramką danych.
themartinmcfly

1
dla osób pracujących z tidyverse: co ciekawe, nie wydaje się to działać, gdy obiekt jest również tibble: kod nie działa poDf <- tibble::as_tibble(Df)
tjebo

1
@Tjebo z aktualizacjami tibble i możliwością rozdzielania między tibble i ramkami danych, to stare podejście nie jest rzeczywiście najlepszą opcją w tidyverse. Lepiej skorzystaj z funkcji tidyselect w połączeniu z mutate_if. Albo jakiekolwiek nowe podejście zostanie udostępnione w kolejnej iteracji dplyr
Joris Meys

30

Wiem, że to pytanie zostało rozwiązane od dawna, ale ostatnio miałem podobny problem i myślę, że znalazłem nieco bardziej eleganckie i funkcjonalne rozwiązanie, chociaż wymaga pakietu magrittr.

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

Do %<>%rury operatora i przypisuje, co jest bardzo przydatne do czyszczenia danych przechowywanie i transformacja prosty. Teraz lista funkcji zastosowania jest znacznie łatwiejsza do odczytania, określając jedynie funkcję, którą chcesz zastosować.


2
zgrabne rozwiązanie. zapomniałeś o jednej nawiasie, ale nie mogę zrobić tej edycji, ponieważ jest za krótka:df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
epo3

1
Nie sądzę, żebyś nawet musiał zawijać to w lappy df[,cols] %<>% as.numeric(as.character(.))działa tak samo
Nate

kiedy próbuję tego polecenia, 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.
pojawia się

Kod jest taki:cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
Urvah Shabbir

Dodano wspornik.
Joe

9

Oto kilka dplyropcji:

# 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

Myślę, że ucfagls odkrył, dlaczego twoja pętla nie działa.

Jeśli nadal nie chcesz używać pętli, oto rozwiązanie z lapply:

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

Edytować. Znalazłem prostsze rozwiązanie. Wygląda na to, że as.matrixprzekształca się w charakter. Więc

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

powinieneś robić, co chcesz.


5

lapply jest właściwie do tego przeznaczony

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

Cześć @transcom i witaj w stackoverflow. Zauważ, że to pytanie dotyczy konwersji do reprezentacji liczbowej z czynnika, a nie odwrotnie. Zobacz rozwiązanie Marka.
Aaron opuścił Stack Overflow

@Aaron, zrozumiałem. Opublikowałem tę odpowiedź ze względu na niejednoznaczność tytułu PO, działając przy założeniu, że inni mogą tu wylądować, szukając sposobu na łatwą konwersję wielu kolumn, niezależnie od klasy. W każdym razie zredagowałem swoją odpowiedź, aby lepiej odpowiedzieć na pytanie :)
transcom,

2

Znalazłem tę funkcję w kilku innych zduplikowanych wątkach i uznałem, że jest to elegancki i ogólny sposób rozwiązania tego problemu. Ten wątek pojawia się jako pierwszy w większości wyszukiwań na ten temat, więc udostępniam go tutaj, aby zaoszczędzić ludziom trochę czasu. Nie biorę za to uznania, więc zobacz oryginalne posty tutaj i tutaj, aby uzyskać szczegółowe informacje.

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

Chciałbym zwrócić uwagę, że jeśli masz NA w dowolnej kolumnie, po prostu użycie indeksów dolnych nie zadziała. Jeśli czynnik zawiera NA, musisz użyć skryptu aplikacji dostarczonego przez Ramnath.

Na przykład

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

Zwraca następujące dane:

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

Ale:

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

Zwroty:

> 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

możesz użyć unfactor()funkcji z pakietu "varhandle" z CRAN:

library("varhandle")

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

my_iris <- unfactor(my_iris)

1

Podoba mi się ten kod, ponieważ jest bardzo przydatny:

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

Nie jest to dokładnie to, o co proszono (konwersja na numeryczne), ale w wielu przypadkach nawet bardziej odpowiednie.


1

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

Próbowałem w ten sposób zmienić jeden typ kolumny i myślę, że jest lepszy niż wiele innych wersji, jeśli nie zamierzasz zmieniać wszystkich typów kolumn

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

na odwrót.


0

Miałem problemy z konwersją wszystkich kolumn na numeryczne z apply()wywołaniem:

apply(data, 2, as.numeric)

Okazuje się, że problem polega na tym, że niektóre ciągi miały przecinek - np. „1024,63” zamiast „1024,63” - a R nie lubi tego sposobu formatowania liczb. Więc je usunąłem, a potem uruchomiłem as.numeric():

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

Zauważ, że wymaga to załadowania pakietu stringr.


0

To działa dla mnie. Do apply()prób funkcyjnych do zmuszania df aby matryca i zwraca NA użytkownika.

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


0

Opierając się na odpowiedzi @ SDahm, było to „optymalne” rozwiązanie dla moich tibble:

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

To wymaga dplyri magrittr.


0

Wypróbowałem kilka z nich w przypadku podobnego problemu i nadal otrzymywałem NA. Base R ma kilka naprawdę irytujących zachowań przymusu, które są generalnie naprawione w pakietach Tidyverse. Kiedyś ich unikałem, ponieważ nie chciałem tworzyć zależności, ale ułatwiają życie, więc teraz nawet nie kłopoczę się próbą znalezienia rozwiązania Base R.

Oto rozwiązanie Tidyverse, które jest niezwykle proste i eleganckie:

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)

Większość odpowiedzi (przynajmniej wszystkie najlepsze odpowiedzi) zapewnia as.numeric(as.character())konwersję, aby uniknąć zbyt powszechnej konwersji poziomów całkowitych zamiast wartości na liczby. Z radością zagłosowałbym za tą odpowiedzią, jeśli pokażesz tę opcję.
Gregor Thomas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.