заполнить столбец в моем фрейме данных условно на другом столбце значениями из третьего столбца - PullRequest
3 голосов
/ 27 марта 2019

Предупреждение: этот вопрос кажется настолько простым, что мне, как новичку, вероятно, не удалось найти правильное решение среди более сложных тем по SO (посмотрел здесь , здесь , здесь и в других местах)

Я хотел бы заполнить столбец в моем фрейме данных на основе другого столбца и использовать в качестве входных данных дополнительные столбцы.Это гораздо яснее с примером:

  Version1 Version2 Version3 Version4 Presented_version Color
1     blue      red    green   yellow                 1    NA
2      red     blue   yellow    green                 4    NA
3   yellow    green      red     blue                 3    NA

Я хотел бы заполнить столбец " Цвет " значением либо Version1 / Version2 / Version3 / Version 4. Столбец Presented_version говорит мне, какое из этих четырех значений необходимо.Например, в строке 1 Presented_version равно 1, поэтому требуемое значение находится в «Version1» («синий»).Цвет в строке 1 должен быть синим.

Может ли кто-нибудь показать мне способ сделать это без зацикливания на кадре данных, используя множество операторов "if"?

structure(list(Version1 = structure(1:3, .Label = c("blue", "red", 
"yellow"), class = "factor"), Version2 = structure(c(3L, 1L, 
2L), .Label = c("blue", "green", "red"), class = "factor"), Version3 = structure(c(1L, 
3L, 2L), .Label = c("green", "red", "yellow"), class = "factor"), 
    Version4 = structure(3:1, .Label = c("blue", "green", "yellow"
    ), class = "factor"), Presented_version = c(1L, 4L, 3L), 
    Color = c(NA, NA, NA)), class = "data.frame", row.names = c(NA, 
-3L))

======================= РЕДАКТИРОВАНИЕ!

Я упростил пример, чтобы объяснить свой вопрос, но пример выше несколько отличается от моего фактического набора данных, и решенияпоэтому делайте предположения, которые на самом деле не соответствуют моим данным.Вот более точное представление data.frame.В частности, не существует фиксированного соответствия между Presented_version и содержимым столбцов Version1 ... Version 4 (которые отличаются в зависимости от дополнительного столбца, который я сейчас назвал Painter), и Version1-Version4 не обязательно находятся в столбцах 1-4.в моем наборе данных.

  FillerColumn Painter Version1 Version2 Version3 Version4 Version_presented Color FillerColumn.1
1           77       A     blue      red    green   yellow                 1    NA             77
2           77       B      red     blue   yellow    green                 4    NA             77
3           77       C   yellow    green      red     blue                 3    NA             77
4           77       D      red     blue   yellow    green                 1    NA             77
structure(list(FillerColumn = c(77L, 77L, 77L, 77L), Painter = structure(1:4, .Label = c("A", 
"B", "C", "D"), class = "factor"), Version1 = structure(c(1L, 
2L, 3L, 2L), .Label = c("blue", "red", "yellow"), class = "factor"), 
    Version2 = structure(c(3L, 1L, 2L, 1L), .Label = c("blue", 
    "green", "red"), class = "factor"), Version3 = structure(c(1L, 
    3L, 2L, 3L), .Label = c("green", "red", "yellow"), class = "factor"), 
    Version4 = structure(c(3L, 2L, 1L, 2L), .Label = c("blue", 
    "green", "yellow"), class = "factor"), Version_presented = c(1L, 
    4L, 3L, 1L), Color = c(NA, NA, NA, NA), FillerColumn.1 = c(77L, 
    77L, 77L, 77L)), class = "data.frame", row.names = c(NA, 
-4L))

Ответы [ 3 ]

4 голосов
/ 27 марта 2019

Мы можем использовать векторизованную опцию с индексированием row/column для извлечения значений вместо любого цикла

df1$color <- df1[1:4][cbind(1:nrow(df1), df1$Presented_version)]
df1$color
#[1] "blue"  "green" "red"  

Тесты

dfN <- df1[rep(seq_len(nrow(df1)), 1e6),]


system.time({
   dfN[1:4][cbind(1:nrow(dfN), dfN$Presented_version)]

 })
# user  system elapsed 
#   1.216   0.110   1.321


system.time({
 cols <- grep("^Version", names(dfN))
 unlist(mapply(function(x, y) dfN[x, cols][y], 
                    1:nrow(dfN),dfN$Presented_version))

 })
#  user  system elapsed 
# 319.907   1.644 322.418 

Теперь, давайте посмотрим на другой вариант с apply

system.time({
  apply(dfN, 1, function(x) x[cols][as.numeric(x["Presented_version"])])
 }) 
#  user  system elapsed 
# 14.240   0.365  14.550 
2 голосов
/ 27 марта 2019

Мне нравится связываться с набором данных.Попробуйте использовать data.table melt

df <- setDT(df)

df1 <- melt.data.table(df,
                       id.vars = c('Presented_version'),
                       measure.vars = patterns('Version'),
                       value.name = 'Color',
                       variable.name = 'Version')[
  , version1 := str_extract(Version, '\\d+')][
    Presented_version == version1][
      version1 := NULL]

, в результате чего

   Presented_version  Version Color 
1:                 1 Version1  blue        
2:                 3 Version3   red        
3:                 4 Version4 green      

И, если вам нужна информация в той же исходной структуре

merge(df, 
      df1[, .(Presented_version, Color)],
      by = 'Presented_version')

   Presented_version Version1 Version2 Version3 Version4 Color
1:                 1     blue      red    green   yellow  blue
2:                 3   yellow    green      red     blue   red
3:                 4      red     blue   yellow    green green  
1 голос
/ 27 марта 2019

В одну сторону, используя mapply

cols <- grep("^Version", names(df))
df$Color <- unlist(mapply(function(x, y) df[x, cols][y], 
                   1:nrow(df),df$Presented_version))

df
#  Version1 Version2 Version3 Version4 Presented_version Color
#1     blue      red    green   yellow                 1  blue
#2      red     blue   yellow    green                 4 green
#3   yellow    green      red     blue                 3   red

И с apply

apply(df, 1, function(x) x[cols][as.numeric(x["Presented_version"])])
#[1] "blue"  "green" "red" 
...