Sınıfı faktörden bir veri çerçevesindeki birçok sütunun sayısal değerine değiştirin


82

Çok sayıda sütunu faktörden sayısal hale getirmenin en hızlı / en iyi yolu nedir?

Aşağıdaki kodu kullandım ancak verilerimi yeniden sipariş etmiş gibi görünüyor.

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

En iyi yol nedir, her sütunu aşağıdaki gibi adlandırmanın kısası:

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

4
Herhangi bir genel çözüm yok mu? Burada önerilen çözümlerden bazıları yalnızca faktörlerle çalışır, diğerleri her zaman faktörlerle dışında çalışır, vb.
skan

Yanıtlar:


56

Ramnath'ın cevabına ek olarak, deneyimlediğiniz davranış as.numeric(x), faktörün xR seviyesindeki içsel, sayısal temsilini döndürmekten kaynaklanıyor . Faktörün seviyeleri olan sayıları korumak istiyorsanız (iç gösterimleri yerine), as.character()Ramnath'ın örneğine göre ilk önce karaktere dönüştürmeniz gerekir .

Kişisel fordöngü sadece kadar makul olduğu applyçağrı ve kod niyeti ne olduğu biraz daha okunabilir olabilir. Sadece bu satırı değiştirin:

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

okumak

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

Bu, R SSS'deki SSS 7.10'dur .

HTH


2
Herhangi bir döngüye gerek yok. Sadece indisleri ve unlist () kullanın. Düzenleme: Bunu gösteren bir cevap ekledim.
Joris Meys

Bu yaklaşım yalnızca bu özel durumda işe yarar. Sütunları dönüştürmek için kullanmayı denedim factorve işe yaramadı. sapplyveya mutate_ifdaha genel olarak uygulanabilir çözümler gibi görünmektedir.
Leo

@Leo Care genişleyecek, çünkü bunun işe yaradığına dair bir gerçek biliyorum. Bu var tam olarak Ramnath kullandığı haricinde altındaysa aynı çözüm applydöngü çalıştırmak için ve OP bir kullanıyordu foraçıkça döngü. Aslında, yüksek oy alan tüm cevaplar as.numeric(as.character())deyimi kullanır .
Gavin Simpson

Evet, birden çok sütunun sınıfını olarak değiştirmeye çalışır numeric, ancak tersine çalışmaz (birden çok sütunun sınıfını olarak değiştirmek için factor). İhtiyaç duyduğunuz indisleri kullanırsanız unlist()ve karakter içeren sütunlara uygulandığında, her bir karakterin listesini çıkarır, bu da çıktıyı geri koyarken artık çalışmamasını sağlar stats[,i]. Cevabı buradan kontrol edin: stackoverflow.com/questions/45713473/…
Leo

@Leo elbette tersine çalışmıyor! Size bunun işe yarayacağı izlenimini veren ne oldu? Asla tasarlanmadı ve OP asla bunu istemedi. Sorulmayan soruları yanıtlamak zor. Eğer dönüştürmek istiyorsanız için bir faktör kullanım as.factor()yerine as.numeric(as.character())burada ve sadece para cezası çalışacağız. Elbette, bir sütun karışımınız varsa i, seçici olarak seçmeniz gerekecek , ancak bu da önemsiz.
Gavin Simpson

73

Faktörleri sayısal olarak değiştirirken dikkatli olmalısınız. İşte bir dizi sütunu faktörden sayısal hale getirecek bir kod satırı. Burada sayısal olarak değiştirilecek sütunların sırasıyla 1, 3, 4 ve 5 olduğunu varsayıyorum. Buna göre değiştirebilirsin

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

3
Bu düzgün çalışmayacak. Örnek: x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x). Bunun applybu tür sorunlara uygun olduğunu düşünmüyorum .
Marek

1
uygulama bu durumlarda mükemmel çalışır. Kodumdaki hata, fonksiyonun sütun olarak uygulanması gerektiğinden 2 yerine margin = 1 kullanıyordu. cevabımı buna göre düzenledim.
Ramnath

Şimdi çalışıyor. Ama bence onsuz da yapılabilir apply. Düzenlememi kontrol et.
Marek

2
... veya Joris ile cevap ver unlist. Ve as.charactersenin çözümde dönüşüm neden gerekli değildir applydönüştürür df[,cols]için charactero kadar apply(df[,cols], 2, function(x) as.numeric(x))çok çalışacaktır.
Marek

@ Ramnath , neden kullanıyorsun =? Neden olmasın <-?
kittygirl

40

Bu tek satırda yapılabilir, ister for-loop ister uygulama olsun, bir döngüye gerek yoktur. Bunun yerine unlist () kullanın:

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

Düzenleme: kodunuz için bu şu olur:

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

Açıkçası, tek sütunlu bir veri çerçeveniz varsa ve R'nin otomatik boyut indirgemesinin onu bir vektöre dönüştürmesini istemiyorsanız, drop=FALSEargümanı eklemeniz gerekir .


1
Küçük iyileştirme , her ikisinin de ayarlanması recursiveve use.namesparametreleri olabilir . unlistFALSE
Marek

@Marek: doğru. Bu oyunu seviyorum :-)
Joris Meys

Gelecekte cevap arayanlar için ekleyeceğim, eğer veri çerçevesi tek bir sütundan ibaretse, bu op + gavin'in yöntemine eşdeğer değildir. Bu durumda bir vektöre dönüşecek, oysa op'ler hala bir veri çerçevesi olacaktır.
themartinmcfly

1
tidyverse ile çalışanlar için: ilginç bir şekilde, nesne aynı zamanda bir Df <- tibble::as_tibble(Df)
tibble

1
@Tjebo, tibble güncellemeleri ve tibble ile veri çerçeveleri arasındaki sapma ile, bu eski yaklaşım tidyverse'de gerçekten en iyi seçenek değil. Tidyselect işlevlerini ile birlikte kullansanız iyi olur mutate_if. Ya da bir sonraki yinelemede sunulan yeni yaklaşım ne olursa olsun dplyr...
Joris Meys

30

Bu sorunun uzun süredir çözüldüğünü biliyorum, ancak son zamanlarda benzer bir sorunla karşılaştım ve magrittr paketini gerektirmesine rağmen biraz daha şık ve işlevsel bir çözüm bulduğumu düşünüyorum.

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

%<>%Operatör boru ve tutma veri temizleme ve dönüştürme basit için çok yararlıdır yeniden atar. Artık liste uygulama işlevini okumak, yalnızca uygulamak istediğiniz işlevi belirterek çok daha kolay.


2
temiz çözüm. bir parantezi unuttunuz ama çok kısa olduğu için bu düzenlemeyi df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
yapamıyorum

1
Bunu lappy df[,cols] %<>% as.numeric(as.character(.))işlerinde aynı şekilde sarmalaman gerektiğini bile sanmıyorum
Nate

bu komutu denediğimde şu hatayı alıyorumError 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

Kod cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
şuna

Parantez şimdi eklendi.
Joe

9

İşte bazı dplyrseçenekler:

# 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

Sanırım ucfagls , döngünüzün neden çalışmadığını anladı .

Hala bir döngü kullanmak istemiyorsanız, burada çözüm lapply:

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

Düzenle. Daha basit bir çözüm buldum. as.matrixKaraktere dönüşüyor gibi görünüyor . Yani

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

ne istersen yapmalısın.


5

lapply bunun için tasarlandı

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

Merhaba @transcom, stackoverflow'a hoş geldiniz. Bu sorunun, tam tersi değil, bir faktörden sayısal gösterime dönüştürmekle ilgili olduğuna dikkat edin. Marek'in çözümüne bakın.
Aaron Stack Overflow'dan

@Aaron, anlaşıldı. Bu cevabı, sınıftan bağımsız olarak birden fazla sütunu kolayca dönüştürmek için başkalarının buraya inebileceği varsayımıyla çalışan OP'nin başlığının belirsizliği nedeniyle gönderdim. Her neyse, soruyu daha uygun bir şekilde ele almak için cevabımı düzenledim :)
transcom

2

Bu işlevi birkaç başka yinelenen iş parçacığında buldum ve bu sorunu çözmenin zarif ve genel bir yolunu buldum. Bu ileti dizisi, bu konudaki çoğu aramada ilk olarak görünüyor, bu yüzden milletlere biraz zaman kazandırmak için burada paylaşıyorum. Bunun için hiçbir kredi almıyorum, bu yüzden ayrıntılar için buradaki ve buradaki orijinal gönderilere bakın.

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

Herhangi bir sütunda NA'lar varsa, sadece abonelikleri kullanmanın işe yaramayacağını belirtmek isterim. Faktörde NA'lar varsa, Ramnath tarafından sağlanan uygulama komut dosyasını kullanmalısınız.

Örneğin

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

Aşağıdakileri döndürür:

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

Fakat:

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

İadeler:

> 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 paketindeki "varhandle" paketindeki işlevi kullanabilirsiniz :

library("varhandle")

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

my_iris <- unfactor(my_iris)

1

Bu kodu beğendim çünkü oldukça kullanışlı:

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

Bu tam olarak istenen şey değildir (nümerik hale çevirmek), ancak çoğu durumda daha da uygundur.


1

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

Bir sütun türünü değiştirmek için bu yolu denedim ve tüm sütun türlerini değiştirmeyecekseniz, diğer birçok sürümden daha iyi olduğunu düşünüyorum.

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

tersi için.


0

Bir apply()çağrı ile tüm sütunları sayısal hale dönüştürmede sorun yaşadım :

apply(data, 2, as.numeric)

Sorun, dizelerin bazılarının virgül içermesinden kaynaklanıyor - örneğin, "1024.63" yerine "1.024.63" ve R, sayıları bu şekilde biçimlendirmekten hoşlanmıyor. Bu yüzden onları kaldırdım ve koştum as.numeric():

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

Bunun stringr paketinin yüklenmesini gerektirdiğini unutmayın.


0

Benim için işe yarayan buydu. apply()Zorlamak için df için fonksiyon çalışır Matris ve NA adlı döner.

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


0

@ SDahm'ın cevabına göre, bu benim için "en uygun" çözümdü tibble:

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

Bu dplyrve gerektirir magrittr.


0

Bunlardan birkaçını benzer bir problem üzerinde denedim ve NA'lar almaya devam ettim. Base R, genellikle Tidyverse paketlerinde sabitlenen bazı gerçekten rahatsız edici zorlama davranışlarına sahiptir. Bağımlılık yaratmak istemediğim için onlardan kaçınırdım, ama onlar hayatı o kadar kolaylaştırıyor ki, artık çoğu zaman Base R çözümünü bulmaya çalışmakla bile uğraşmıyorum.

İşte son derece basit ve zarif olan Tidyverse çözümü:

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)

Yanıtların çoğu (en azından en iyi yanıtların tümü) , değerlerin sayısal olarak değil tam sayı düzeylerinin çok yaygın olarak dönüştürülmesini as.numeric(as.character())önlemek için dönüştürmeyi yaptığınızdan emin olun . Bu seçeneği gösterirseniz, bu yanıtı memnuniyetle yükseltebilirim.
Gregor Thomas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.