В процессе создания наложенной гистограммы с данными в широком формате я заметил, что мои значения для гистограммы удваиваются.
Быстрое решение состояло в том, чтобы просто разделить значения на 2.
Но что именно происходит под капотом, из-за чего это происходит?
# Data
status <- c("Infected","Not Infected")
female <- c(0.3,0.7)
male <- c(0.8,0.2)
# Colors
dark <- '#008AFE' # blue
lightest <-'#CCE8FF'
light_accent <-'#FFCB93' #peachy
lightest_accent <- '#FFE5C9'
df <- data.frame("Covid" = status,"female_perc" = female, "male_perc" = male)
gender_claim <- ggplot() +
geom_col(data = df,
mapping= aes(x = c("Male"), y = male_perc[2]/2),
fill = light_accent, width = .25) +
geom_col(data = df,
mapping= aes(x = c("Male"), y = male_perc[1]/2),
fill = lightest, width = .15) +
scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, by = .25),
labels = function(y) paste0(round(y*100,0),"%"),
seq(0, 1, by = .25),expand = expansion(mult = c(0, 0))) +
geom_col(data = df,
mapping= aes(x = c("Female"), y = female_perc[2]/2),
fill = lightest_accent, width = .25) +
geom_col(data = df,
mapping= aes(x = c("Female"), y = female_perc[1]/2),
fill = dark, width = .15) +
coord_flip()





ggplot использует ваш фрейм данных. Когда вы используете geom_col(data = df, ...), geom_col попытается нарисовать геометрию для каждой строки df. Вы явно устанавливаете, что x = "Male" и y male_perc[2]/2, и эти отдельные значения будут перерабатываться для каждой строки фрейма данных.
Это то же самое, что:
ggplot(data = data.frame(x = c(1, 2, 3, 4)) +
geom_line(aes(x = x, y = 1))
Я даю ggplot фрейм данных с 4 строками. Я говорю это y = 1, поэтому значение y1 перерабатывается и применяется ко всем 4 рядам. Ваши x = "Male" и y = male_perc[2]/2 будут переработаны во всех двух строках данных.
По умолчанию geom_col будет накладывать перекрывающиеся значения друг на друга. Так вот как ваши данные удваиваются.
Лучше, чем делить на 2, вы можете использовать position = "identity", чтобы ничего не складывалось. Более того, поместите свои данные в длинный формат и используйте эстетические сопоставления.
Чтобы расширить поясняющий ответ от Грегора Томаса, вот пример того, как повернуть ваши данные и построить их:
df %>%
pivot_longer(
-Covid,
values_to = "fraction",
names_to = c("sex", "type"),
names_sep = "_"
) %>%
ggplot(aes(x = sex, y = fraction, fill = Covid)) +
geom_col(position = "dodge")
Здесь pivot_longer берет информацию о поле, встроенную в имена столбцов вашего исходного data.frame, и делает их доступными для ggplot в качестве переменной, чтобы вы могли программно получить к ним доступ и заставить свой график реагировать на них.
«Аккуратное» решение с более ручным управлением эстетикой каждого бара для достижения желаемого внешнего вида:
df %>%
pivot_longer(
-Covid,
values_to = "fraction",
names_to = c("sex", "type"),
names_sep = "_"
) %>%
arrange(desc(Covid)) %>%
ggplot(aes(x = sex, y = fraction, group = Covid)) +
geom_col(position = "identity", aes(width = rep(c(0.25, 0.15), each = 2), fill = letters[1:4]), alpha = 1) +
scale_fill_manual(values = c(lightest_accent, light_accent, dark, lightest)) +
scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, by = .25),
labels = function(y) paste0(round(y*100,0),"%"),
seq(0, 1, by = .25),expand = expansion(mult = c(0, 0))) +
theme(legend.position = "none") +
coord_flip()
Обратите внимание, что здесь вызов arrange строит полосу в нужном порядке, поэтому последние в data.frame отображаются последними и идут вверху. width и fill необходимо установить вручную, чтобы они соответствовали желаемому порядку.
@Bjc51192 Bjc51192 - Только что обновил свой ответ с некоторыми изменениями, чтобы он выглядел как ваш оригинал. Конечно, чтобы получить такой уровень ручного управления, код становится немного громоздким, но я думаю, что он все же значительно более удобочитаем, удобен в сопровождении и обобщаем, чем оригинал.
На самом деле я сделал это немного иначе, используя фильтрацию. Но твой ответ помог.
Проблема, которую я вижу, заключается в том, что я потерял бы гибкость в настройке каждого столбца на гистограмме наложения.