Прекодиране на една променлива, разпределена в няколко в 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)


Това не е много елегантно, но мисля, че работи. Използването на цикли, особено на вложени цикли, не е много „R“, тъй като те са бавни, но имат и странични ефекти като претрупване на работното ви пространство.

и може да искате да промените как това третира 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, нито една и т.н.), това ще доведе до повече от две състезания. Освен това, ако има допълнителни колони освен това, което имам, това може да обърква нещата. добавете 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
edit: Промених леко кода в линка. Дава ми предимно две или повече, но понякога испанец или латиноамериканец, а понякога само 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