Alt kümelenmiş bir veri çerçevesinde düşme faktörü düzeyleri


543

Bir içeren bir veri çerçevesi var factor. Bu subsetveri çerçevesinin bir alt kümesini veya başka bir dizin oluşturma işlevini oluşturduğumda, yeni bir veri çerçevesi oluşturulur. Ancak factordeğişken, yeni veri çerçevesinde bulunmasalar bile, orijinal düzeylerinin tümünü korur.

Bu, yönlü çizim yaparken veya faktör düzeylerine dayanan işlevleri kullanırken sorunlara neden olur.

Yeni veri çerçevesindeki bir faktörden seviyeleri kaldırmanın en özlü yolu nedir?

İşte bir örnek:

df <- data.frame(letters=letters[1:5],
                    numbers=seq(1:5))

levels(df$letters)
## [1] "a" "b" "c" "d" "e"

subdf <- subset(df, numbers <= 3)
##   letters numbers
## 1       a       1
## 2       b       2
## 3       c       3    

# all levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"

Yanıtlar:


420

Yapmanız gereken tek şey, alt ayardan sonra değişkeninize tekrar factor () uygulamaktır:

> subdf$letters
[1] a b c
Levels: a b c d e
subdf$letters <- factor(subdf$letters)
> subdf$letters
[1] a b c
Levels: a b c

DÜZENLE

Faktör sayfası örneğinden:

factor(ff)      # drops the levels that do not occur

Bir veri çerçevesindeki tüm faktör sütunlarından düzey bırakmak için şunları kullanabilirsiniz:

subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)

22
Bir kereye mahsus için bu iyi, ancak çok sayıda sütuna sahip bir data.frame'de bunu bir faktör olan her sütunda yapabilirsiniz ... drop.levels () gibi bir işleve ihtiyaç duyulmasına neden olur gdata'dan.
Dirk Eddelbuettel

6
Anlıyorum ... ama kullanıcı açısından bakıldığında subdf [] <- lapply (subdf, function (x) if (is.factor (x)) factor (x) else x) gibi bir şey yazmak hızlı ... drop.levels () büyük veri kümeleri ile çok daha verimli bir hesaplama veya daha iyi? (Sanırım, yukarıdaki satırı büyük bir veri çerçevesi için bir for-
loop'ta

1
Teşekkürler Stephen & Dirk - Bunu bir faktörün mağaraları için yaşasın veriyorum, ancak umarım millet faktörlerin tüm veri çerçevesini temizleme hakkındaki önerileriniz için bu yorumları okuyacaktır.
medriscoll

9
Yan etki olarak, işlev veri çerçevesini bir listeye dönüştürür, bu nedenle mydf <- droplevels(mydf)Roman Luštrik ve Tommy O'Dell tarafından önerilen çözüm tercih edilir.
Johan

1
Ayrıca: Bu yöntem yapar değişkenin sıralamasını korumanız.
webelo

492

R sürüm 2.12'den beri bir droplevels()işlev var.

levels(droplevels(subdf$letters))

7
Bu yöntemin kullanımına göre bir avantajı factor(), orijinal veri çerçevesini değiştirmenin veya yeni bir kalıcı veri çerçevesi oluşturmanın gerekli olmamasıdır. droplevelsBir alt küme veri çerçevesi etrafında sarın ve bir kafes işlevi için veri bağımsız değişkeni olarak kullanabilirsiniz ve gruplar doğru ele alınacaktır.
Mars

Faktörümde bir NA seviyem varsa (gerçek bir NA seviyesi), NA'lar mevcut olsa bile, düşülen seviyeler tarafından düştüğünü fark ettim.
Meep

46

Bu davranışı istemiyorsanız, faktörleri kullanmayın, bunun yerine karakter vektörlerini kullanın. Bence bu daha sonra işleri düzeltmekten daha mantıklı. Verilerinizi read.tableveya ile yüklemeden önce aşağıdakileri deneyin read.csv:

options(stringsAsFactors = FALSE)

Dezavantajı alfabetik sıralamayla sınırlı olmanızdır. (yeniden sıralamak arkadaşlarınız için arkadaşınızdır)


38

Bu bilinen bir sorundur ve olası bir çözüm, örneğinizin drop.levels()gerçekleştiği gdata paketinde sağlanır.

> drop.levels(subdf)
  letters numbers
1       a       1
2       b       2
3       c       3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"

Hmisc paketinde de dropUnusedLevelsfonksiyon var . Ancak, yalnızca altküme operatörünü değiştirerek çalışır ve burada uygulanamaz.[

Sonuç olarak, sütun başına temel bir yaklaşım basittir as.factor(as.character(data)):

> levels(subdf$letters)
[1] "a" "b" "c" "d" "e"
> subdf$letters <- as.factor(as.character(subdf$letters))
> levels(subdf$letters)
[1] "a" "b" "c"

5
reorderParametre drop.levelsfonksiyonu bahsetmemiz: Bunu faktörlerin orijinal düzeni korumak için varsa, onu kullanmak FALSEdeğeri.
daroczig

Gdata sadece drop.levels için kullanıldığında "gdata: read.xls 'XLS' (Excel 97-2004) dosyaları için ETKİN desteklenir." "gdata: read.xls () tarafından ihtiyaç duyulan perl liberies yüklenemiyor" "gdata: 'XLSX' (Excel 2007+) dosyalarını desteklemek için." msgstr "gdata: 'installXLSXsupport ()' 'işlevini çalıştır" "gdata: perl'i otomatik olarak indirip kurmak için". BaseR'den droplevels kullanın ( stackoverflow.com/a/17218028/9295807 )
Vrokipal

Şeyler zamanla olur. Sen edilir Dokuz sene önce yazdığım bir cevap üzerinde yorum. Bu yüzden bunu genel olarak temel R çözümlerini tercih etmek için bir ipucu olarak alalım, çünkü bunlar hala N yıl civarında olacak işlevselliği kullananlardır .
Dirk Eddelbuettel

25

Aynı şeyi yapmanın başka bir yolu dplyr

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)

Düzenle:

Ayrıca Çalışır! Agenis sayesinde

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)

17

Bütünlük adına, şimdi de var fct_dropiçinde forcatspaketin http://forcats.tidyverse.org/reference/fct_drop.html .

İlgilendiği droplevelsyoldan farklıdır NA:

f <- factor(c("a", "b", NA), exclude = NULL)

droplevels(f)
# [1] a    b    <NA>
# Levels: a b <NA>

forcats::fct_drop(f)
# [1] a    b    <NA>
# Levels: a b

15

İşte factor(..)yaklaşıma eşdeğer olduğuna inandığım başka bir yol :

> df <- data.frame(let=letters[1:5], num=1:5)
> subdf <- df[df$num <= 3, ]

> subdf$let <- subdf$let[ , drop=TRUE]

> levels(subdf$let)
[1] "a" "b" "c"

Ha, bunca yıldan sonra `[.factor`, droptartışması olan bir yöntem olduğunu bilmiyordum ve bunu 2009'da yayınladın ...
David Arenburg

8

Bu iğrenç. Diğer paketleri yüklemekten kaçınmak için genellikle böyle yaparım:

levels(subdf$letters)<-c("a","b","c",NA,NA)

hangi seni alır:

> subdf$letters
[1] a b c
Levels: a b c

Yeni düzeylerin eski düzeylerde (subdf $ mektuplar) dizininde ne varsa onların yerini alacağına dikkat edin.

levels(subdf$letters)<-c(NA,"a","c",NA,"b")

çalışmaz.

Çok fazla seviyeniz olduğunda bu kesinlikle ideal değildir, ancak birkaç tanesi için hızlı ve kolaydır.


8

R kaynağındakidroplevels yöntemler koduna baktığınızda, factorişlevini tamamladığını görebilirsiniz . Bu, temel olarak sütunu factorişlevle yeniden oluşturabileceğiniz anlamına gelir .
Tüm faktör sütunlarından düzeyleri düşürmek için data.table yolunun altında.

library(data.table)
dt = data.table(letters=factor(letters[1:5]), numbers=seq(1:5))
levels(dt$letters)
#[1] "a" "b" "c" "d" "e"
subdt = dt[numbers <= 3]
levels(subdt$letters)
#[1] "a" "b" "c" "d" "e"

upd.cols = sapply(subdt, is.factor)
subdt[, names(subdt)[upd.cols] := lapply(.SD, factor), .SDcols = upd.cols]
levels(subdt$letters)
#[1] "a" "b" "c"

1
Sanırım data.tableyol böyle bir şey olurdufor (j in names(DT)[sapply(DT, is.factor)]) set(DT, j = j, value = factor(DT[[j]]))
David Arenburg

1
@DavidArenburg, [.data.tablesadece bir kez dediğimiz gibi burada çok fazla
değişmiyor

7

İşte bunu yapmanın bir yolu

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]

2
Bu, 5 yıl önce yayınlanan bu cevabın bir kopyasıdır .
David Arenburg

6

Bunu yapmak için yardımcı fonksiyonlar yazdım. Şimdi gdata'nın düşüşünü bildiğime göre. Seviyeler, oldukça benzer görünüyor. İşte bunlar ( buradan ):

present_levels <- function(x) intersect(levels(x), x)

trim_levels <- function(...) UseMethod("trim_levels")

trim_levels.factor <- function(x)  factor(x, levels=present_levels(x))

trim_levels.data.frame <- function(x) {
  for (n in names(x))
    if (is.factor(x[,n]))
      x[,n] = trim_levels(x[,n])
  x
}

4

Çok ilginç bir iş parçacığı, özellikle sadece alt seçimi tekrar etmeyi düşündüm. Daha önce benzer bir sorun yaşadım ve sadece karaktere ve sonra faktöre döndüm.

   df <- data.frame(letters=letters[1:5],numbers=seq(1:5))
   levels(df$letters)
   ## [1] "a" "b" "c" "d" "e"
   subdf <- df[df$numbers <= 3]
   subdf$letters<-factor(as.character(subdf$letters))

Yani, factor(as.chracter(...))çalışır, ama sadece daha az verimli ve özlü factor(...). Diğer cevaplardan kesinlikle daha kötü görünüyor.
Gregor Thomas

1

Ne yazık ki RevoScaleR'nin rxDataStep'i kullanılırken factor () çalışmıyor gibi görünüyor. İki adımda yapıyorum: 1) Karaktere dönüştürün ve geçici harici veri çerçevesinde (.xdf) saklayın. 2) Faktöre geri dönün ve kesin dış veri çerçevesinde saklayın. Bu, tüm verileri belleğe yüklemeden kullanılmayan faktör seviyelerini ortadan kaldırır.

# Step 1) Converts to character, in temporary xdf file:
rxDataStep(inData = "input.xdf", outFile = "temp.xdf", transforms = list(VAR_X = as.character(VAR_X)), overwrite = T)
# Step 2) Converts back to factor:
rxDataStep(inData = "temp.xdf", outFile = "output.xdf", transforms = list(VAR_X = as.factor(VAR_X)), overwrite = T)

1

Benim durumumda hiçbiri dışında ama hepsi gibi görünüyorsa burada örneklerin çoğunu denedim. Oldukça uzun bir süre mücadele ettikten sonra , faktör sütununda as.character () kullanmayı denedim .

Performans sorunlarından emin değilim.

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.