15 Faktörler

15.1 Giriş

R’da, faktörler sabit ve bilinen bir olası değerler kümesi olan kategorik değişkenler ile çalışmak için kullanılır. Karakter vektörlerini alfabetik olmayan bir sırada görüntülemek istediğinizde de kullanışlılardır.

Tarihsel olarak, faktörlerle çalışmak karakterlerle çalışmaktan çok daha kolaydı. Buna bağlı olarak, temel R’daki fonksiyonların çoğu karakterleri otomatik olarak faktörlere dönüştürür. Bu, faktörlerin çoğu zaman gerçekten gerekli olmadıkları yerlerde bitmelerine neden olur. Neyse ki, tidyverse’de bu konuda endişelenmenize gerek yok ve faktörlerin gerçekten yararlı olduğu durumlara odaklanabilirsiniz.

15.1.1 Ön koşullar

Faktörlerle çalışmak için, kategorik (categorical) değişkenlerle baş etmemize yardımcı araçlar sağlayan forcats paketini kullanacağız (ve bu, faktörler anlamına gelen factors’ün bir anagramı!). Bu paket, faktörlerle çalışmak için çok çeşitli yardımcılar sağlar. forcats temel tidyverse paketlerinin bir parçası değildir, bu yüzden ayrıca yüklemeliyiz.

library(tidyverse)
library(forcats)

15.1.2 Daha fazlasını öğrenmek için

Eğer faktörler hakkında daha fazla bilgi edinmek istiyorsanız, Amelia McNamara ve Nicholas Horton’un makalesini, Wrangling categorical data in R okumanızı öneriyorum. Bu makale, stringsAsFactors: An unauthorized biography ve stringsAsFactors = <sigh>’te tartışılan tarihin bir kısmını ortaya koymaktadır, ve bu kitapta açıklanan kategorik verilere ilişkin tidyverse yaklaşımlarını temel R yöntemleriyle karşılaştırır. Makalenin erken bir sürümü forcats paketini motive etmeye ve kapsamını belirleme yardımcıdır; Amelia ve Nick’e teşekkürler!

15.2 Faktör oluşturma

Ayları içeren bir değişkeniniz olduğunu varsayalım:

x1 <- c("Dec", "Apr", "Jan", "Mar")

Bu değişkeni kaydetmek için dizge kullanmanın iki sorunu vardır:

  1. Sadece on iki olası ay var ve sizi yazım hatalarından koruyacak hiçbir şey yok:

    x2 <- c("Dec", "Apr", "Jam", "Mar")
  2. İçeriği yararlı bir şekilde sıralamaz:

    sort(x1)
    #> [1] "Apr" "Dec" "Jan" "Mar"

Bu sorunların her ikisini de faktörle düzeltebilirsiniz. Bir faktör oluşturmak için geçerli bir levels listesi oluşturarak başlamalısınız:

month_levels <- c(
  "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)

Şimdi bir faktör oluşturabilirsiniz:

y1 <- factor(x1, levels = month_levels)
y1
#> [1] Dec Apr Jan Mar
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
sort(y1)
#> [1] Jan Mar Apr Dec
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

Ve bu küme içerisinde yer almayan tüm değerler, sessizce NA’ya dönüştürülecektir:

y2 <- factor(x2, levels = month_levels)
y2
#> [1] Dec  Apr  <NA> Mar 
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

Eğer ki bir uyarı almak isterseniz, readr::parse_factor() fonksiyonunu kullanabilirsiniz:

y2 <- parse_factor(x2, levels = month_levels)
#> Warning: 1 parsing failure.
#> row col           expected actual
#>   3  -- value in level set    Jam

Eğer seviyeleri (levels) çıkarırsanız, veriden alfabetik sıra ile çıkarılırlar:

factor(x1)
#> [1] Dec Apr Jan Mar
#> Levels: Apr Dec Jan Mar

Bazen faktörün seviyelerinin veride ilk göründükleri sırada olmasını tercih edebilirsiniz. Bunu faktörü oluştururken seviyeleri unique(x)e eşitleyerek, ya da sonrasında, fct_inorder() ile yapabilirsiniz:

f1 <- factor(x1, levels = unique(x1))
f1
#> [1] Dec Apr Jan Mar
#> Levels: Dec Apr Jan Mar

f2 <- x1 %>% factor() %>% fct_inorder()
f2
#> [1] Dec Apr Jan Mar
#> Levels: Dec Apr Jan Mar

Eğer geçerli seviyelere direkt olarak erişmeniz gerekirse, bunu levels() ile yapabilirsiniz:

levels(f2)
#> [1] "Dec" "Apr" "Jan" "Mar"

15.3 Genel Sosyal Anket

Bu bölümün geri kalanında, forcats::gss_cat verisine odaklanacağız. Bu, uzun süredir devam etmekte olan ve Chicago Üniversitesi’nde bağımsız araştırma organizasyonu NORC tarafından düzenlenen bir ABD anket olan Genel Sosyal Anket’ten örnek bir veri. Ankette binlerce soru var, bu yüzden gss_catte, faktörlerle çalışırken karşılaşacağınız bazı yaygın zorlukları örnekleyecek birkaç örnek seçtim.

gss_cat
#> # A tibble: 21,483 × 9
#>    year marital         age race  rincome        partyid     relig denom tvhours
#>   <int> <fct>         <int> <fct> <fct>          <fct>       <fct> <fct>   <int>
#> 1  2000 Never married    26 White $8000 to 9999  Ind,near r… Prot… Sout…      12
#> 2  2000 Divorced         48 White $8000 to 9999  Not str re… Prot… Bapt…      NA
#> 3  2000 Widowed          67 White Not applicable Independent Prot… No d…       2
#> 4  2000 Never married    39 White Not applicable Ind,near r… Orth… Not …       4
#> 5  2000 Divorced         25 White Not applicable Not str de… None  Not …       1
#> 6  2000 Married          25 White $20000 - 24999 Strong dem… Prot… Sout…      NA
#> # … with 21,477 more rows

(Hatırlayın, bu veriseti bir paket tarafından sağlandığından, değişkenlerle ilgili daha fazla bilgiyi ?gss_cat ile alabilirsiniz.)

Faktörler bir tibble olarak kaydedildiğinde seviyelerini çok rahatlıkla göremezsiniz. Bunları görebilmenin bir yolu count() iledir:

gss_cat %>%
  count(race)
#> # A tibble: 3 × 2
#>   race      n
#>   <fct> <int>
#> 1 Other  1959
#> 2 Black  3129
#> 3 White 16395

Ya da bir çubuk grafik ile:

ggplot(gss_cat, aes(race)) +
  geom_bar()

Varsayılan olarak, ggplot2 bir değere sahip olmayan seviyeleri veriden çıkartacaktır. Onları görüntülemeye şu şekilde zorlayabilirsiniz:

ggplot(gss_cat, aes(race)) +
  geom_bar() +
  scale_x_discrete(drop = FALSE)

Bu seviyeler bu verisetinde mevcut olmayan geçerli değerleri temsil etmektedir. Ne yazık ki, dplyr’ın henüz bir drop fonksiyonu yok, ancak gelecekte olacak.

Faktörlerle çalışırken, en yaygın iki işlem seviyelerin sıralamasını değiştirmek ve seviyelerin değerlerini değiştirmektir. Bu işlemler aşağıdaki bölümlerde açıklanmıştır.

15.3.1 Alıştırmalar

  1. rincome (rincome, rapor edilen gelir)’in dağılımını inceleyin. Varsayılan çubuk grafiğinin anlaşılmasını zorlaştıran nedir? Bu grafiği iyileştirebilir misiniz?

  2. Bu anketteki en yaygın relig nedir? En yaygın partyid nedir?

  3. Hangi denom değişkeni hangi relig için geçerlidir? Bunu bir tablo ile nasıl bulursunuz? Bunu bir görselleştirme ile nasıl bulursunuz?

15.4 Faktör sırasını değiştirmek

Görselleştirme için faktör sıralamasını değiştirmek genellikle yararlıdır. Örneğin, dinler arasında günlük TV izlemek için harcanan ortalama saat sayısını karşılaştırmak istediğinizi düşünün:

relig_summary <- gss_cat %>%
  group_by(relig) %>%
  summarise(
    age = mean(age, na.rm = TRUE),
    tvhours = mean(tvhours, na.rm = TRUE),
    n = n()
  )

ggplot(relig_summary, aes(tvhours, relig)) + geom_point()

Bu figürü yorumlamak zor çünkü genel bir örüntü yok. Bunu religdeki seviyeleri fct_reorder() kullanarak yeniden sıralayarak geliştirebiliriz. fct_reorder() üç argüman alır:

  • f, seviyelerini değiştirmek istediğiniz faktör.
  • x, seviyeleri tekrar sıralamak için kullanacağınız nümerik bir vektör.
  • Opsiyonel olarak, fun, eğer fteki her değer için birden fazla x var ise kullanılacak fonksiyon. Varsayılan değer mediandır.
ggplot(relig_summary, aes(tvhours, fct_reorder(relig, tvhours))) +
  geom_point()

Dinleri tekrar sıralamak, “bilmiyorum” kategorisindeki insanların çok daha fazla, Hinduizm ve diğer doğu dinlerininse daha az TV izlediğini görmeyi çok daha kolaylaştırıyor.

Daha karmaşık dönüşümler yapmaya başladıkça, bunları aes() dışına çıkartıp, farklı bir mutate() basamağı haline getirmeyi öneririm. Örneğin, yukarıdaki figürü şu şekilde de yazabilirsiniz:

relig_summary %>%
  mutate(relig = fct_reorder(relig, tvhours)) %>%
  ggplot(aes(tvhours, relig)) +
    geom_point()

Peki ya benzer bir figürü belirtilen gelir seviyesine göre ortalama yaşın nasıl değiştiğine bakmak için çizersek ne olur?

rincome_summary <- gss_cat %>%
  group_by(rincome) %>%
  summarise(
    age = mean(age, na.rm = TRUE),
    tvhours = mean(tvhours, na.rm = TRUE),
    n = n()
  )

ggplot(rincome_summary, aes(age, fct_reorder(rincome, age))) + geom_point()

Burada, seviyelerin keyfi bir şekilde yeniden sıralanması iyi bir fikir değil! Bunun nedeni, rincomeın zaten uğraşmamamız gereken ilkeli bir düzene sahip olmasıdır. fct_reorder()ı isteğe bağlı olarak sıralanmış faktörler için ayırın.

Bununla birlikte, “Not applicable”ı diğer özel seviyelerin önüne çekmek mantıklıdır. fct_relevel() kullanabilirsiniz. Bu fonksiyon bir faktör f, ve sonra sıranın önüne almak istediğiniz seviyeleri alır.

ggplot(rincome_summary, aes(age, fct_relevel(rincome, "Not applicable"))) +
  geom_point()

Neden “Not applicable” için ortalama yaşın bu kadar yüksek olduğunu düşünüyorsunuz?

Bir figür üzerindeki çizgileri renklendirirken başka bir yeniden sıralama türü kullanışlıdır. fct_reorder2(), faktörü en büyük x değeri ile ilişkili y değerine göre yeniden sıralar. Bu, çizgi renklerinin açıklama ile uyumlu (aynı sırada) olması nedeniyle figürün okunmasını kolaylaştırır.

by_age <- gss_cat %>%
  filter(!is.na(age)) %>%
  count(age, marital) %>%
  group_by(age) %>%
  mutate(prop = n / sum(n))

ggplot(by_age, aes(age, prop, colour = marital)) +
  geom_line(na.rm = TRUE)

ggplot(by_age, aes(age, prop, colour = fct_reorder2(marital, age, prop))) +
  geom_line() +
  labs(colour = "marital")

Son olarak, çubuk grafikleri için, seviyeleri artan frekansa göre sıralamak için fct_infreq() fonksiyonunu kullanabilirsiniz: bu en basit yeniden sıralama tipidir çünkü başka bir değişkene ihtiyaç duymaz. Bunu fct_rev() ile birleştirmek isteyebilirsiniz.

gss_cat %>%
  mutate(marital = marital %>% fct_infreq() %>% fct_rev()) %>%
  ggplot(aes(marital)) +
    geom_bar()

15.4.1 Alıştırmalar

  1. tvhoursta bazı şüpheli derecede yüksek sayılar var. Ortalama bu veri için iyi bir özet mi?

  2. gss_cattaki her faktör için, seviyelerin sırasının keyfi mi ilkeli mi olduğunu belirleyin.

  3. Neden “Not applicable” levelını seviyelerin önüne taşımak onu figürün en altına taşıdı?

15.5 Faktör seviyelerini değiştirme

Seviyelerin sırasını değiştirmekten daha güçlü olan şey değerlerini değiştirmektir. Bu, yayın için etiketleri netleştirmenize ve üst düzey ekranlar için düzeyleri daraltmanıza olanak sağlar. En genel ve güçlü araç fct_recode()dır. Bu, her seviyenin değerini yeniden kodlamanızı ya da değiştirmenizi sağlar. Örneğin, gss_cat$partyidi alalım:

gss_cat %>% count(partyid)
#> # A tibble: 10 × 2
#>   partyid                n
#>   <fct>              <int>
#> 1 No answer            154
#> 2 Don't know             1
#> 3 Other party          393
#> 4 Strong republican   2314
#> 5 Not str republican  3032
#> 6 Ind,near rep        1791
#> # … with 4 more rows

Seviyeler kısa ve tutarsız. Daha uzun olacak ve paralel bir yapı kullanacak şekilde tekrar ayarlayalım.

gss_cat %>%
  mutate(partyid = fct_recode(partyid,
    "Republican, strong"    = "Strong republican",
    "Republican, weak"      = "Not str republican",
    "Independent, near rep" = "Ind,near rep",
    "Independent, near dem" = "Ind,near dem",
    "Democrat, weak"        = "Not str democrat",
    "Democrat, strong"      = "Strong democrat"
  )) %>%
  count(partyid)
#> # A tibble: 10 × 2
#>   partyid                   n
#>   <fct>                 <int>
#> 1 No answer               154
#> 2 Don't know                1
#> 3 Other party             393
#> 4 Republican, strong     2314
#> 5 Republican, weak       3032
#> 6 Independent, near rep  1791
#> # … with 4 more rows

fct_recode(), açıkça belirtilmeyen seviyeleri olduğu gibi bırakacak ve yanlışlıkla olmayan bir seviyeden bahsederseniz sizi uyaracaktır.

Grupları birleştirmek için aynı yeni seviyeye birden fazla eski seviye atayabilirsiniz:

gss_cat %>%
  mutate(partyid = fct_recode(partyid,
    "Republican, strong"    = "Strong republican",
    "Republican, weak"      = "Not str republican",
    "Independent, near rep" = "Ind,near rep",
    "Independent, near dem" = "Ind,near dem",
    "Democrat, weak"        = "Not str democrat",
    "Democrat, strong"      = "Strong democrat",
    "Other"                 = "No answer",
    "Other"                 = "Don't know",
    "Other"                 = "Other party"
  )) %>%
  count(partyid)
#> # A tibble: 8 × 2
#>   partyid                   n
#>   <fct>                 <int>
#> 1 Other                   548
#> 2 Republican, strong     2314
#> 3 Republican, weak       3032
#> 4 Independent, near rep  1791
#> 5 Independent            4119
#> 6 Independent, near dem  2499
#> # … with 2 more rows

Bu tekniği dikkatli kullanmalısınız: eğer gerçekten farklı kategorileri bir arada gruplandırırsanız, yanıltıcı sonuçlarla karşılaşırsınız.

Çok fazla seviyeyi daraltmak istiyorsanız, fct_collapse(), fct_recode()un kullanışlı bir çeşididir. Her yeni değişken için eski seviyelerin bir vektörünü sağlayabilirsiniz:

gss_cat %>%
  mutate(partyid = fct_collapse(partyid,
    other = c("No answer", "Don't know", "Other party"),
    rep = c("Strong republican", "Not str republican"),
    ind = c("Ind,near rep", "Independent", "Ind,near dem"),
    dem = c("Not str democrat", "Strong democrat")
  )) %>%
  count(partyid)
#> # A tibble: 4 × 2
#>   partyid     n
#>   <fct>   <int>
#> 1 other     548
#> 2 rep      5346
#> 3 ind      8409
#> 4 dem      7180

Bazen, bir figür veya tabloyu daha basitleştirmek için bütün küçük grupları bir araya toplamak istersiniz. Bu fct_lump()ın işidir:

gss_cat %>%
  mutate(relig = fct_lump(relig)) %>%
  count(relig)
#> # A tibble: 2 × 2
#>   relig          n
#>   <fct>      <int>
#> 1 Protestant 10846
#> 2 Other      10637

Varsayılan davranış aşamalı olarak küçük grupları bir araya getirmektir eğer toplanmış grup hala en küçük grup olacaksa. Bu durumda çok yardımcı değil: Bu ankete katılan Amerikalıların çoğunluğunun Protestan olduğu doğrudur, ancak muhtemelen fazla daralttık.

Bunun yerine, n parametresini kullanarak kaç tane grubu (diğer hariç) tutmak istediğimizi belirtebiliriz:

gss_cat %>%
  mutate(relig = fct_lump(relig, n = 10)) %>%
  count(relig, sort = TRUE) %>%
  print(n = Inf)
#> # A tibble: 10 × 2
#>    relig                       n
#>    <fct>                   <int>
#>  1 Protestant              10846
#>  2 Catholic                 5124
#>  3 None                     3523
#>  4 Christian                 689
#>  5 Other                     458
#>  6 Jewish                    388
#>  7 Buddhism                  147
#>  8 Inter-nondenominational   109
#>  9 Moslem/islam              104
#> 10 Orthodox-christian         95

15.5.1 Alıştırmalar

  1. Demokrat, Cumhuriyetçi ve Bağımsız olan insanların oranı zaman içinde nasıl değişti?

  2. rincomeı küçük kategori setlerine nasıl daraltabilirsiniz?