У меня есть набор данных, который показывает религиозную приверженность партии А и партии Б в стране X, а также процент религиозных приверженцев в каждой стране.
df <- data.frame(
PartyA = c("Christian","Muslim","Muslim","Jewish","Sikh"),
PartyB = c("Jewish","Muslim","Christian","Muslim","Buddhist"),
ChristianPop = c(12,1,74,14,17),
MuslimPop = c(71,93,5,86,13),
JewishPop = c(9,2,12,0,4),
SikhPop = c(0,0,1,0,10),
BuddhistPop = c(1,0,2,0,45)
)
# PartyA PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop
# 1 Christian Jewish 12 71 9 0 1
# 2 Muslim Muslim 1 93 2 0 0
# 3 Muslim Christian 74 5 12 1 2
# 4 Jewish Muslim 14 86 0 0 0
# 5 Sikh Buddhist 17 13 4 10 45
При этом я хочу сложить общую сумму «задействованных» приверженцев религии. Таким образом, в первой строке будет переменная, равная 12 + 9, во второй строке — только 93 (без добавления, поскольку Сторона A и Сторона B одинаковы) и т. д.
# PartyA PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop PartyRel
# 1 Christian Jewish 12 71 9 0 1 21
# 2 Muslim Muslim 1 93 2 0 0 93
# 3 Muslim Christian 74 5 12 1 2 79
# 4 Jewish Muslim 14 86 0 0 0 86
# 5 Sikh Buddhist 17 13 4 10 45 55
Мне трудно даже найти, с чего начать, и я буду очень признателен за помощь.





Мы можем перебирать строки с помощью sapply, а затем paste строку «Pop» к вашим Party столбцам для индексации и суммирования.
df$PartyRel <- sapply(
1:nrow(df),
\(x) ifelse(df[x, 1] == df[x, 2],
df[x, paste0(df[x, 1], "Pop")],
df[x, paste0(df[x, 1], "Pop")] + df[x, paste0(df[x, 2], "Pop")])
)
Идея аналогична моему базовому решению R, описанному выше, но здесь используется map2 из пакета purrr в стиле tidyverse.
library(tidyverse)
df %>%
rowwise() %>%
mutate(PartyRel = map2_int(PartyA, PartyB,
~ifelse(.x == .y,
get(paste0(.x, "Pop")),
get(paste0(.x, "Pop")) + get(paste0(.y, "Pop"))))) %>%
ungroup()
Оба вышеперечисленных результата дают один и тот же результат:
df
PartyA PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop PartyRel
1 Christian Jewish 12 71 9 0 1 21
2 Muslim Muslim 1 93 2 0 0 93
3 Muslim Christian 74 5 12 1 2 79
4 Jewish Muslim 14 86 0 0 0 86
5 Sikh Buddhist 17 13 4 10 45 55
@Dobbleri Рад, что вы нашли ответ, который сработал :) Я думаю, вы используете мой метод map, ошибка произошла потому, что, я полагаю, в ваших столбцах «Pop» есть десятичные числа. map_int возвращает ошибку, если они не целые, вы можете заменить его на map2_dbl (или просто map2(), затем unnest()).
Вы можете использовать grep внутри vapply с ifelse:
srch <- paste(df$PartyA, df$PartyB, sep = "|")
vapply(srch, \(x) ifelse(is.null(dim(df[, grep(x, names(df))])),
df[which(srch == x), grep(x, names(df))],
sum(df[which(srch == x), grep(x, names(df))])),
numeric(1L))
# Christian|Jewish Muslim|Muslim Muslim|Christian Jewish|Muslim Sikh|Buddhist
# 21 93 79 86 55
# to assign the results to a new column:
df$newcol <- vapply(srch, \(x) ifelse(is.null(dim(df[, grep(x, names(df))])),
df[which(srch == x), grep(x, names(df))],
sum(df[which(srch == x), grep(x, names(df))])),
numeric(1L))
Вы можете использовать rowwise, если предпочитаете синтаксис tidyverse:
library(tidyverse)
df %>%
rename_with(~gsub('Pop', '', .x)) %>%
rowwise() %>%
mutate(Partyrel = sum(c_across(-(1:2))[match(PartyA, names(.)) - 2])) %>%
mutate(Partyrel = if (PartyA == PartyB) { Partyrel } else {
sum(c_across(-(1:2))[match(PartyB, names(.)) - 2]) + Partyrel}) %>%
ungroup()
#> # A tibble: 5 x 8
#> PartyA PartyB Christian Muslim Jewish Sikh Buddhist Partyrel
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Christian Jewish 12 71 9 0 1 21
#> 2 Muslim Muslim 1 93 2 0 0 93
#> 3 Muslim Christian 74 5 12 1 2 79
#> 4 Jewish Muslim 14 86 0 0 0 86
#> 5 Sikh Buddhist 17 13 4 10 45 55
Вы можете приблизиться к этому, переведя набор данных в длинный формат, суммируя строки, в которых партия соответствует населению, а затем снова вернув его к широкому формату.
library(tidyverse)
df |>
mutate(country=row_number())|>
pivot_longer(cols=-c(PartyA, PartyB, country),names_pattern = "(.*)Pop") |>
group_by(country) |>
mutate(PartyRel=sum(value[name==PartyA|name==PartyB])) |>
pivot_wider(id_cols = c(PartyA, PartyB,country,PartyRel))
# A tibble: 5 × 9
# Groups: country [5]
PartyA PartyB country PartyRel Christian Muslim Jewish Sikh Buddhist
<chr> <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Christian Jewish 1 21 12 71 9 0 1
2 Muslim Muslim 2 93 1 93 2 0 0
3 Muslim Christian 3 79 74 5 12 1 2
4 Jewish Muslim 4 86 14 86 0 0 0
5 Sikh Buddhist 5 55 17 13 4 10 45
Чтобы понять, что здесь происходит, взгляните на результат после Pivot_longer():
# A tibble: 25 × 5
PartyA PartyB country name value
<chr> <chr> <int> <chr> <dbl>
1 Christian Jewish 1 Christian 12
2 Christian Jewish 1 Muslim 71
3 Christian Jewish 1 Jewish 9
4 Christian Jewish 1 Sikh 0
5 Christian Jewish 1 Buddhist 1
6 Muslim Muslim 2 Christian 1
7 Muslim Muslim 2 Muslim 93
8 Muslim Muslim 2 Jewish 2
9 Muslim Muslim 2 Sikh 0
10 Muslim Muslim 2 Buddhist 0
# ℹ 15 more rows
# ℹ Use `print(n = ...)` to see more rows
тогда это просто случай определения правильных строк для суммирования.
Я думаю, что именно такой подход я бы выбрал в реальной жизни. +1
Для каждой строки сравниваются столбцы Party с именами df, давая логический вектор, который выбирает соответствующие столбцы и, наконец, суммирует их.
df %>%
rowwise %>%
mutate(PartyRel =
sum(pick(everything())[grepl(paste0(PartyA, "|", PartyB), names(.))])) %>%
ungroup
предоставление
# A tibble: 5 × 8
PartyA PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop PartyRel
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Christian Jewish 12 71 9 0 1 21
2 Muslim Muslim 1 93 2 0 0 93
3 Muslim Chris… 74 5 12 1 2 79
4 Jewish Muslim 14 86 0 0 0 86
5 Sikh Buddh… 17 13 4 10 45 55
>
Вы можете попробовать следующий вариант базы R
ptA <- diag(t(df[paste0(df$PartyA, "Pop")]))
ptB <- diag(t(df[paste0(df$PartyB, "Pop")]))
transform(df, PartyRel = (ptA + ptB) / (1 + (PartyA == PartyB)))
который дает
PartyA PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop
1 Christian Jewish 12 71 9 0 1
2 Muslim Muslim 1 93 2 0 0
3 Muslim Christian 74 5 12 1 2
4 Jewish Muslim 14 86 0 0 0
5 Sikh Buddhist 17 13 4 10 45
PartyRel
1 21
2 93
3 79
4 86
5 55
Спасибо за ответ. Я попробовал это, но получил ошибку, которую невозможно преобразовать из числа в целое число? Не нашел решения этой проблемы ниже, но большое спасибо за вашу помощь.