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)
Btibert3
źródło
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

Gavin Simpson
źródło
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)));
Ramnath
źródło
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.

Joris Meys
źródło
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ć.

Dan
źródło
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(.))) 
sbha
źródło
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.

Marek
źródło
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])))
transcom
źródło
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")
Agitator
źródło
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
Elizabeth
źródło
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)
Mehrad Mahmoudian
źródło
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.

SDahm
źródło
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.

huseyn rahimov
źródło
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.

CoderGuy123
źródło
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))

Alina Shabatov
źródło
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.

James Hirschorn
źródło
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)
Aaron Cooley
źródło
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