library(forcats)
library(dplyr)
library(ggplot2)
Розглянемо пакет для маніпуляції із факторами forcats від Hadley Wickham.
Створимо фактор для маніпуляції над ним. Окрім введення тексту, одним із основних найважливіших аргументів являється список доступних рівнів.
x <- c("pear", "apple", "banana", "apple", "pear", "apple")
factor(x, levels = c("banana", "apple", "pear"))
## [1] pear apple banana apple pear apple
## Levels: banana apple pear
Всі значення, які не вказані в списку рівнів, приймають значення NA:
factor(x, levels = c("apple", "banana"))
## [1] <NA> apple banana apple <NA> apple
## Levels: apple banana
Якщо не вказати рівні, то вони будуть взяті із даних в алфавітному порядку:
factor(x)
## [1] pear apple banana apple pear apple
## Levels: apple banana pear
Інколи потрібно, щоб порядок рівнів співпадав із порядком першої появи значення в даних. Для цього можна використати функції unique(), задавши її в аргументі levels=. А можна використати функцію fct_inorder():
f1 <- factor(x, levels = unique(x))
f1
## [1] pear apple banana apple pear apple
## Levels: pear apple banana
f2 <- x %>% factor() %>% fct_inorder()
f2
## [1] pear apple banana apple pear apple
## Levels: pear apple banana
Якщо потрібно отримати доступ до рівнів фактора, то можна використати функцію levels()
levels(f2)
## [1] "pear" "apple" "banana"
В подільшому будемо використовувати дані forcats::gss_cat - вибірка даних із загального соціального дослідження.
# data("gss_cat")
# ?gss_cat
gss_cat
## # A tibble: 21,483 <U+00D7> 9
## year marital age race rincome partyid
## <int> <fctr> <int> <fctr> <fctr> <fctr>
## 1 2000 Never married 26 White $8000 to 9999 Ind,near rep
## 2 2000 Divorced 48 White $8000 to 9999 Not str republican
## 3 2000 Widowed 67 White Not applicable Independent
## 4 2000 Never married 39 White Not applicable Ind,near rep
## 5 2000 Divorced 25 White Not applicable Not str democrat
## 6 2000 Married 25 White $20000 - 24999 Strong democrat
## 7 2000 Never married 36 White $25000 or more Not str republican
## 8 2000 Divorced 44 White $7000 to 7999 Ind,near dem
## 9 2000 Married 44 White $25000 or more Not str democrat
## 10 2000 Married 47 White $25000 or more Strong republican
## # ... with 21,473 more rows, and 3 more variables: relig <fctr>,
## # denom <fctr>, tvhours <int>
Коли фактори зберігаються в даних формату tibble, не можна просто побачити його рівні. Для цього можна скористатися функцією count()
gss_cat %>%
count(race)
## # A tibble: 3 <U+00D7> 2
## race n
## <fctr> <int>
## 1 Other 1959
## 2 Black 3129
## 3 White 16395
Або за допомогою стовбчастої діаграми:
ggplot(gss_cat, aes(race)) +
geom_bar()
Інколи корисно буває змінити порядок факторів (особливо при візуалізації). Наприклад, представте, що потрібно дослідити середню кількість годин, проведених дивлячись ТВ у розрізі релігії:
relig <- gss_cat %>%
group_by(relig) %>%
summarise(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE)
)
ggplot(relig, aes(x = tvhours, y = relig)) +
geom_point()
Дуже складно інтерпритувати даний графік, тому що не видно загальної картини. Це можна виправити, змінивши порядок рівнів у фактору relig за допомогою фукнції fct_reorder(). Функція fct_reorder() приймає три аргументи:
f - сам фактор;x - числовий вектор, який потрібно використати для зміни порядку фактора;fun - необов’язковий аргумент, використовується при наявності декількох значень x для одного значення f (значення за замовчуванням - медіана).ggplot(relig, aes(x = tvhours, y = fct_reorder(relig, tvhours))) +
geom_point()
Зміна порядку фактору привело до того, що ми відразу бачимо, що ті, хто не знає своєї релігії, дивляться більше ТВ, а ті, хто сповідує Іудаїзм та інші східні релігії - найменше.
Для кращого сприйняття коду (і щоб осі нормально підписані були), буде правильніше винести функцію fct_reorder() за межі аргументу aes():
relig %>%
mutate(relig = fct_reorder(relig, tvhours)) %>%
ggplot(aes(x = tvhours, y = relig)) +
geom_point()
Розглянемо середнє значення віку у розрізі доходу:
rincome <- gss_cat %>%
group_by(rincome) %>%
summarise(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n()
)
ggplot(rincome, aes(age, rincome))+geom_point()
Але напевне значення "Not applicable" якось не вписується у верхню частину графіку, по причині того, що дані і так впорядковані, а дане значення виглядало більш коректно у нижній частині графіку. Для цього можна використати іншу функцію зміни порядку фактора fct_relevel() - переміщує рівень “у початок списку”:
levels(rincome$rincome)
## [1] "No answer" "Don't know" "Refused" "$25000 or more"
## [5] "$20000 - 24999" "$15000 - 19999" "$10000 - 14999" "$8000 to 9999"
## [9] "$7000 to 7999" "$6000 to 6999" "$5000 to 5999" "$4000 to 4999"
## [13] "$3000 to 3999" "$1000 to 2999" "Lt $1000" "Not applicable"
levels(fct_relevel(rincome$rincome, "Not applicable"))
## [1] "Not applicable" "No answer" "Don't know" "Refused"
## [5] "$25000 or more" "$20000 - 24999" "$15000 - 19999" "$10000 - 14999"
## [9] "$8000 to 9999" "$7000 to 7999" "$6000 to 6999" "$5000 to 5999"
## [13] "$4000 to 4999" "$3000 to 3999" "$1000 to 2999" "Lt $1000"
rincome %>%
mutate(rincome = fct_relevel(rincome, "Not applicable")) %>%
ggplot(aes(age, rincome))+geom_point()
Інший вид зміни порядку фактора - функція fct_reorder2(). Використовується, коли задається колір для ліній на груфіку. Це робить графік більш читабельним.
Її аргументи:
f - фактор;x, y - змінні, по яким відбувається зміна порядку фактора. Сортує фактор по значенням y зв“язаних із найбільшими значеннями x;fun - необов’язковий аргумент, використовується при наявності декількох значень x для одного значення f (значення за замовчуванням - медіана).by_age <- gss_cat %>%
filter(!is.na(age)) %>%
group_by(age, marital) %>%
count() %>%
mutate(prop = n / sum(n))
levels(by_age$marital)
## [1] "No answer" "Never married" "Separated" "Divorced"
## [5] "Widowed" "Married"
levels(fct_reorder2(by_age$marital, by_age$age, by_age$prop))
## [1] "Widowed" "Married" "Divorced" "Never married"
## [5] "No answer" "Separated"
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")
Для bar plot зручно виростивувати фактори, які впрорядковані по частоті. Це можна зробити за допомогою функції fct_infreq(). Також дана функція може бути використана разом із функцією fct_rev - обернений порядок рівнів фактора.
factor(x)
## [1] pear apple banana apple pear apple
## Levels: apple banana pear
fct_infreq(x)
## [1] pear apple banana apple pear apple
## Levels: apple pear banana
gss_cat %>%
mutate(marital = marital %>% fct_infreq()) %>%
ggplot(aes(marital)) +
geom_bar()
gss_cat %>%
mutate(marital = marital %>% fct_infreq() %>% fct_rev()) %>%
ggplot(aes(marital)) +
geom_bar()
Більш потужим механізмом, аніж зміна порядку фактора, є зміна його значення. Це дозволить більш ясніше вказати мітки рівнів фактору для публікацій. Найбільш загальний і потужний інструмент - це fct_recode(). Дана функція дозволяє перекодувати або змінити значення кожного рівня. Наприклад, використаємо gss_cat$partyid (партійну приналежність):
gss_cat %>%
count(partyid)
## # A tibble: 10 <U+00D7> 2
## partyid n
## <fctr> <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
## 7 Independent 4119
## 8 Ind,near dem 2499
## 9 Not str democrat 3690
## 10 Strong democrat 3490
Рівні фактору незрозумілі. Можна їх поправити:
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 <U+00D7> 2
## partyid n
## <fctr> <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
## 7 Independent 4119
## 8 Independent, near dem 2499
## 9 Democrat, weak 3690
## 10 Democrat, strong 3490
fct_recode() не буде змінювати рівні, які не згадуються у функції. Якщо вказати рівень, якого не існує, то функція нагадає про це.
Для об“єднання в групи, можна назначити декілька старих рівнів одному новому:
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 <U+00D7> 2
## partyid n
## <fctr> <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
## 7 Democrat, weak 3690
## 8 Democrat, strong 3490
Якщо потрібно “схлопнути”" багато рівнів, то краще використовувати функцію fct_collapse() замість fct_recode():
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 <U+00D7> 2
## partyid n
## <fctr> <int>
## 1 other 548
## 2 rep 5346
## 3 ind 8409
## 4 dem 7180
gss_cat %>%
mutate(relig = relig) %>%
count(relig)
## # A tibble: 15 <U+00D7> 2
## relig n
## <fctr> <int>
## 1 No answer 93
## 2 Don't know 15
## 3 Inter-nondenominational 109
## 4 Native american 23
## 5 Christian 689
## 6 Orthodox-christian 95
## 7 Moslem/islam 104
## 8 Other eastern 32
## 9 Hinduism 71
## 10 Buddhism 147
## 11 Other 224
## 12 None 3523
## 13 Jewish 388
## 14 Catholic 5124
## 15 Protestant 10846
Якщо потрібно об“єднати всі найменші рівні в один, можна використати функцію fct_lump, яка залишить найбільший рівень, а всі інші об”єдна“є:
gss_cat %>%
mutate(relig = fct_lump(relig)) %>%
count(relig)
## # A tibble: 2 <U+00D7> 2
## relig n
## <fctr> <int>
## 1 Protestant 10846
## 2 Other 10637
Також можна використати параметр n, щоб вказати скільки груп (за виключенням Other) залишити:
gss_cat %>%
mutate(relig = fct_lump(relig, n = 5)) %>%
count(relig, sort = TRUE)
## # A tibble: 6 <U+00D7> 2
## relig n
## <fctr> <int>
## 1 Protestant 10846
## 2 Catholic 5124
## 3 None 3523
## 4 Other 913
## 5 Christian 689
## 6 Jewish 388