Перекодирование одной переменной, разбросанной по нескольким в R

Я работаю с данными опроса, в котором есть вопрос о расе. Каждая категория расы - это отдельная переменная. Вот что я хочу сделать:

  1. Создайте новую переменную p.race.
  2. Присвойте p.race значение одной из восьми переменных расы / этнической принадлежности (см. Ниже).
  3. Определите, отметил ли человек две или более гонок, и присвойте p.race значение «Две или более гонок» в таких случаях.
  4. Присвойте p.race значение «латиноамериканец или латиноамериканец», когда они указали эту этническую принадлежность.
  5. Создайте новую переменную p.poc, чтобы указать, являются ли они цветными (т. Е. не белыми, включая испаноязычных / латиноамериканцев). Это должно быть 0 или 1.

Восемь категорий рас: белые *, черные *, азиатские *, AIAN *, NHPI *, некоторые другие расы *, две или более рас * и испаноязычные; где * обозначает не латиноамериканского или латиноамериканского происхождения.


Вот что я до сих пор пробовал для разбора "Две или более гонок":

p['p.race'] <- NA # create new variable for race

# list of variable names that store a string indicating the race
## e.g., `race_white` would be either blank or contain "White, European, Middle Eastern, or Caucasian"
race.list <- c('p.race_white', 'p.race_black', 'p.race_asian', 'p.race_aian', 'p.race_nhpi', 'p.race_other')

# iterate through each record
for ( n in 1:length(p) ) {
  multiflag = 0

  # iterate through the race list
  for ( i in race.list ) {

    # if it is not blank, +1 to multiflag
    if ( p$i[n] != '' ) {
      multiflag <- multiflag + 1
    }
  }

  # if multiflag was flagged more than once, assign "Two or more races" to `race`
  if ( multiflag > 1 ) {
    p$p.race[n] <- 'Two or more races'
  }
}

При выполнении он возвращает эту ошибку:

> Error in if (p$i[n] != "") { : argument is of length zero

И вот моя кодировка переменной poc с ошибкой ниже:

p['p.poc'] <- 0 # create a new variable for whether they are a person of color
for ( n in 1:length(p) ) {
  if ( p$p.race_black[n] == 'Black, African-American, or African'
       | p$p.race_asian[n] == 'Asian or Asian-American'
       | p$p.race_aian[n] == 'American Indian or Alaskan Native'
       | p$p.race_nhpi[n] == 'Native Hawaiian or other Pacific Islander'
       | p$p.race_other[n] == 'Other (please specify)'
       | p$p.hispanic[n] == 'Yes') {
    p$p.poc[n] <- 1
  }
}

> Error in if (p$p.race_black[n] == "Black, African-American, or African" |  : 
  missing value where TRUE/FALSE needed

Я действительно не знаю, с чего начать, чтобы присвоить новой переменной race одну из восьми категорий гонок, не делая ее очень длинным кодом.


Если это поможет, ниже приведены вопросы для опроса:

Q1. Вы считаете себя латиноамериканцем, латиноамериканцем или испанцем?

  • да
  • No

Q2. К какой расе вы относитесь (отметьте все подходящие варианты)?

  • Белый, европейский, ближневосточный или кавказский
  • Черный, афроамериканец или африканец
  • Азиат или американец азиатского происхождения
  • Американские индейцы или коренные жители Аляски
  • Коренной гавайец или другой житель тихоокеанских островов
  • Другое (укажите)

А вот пример вывода (текст усечен):

> p[264:271]
#    
#      p.hispanic  p.race_white p.race_black p.race_asian p.race_aian p.race_nhpi p.race_other
#   1  Yes         White
#   2  No          White
#   3  No                       Black
#   4  No          White                     Asian
#   5  Yes                                                                        Some other race

А вот результат dput:

> dput(p[264:270])
structure(list(p.hispanic = structure(c(2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 
2L, 2L, 2L, 2L, 3L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("", "No", "Yes"
), class = "factor"), p.race_white = structure(c(2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 
1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 
2L, 1L, 2L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L), .Label = c("", 
"White, European, Middle Eastern, or Caucasian"), class = "factor"), 
    p.race_black = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 
    1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("", 
    "Black, African-American, or African"), class = "factor"), 
    p.race_asian = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 2L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L), .Label = c("", 
    "Asian or Asian-American"), class = "factor"), p.race_aian = structure(c(1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 
    1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L), .Label = c("", "American Indian or Alaskan Native"
    ), class = "factor"), p.race_nhpi = c(NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), 
    p.race_other = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 
    1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("", 
    "Other (please specify)"), class = "factor")), .Names = c("p.hispanic", 
"p.race_white", "p.race_black", "p.race_asian", "p.race_aian", 
"p.race_nhpi", "p.race_other"), class = "data.frame", row.names = c(NA, 
-79L))

person plnnr    schedule 15.10.2014    source источник
comment
Вы можете dput образец необработанных данных, с которыми вы работаете   -  person rawr    schedule 15.10.2014
comment
Наклеено. Выглядит ужасно - дайте мне знать, если я буду делать репост другим способом.   -  person plnnr    schedule 15.10.2014


Ответы (2)


Это не очень элегантно, но я думаю, что это работает. Использование циклов, особенно вложенных циклов, не очень подходит, поскольку они медленные, но также имеют побочные эффекты, такие как загромождение рабочего пространства.

и вы можете захотеть изменить, как это относится к p.poc, если раса не указана, потому что по умолчанию она равна 1, что может быть не тем, что вы хотите.

Итак, вот один из способов:

tmp <- lapply(1:nrow(p), function(ii) {
  ## this checks for columns that aren't blank or NA, takes the colname
  ## and strips off the prefix
  tmp <- gsub('p.race_', '', names(p)[which(p[ii, -1] != '' & !is.na(p[ii, -1])) + 1])

  ## some special cases for > 1 race and blanks and p.poc
  tmp <- ifelse(length(tmp) > 1, 'Two or more', tmp)
  tmp[is.na(tmp)] <- 'Not specified'
  tmp <- ifelse(p[ii, 1] %in% 'Yes', 'Hispanic or Latino', tmp)
  p.poc <- (!grepl('white', tmp)) * 1

  return(list(p.race = tmp, p.poc = p.poc))
})

head(do.call(rbind, tmp), 20)

#   p.race               p.poc
# [1,] "white"               0    
# [2,] "white"               0    
# [3,] "white"               0    
# [4,] "white"               0    
# [5,] "white"               0    
# [6,] "white"               0    
# [7,] "white"               0    
# [8,] "white"               0    
# [9,] "asian"               1    
# [10,] "white"              0    
# [11,] "other"              1    
# [12,] "white"              0    
# [13,] "white"              0    
# [14,] "white"              0    
# [15,] "Hispanic or Latino" 1    
# [16,] "white"              0    
# [17,] "white"              0    
# [18,] "white"              0    
# [19,] "white"              0    
# [20,] "white"              0   

## and combine back to the data frame
p <- cbind(p, do.call(rbind, tmp))

данные:

p <- structure(list(p.hispanic = structure(c(2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 
2L, 2L, 2L, 2L, 3L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("", "No", "Yes"
), class = "factor"), p.race_white = structure(c(2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 
1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 
2L, 1L, 2L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L), .Label = c("", 
"White, European, Middle Eastern, or Caucasian"), class = "factor"), 
p.race_black = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 
1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("", 
"Black, African-American, or African"), class = "factor"), 
p.race_asian = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 2L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L), .Label = c("", 
"Asian or Asian-American"), class = "factor"), p.race_aian = structure(c(1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 
1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L), .Label = c("", "American Indian or Alaskan Native"
), class = "factor"), p.race_nhpi = c(NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), 
p.race_other = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 
1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("", 
"Other (please specify)"), class = "factor")), .Names = c("p.hispanic", 
"p.race_white", "p.race_black", "p.race_asian", "p.race_aian", 
"p.race_nhpi", "p.race_other"), class = "data.frame", row.names = c(NA, 
  -79L))
person rawr    schedule 15.10.2014
comment
Во-первых, большое спасибо, что взглянули на это! Мне есть чему поучиться из вашего кода. Однако, когда я запускаю код, каждая запись превращается в Два или более, а цветовой флаг p.poc - «1». На самом деле большинство записей принадлежит белым людям. Вы знаете, что пошло не так? - person plnnr; 15.10.2014
comment
Хм, я написал это, глядя на ваши p данные. Правильно ли работает, если запустить код с предоставленными данными? Для категорий гонок я рассматривал только два варианта: пустой '' или NA, поэтому, если есть другие значения (пробелы, N / A, none и т. Д.), Это приведет к более чем двум гонкам. Также, если есть дополнительные столбцы, которые у меня есть, это может испортить ситуацию. добавьте print(tmp) после строки с gsub, чтобы увидеть, какие столбцы выбираются. Это тоже может помочь с p.poc проблемой. Или, может быть, вам просто нужно использовать grepl('white', tmp, ignore.case = TRUE) вместо - person rawr; 15.10.2014
comment
Здесь фактический образец моих данных в виде .csv. Должны быть только пустые значения и строки. Когда я запускаю код, он работает не так, как я ожидал. print(tmp) выводит ВСЕ имена переменных во фрейме данных, а не только гонку. Я попробовал ignore.case = TRUE, но это тоже не сработало. Я также изменил 'white' на 'White, European, Middle Eastern, or Caucasian', чтобы убедиться, что это не так. - person plnnr; 15.10.2014
comment
Я думаю, что у меня есть p.race_, а у вас только race_, поэтому я могу читать ваши данные с помощью p <- read.csv('~/downloads/race_example.csv', stringsAsFactors = FALSE) и менять gsub('p.race_') на gsub('race_'), и он работает, как ожидалось - person rawr; 15.10.2014
comment
Хм, это очень странно. Вот код, который я выполняю, который дает мне результат, содержащий все записи Два или более, и каждый человек цвета. - person plnnr; 15.10.2014
comment
изменить: я немного изменил код в ссылке. Это дает мне в основном два или больше, но иногда латиноамериканец или латиноамериканец, а иногда просто p.poc. - person plnnr; 15.10.2014
comment
вам не нужно инициализировать столбцы p.poc и p.race. Это беспорядок, потому что which просматривает все столбцы за вычетом первого, поэтому, если вы добавите дополнительные, вы получите странные результаты, а именно, что which набирает p.race, потому что вы установили его как NA и это было одним из условий в which. Уберите эти две строки и убедитесь, что вы согласны с тем, что вы называете фреймом данных, и все будет хорошо. - person rawr; 15.10.2014
comment
Еще один вопрос. Когда я делаю str(rd$race) и str(rd$poc), он говорит, что это список из 78. Можно ли преобразовать список (ы) обратно в коэффициент? - person plnnr; 16.10.2014
comment
попробуйте unlist(rd$race) - person rawr; 16.10.2014
comment
Идеально! Большое вам спасибо за вашу помощь! - person plnnr; 16.10.2014
comment
Когда я попытался применить это к исходному набору данных (p вместо rd), он вернулся к предыдущей ошибке (в основном «два или более» и много poc = 1). Не могу найти ошибку. код размещен в pastebin. - person plnnr; 16.10.2014
comment
сложно сказать без данных. сравните str rd и p. если вы можете управлять столбцами, именами и т. д. в p, чтобы они были точно такими же, как rd, просто переработайте код, который работает для rd - person rawr; 16.10.2014

С моей точки зрения, такая задача всегда кажется проще, если данные имеют длинный формат, а не широкий. Однако это означает, что для каждого ответа требуется уникальный идентификатор - в таком случае вы можете просто присвоить целое число каждой строке.

library(tidyr)
library(dplyr)

# Add individual ID to each row
p = mutate(p, id = 1:n())

Как только это будет сделано, я бы немного поработал, чтобы столбец p.hispanic выглядел более похожим на другие столбцы гонки, поместил набор данных в длинный формат, удалил все NA / пробелы, а затем создал две новые переменные. После создания новых переменных их можно присоединить к исходным. Я использую пакет tidyr для изменения формы и dplyr для манипуляций.

p %>%
    mutate(p.hispanic = ifelse(p.hispanic == "No", NA, "Hispanic or Latino")) %>% # change p.hispanic column
    gather(category, answer, p.hispanic:p.race_other, na.rm = TRUE) %>%
    filter(answer != "") %>% # get rid of blanks (if were NA would have removed in "gather")
    group_by(id) %>%
    # Create new variable p.race and p.pop based on rules
    mutate(p.race = ifelse(n_distinct(answer) > 1, "Two or more races", answer),
          p.poc = as.integer(p.race == "White, European, Middle Eastern, or Caucasian")) %>%
    slice(1) %>% # take only 1 record for the duplicate id's
    select(-category, - answer) %>% # remove columns that aren't needed
    left_join(p, ., by = "id") %>% # join new columns with original dataset
    select(-id) # remove ID column if not wanted

Получив этот набор данных, вы можете сбросить уровни p.race с помощью factor, если хотите, чтобы уровни выглядели определенным образом.

person aosmith    schedule 15.10.2014