Определите переменную на основе переменных из разных фреймов данных в R - PullRequest
0 голосов
/ 06 марта 2020

Мне нужно определить переменную в df1 на основе переменной в df1 df1$person_id и переменной в df2 df2$gender. Структура следующая * и df1$gender. df1$gender может иметь три варианта: либо мужской, либо женский, либо оба. Пожалуйста, не могли бы вы помочь мне?

Ответы [ 4 ]

1 голос
/ 06 марта 2020

Вот как я это сделаю с dplyr и tidyr:

# Note the stringsAsFactors=T
authors <- data.frame(ids = c("2, 1", "3, 4, 5, 6, 7, 8", "6", "1, 4, 7, 8"), genders = c(NA, NA, NA, NA),
                      stringsAsFactors = FALSE)
gender <- structure(list(id = 1:8, gender = c(1, 1, 2, 1, 1, 1, 2, 1)),row.names = c(NA, -8L), class = "data.frame")

library("dplyr")
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

# Let's normalise your groups
groups <- 
  authors %>%
  # create ID for the group of author
  mutate(group_id = 1:n()) %>% 
  # split authors
  mutate(author_id = strsplit(ids, split = ", ", fixed = TRUE)) %>%
  tidyr::unnest(author_id) %>%
  # cast to integer for compatibility with the other table
  mutate(author_id = as.integer(author_id)) %>% 
  select(group_id, author_id)

# This is the normalized version of your group / author relationship
groups
#> # A tibble: 13 x 2
#>    group_id author_id
#>       <int>     <int>
#>  1        1         2
#>  2        1         1
#>  3        2         3
#>  4        2         4
#>  5        2         5
#>  6        2         6
#>  7        2         7
#>  8        2         8
#>  9        3         6
#> 10        4         1
#> 11        4         4
#> 12        4         7
#> 13        4         8

# Technically this is the "author" table since it contains author details
# I'll rename it for clarity
authors <- gender


group_gender <- 
  groups %>%
  left_join(authors, by = c("author_id" = "id")) %>%
  group_by(group_id) %>%
  summarise(gender = case_when(
    all(gender == 1) ~ "Male",
    all(gender == 2) ~ "Female",
    TRUE ~ "Both"
  ))

# I took the liberty to create a single column with the gender. 
# It makes it easier to compute gender stats.
group_gender
#> # A tibble: 4 x 2
#>   group_id gender
#>      <int> <chr> 
#> 1        1 Male  
#> 2        2 Both  
#> 3        3 Male  
#> 4        4 Both

Создано в 2020-03-06 пакетом Представить (v0 .3.0)

1 голос
/ 06 марта 2020

Вот как я могу сделать это, используя dplyr и tidyr. Нет необходимости в циклах.

library(dplyr)
library(tidyr)

authors %>% 
  mutate(record=row_number(), genders=NULL, id=ids) %>% 
  separate_rows(id, convert=TRUE) %>% 
  left_join(gender) %>% 
  group_by(record, ids) %>% 
  summarize(genders=paste(gender, collapse=", "),
            all_male=all(gender==1),
            all_female=all(gender==2),
            both=any(gender==1) & any(gender==2),
            class=case_when(all(gender==1)~"Male",
                            all(gender==2)~"Female",
                            TRUE~"Both"))

В основном мы сначала расширяем значения, разделенные запятыми, на отдельные строки. Это позволяет нам легко объединять данные с гендерной информацией, а затем обобщать, чтобы проверить распределение полов по авторам.

Это дает

  record ids              genders          all_male all_female both  class
   <int> <fct>            <chr>            <lgl>    <lgl>      <lgl> <chr>
1      1 2, 1             1, 1             TRUE     FALSE      FALSE Male 
2      2 3, 4, 5, 6, 7, 8 2, 1, 1, 1, 2, 1 FALSE    FALSE      TRUE  Both 
3      3 6                1                TRUE     FALSE      FALSE Male 
4      4 1, 4, 7, 8       1, 1, 2, 1       FALSE    FALSE      TRUE  Both 
0 голосов
/ 06 марта 2020

Вы можете написать genderize функцию, основанную на table. И применить его на strsplit. Я изменил данные author так, чтобы в одной строке были оба мужчины.

genderize <- function(id) {
  r <- table(gender[as.double(id), "gender"])
  if (length(r) == 2) r <- 3
  else r <- as.double(names(r))
  factor(r, levels=1:3, labels=c("female", "male", "both"))
}

authors$genders <- sapply(strsplit(as.character(authors$ids), ", "), genderize)
               ids genders
1             3, 7    male
2 3, 4, 5, 6, 7, 8    both
3                6  female
4       1, 4, 7, 8    both

Данные

authors <- structure(list(ids = structure(c(3L, 2L, 4L, 1L), .Label = c("1, 4, 7, 8", 
"3, 4, 5, 6, 7, 8", "3, 7", "6"), class = "factor"), genders = c(NA, 
NA, NA, NA)), class = "data.frame", row.names = c(NA, -4L))

gender <- structure(list(id = 1:8, gender = c(1, 1, 2, 1, 1, 1, 2, 1)), row.names = c(NA, 
-8L), class = "data.frame")
0 голосов
/ 06 марта 2020

Другой вариант:

idx <- strsplit(as.character(authors$ids), split = ', ')

transform(
  authors,
  genders = ifelse(
    sapply(idx, function(x) all(c(1, 2) %in% gender$gender[match(x, gender$id)])),
    'Both',
    ifelse(
      sapply(idx, function(x) any(1 == gender$gender[match(x, gender$id)]) & !any(2 == gender$gender[match(x, gender$id)])),
      'Male',
      'Female')
  )
)

Выход:

               ids genders
1             2, 1    Male
2 3, 4, 5, 6, 7, 8    Both
3                6    Male
4       1, 4, 7, 8    Both
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...