Изменение формы данных на основе разных столбцов

У меня есть этот набор данных под названием "inputdata"

Country Unknown Male Female Affected Male Female Unaffected Male Female
USA      200  120   80     130     80    50     70        40    30
AU       140  80    60     60      30    30     80        50    30

И у меня будет два набора выходных данных, и они будут похожи на приведенные ниже, как вы можете видеть, входящий набор данных имеет здесь 3 категории, как указано в именах столбцов 2,5,8.

В выходном наборе данных 1 столбец Категория имеет категории, которые являются именами столбцов во входных данных. Тогда страна из столбца страны и столбец Мужской будет иметь значение из столбца Мужской во входных данных.

Точно так же в выходном наборе данных 2 Категория и Страна остаются такими же, но в 3-м столбце Фактические данные должны быть значения из соответствующих категорий во входных столбцах 2,5,8.

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

Output 1

Category    Country Male 
Unknown      USA     120  
Affected     USA     80
Unaffected   USA     40
.
.
.

Output 2

Category    Country Actuals
Unknown      USA     200   
Affected     USA     130 
Unaffected   USA     70 
.
.
.

Итак, что я сделал сейчас, это использовал цикл for для подмножества каждой категории из входного набора данных с использованием индексов -

Например, при использовании inputdata[,c(1,i)] и моей переменной 'i' будут 3,6,9 for output 1 и 2,5,8 for output 2. Затем создал список фреймов данных (по одному для каждой категории) и объединил их для каждого вывода. Мне просто интересно, есть ли другой способ сделать это эффективным.

РЕДАКТИРОВАТЬ: - Добавление моего кода по запросу,

for(i in seq(3, 9, by=3)) {
    if(!exists('mylist')) mylist <- NULL
    output1 <- inputdata[,c(1,i)]
    if(i==3) {
      output1$category <- 'unknown'
    } else if (i==6) {
      output1$category <- 'affected'
    } else
      output1$category <- 'unaffected'
    mylist <- c(mylist,output1)
    rm(output1)
  }


   for(i in seq(2, 8, by=3)) {
        if(!exists('mylist')) mylist <- NULL
        output2 <- inputdata[,c(1,i)]
        if(i==3) {
          output2$category <- 'unknown'
        } else if (i==6) {
          output2$category <- 'affected'
        } else
          output2$category <- 'unaffected'
        mylist <- c(mylist,output2)
        rm(output2)
      }

Пожалуйста, дайте мне знать, если что-то неясно.


person ds_user    schedule 14.05.2017    source источник
comment
Пожалуйста, опубликуйте код, который вы пробовали.   -  person Sotos    schedule 14.05.2017
comment
Добавил сюда. :)   -  person ds_user    schedule 14.05.2017
comment
Но у вас есть столбцы с таким же именем. Имена столбцов должны быть уникальными. Можете ли вы загрузить эту таблицу в R как data.frame, сохраняя имена этих столбцов?   -  person Osdorp    schedule 14.05.2017
comment
Да. R позволяет читать его из excel таким образом, используя функцию read_excel.   -  person ds_user    schedule 14.05.2017
comment
Мы даже можем переименовать его в male1, male2, female1, female2 после прочтения на R. Это не проблема.   -  person ds_user    schedule 14.05.2017
comment
Да, но как мы можем узнать, что есть что? Какие столбцы называются женскими? А разные мужские?   -  person Osdorp    schedule 14.05.2017
comment
Почему бы вам не захотеть, чтобы и мужские (или женские), и фактические значения в одном фрейме данных?   -  person Sotos    schedule 14.05.2017
comment
Ага. Мы также можем иметь оба в одном наборе данных. Затем я подберу их для своего использования.   -  person ds_user    schedule 14.05.2017


Ответы (1)


Вот базовый метод R, который использует stack для преобразования в long на основе определенных столбцов (например, мужской / женский, категория).

#data frame of categories - You can further split of country if needed 
# by split(d1, d1$country)

d1 <- data.frame(stack(df[-1][c(T, F, F)]), country = df$Country, stringsAsFactors = FALSE)

#  values        ind country
#1    200    Unknown     USA
#2    140    Unknown      AU
#3    130   Affected     USA
#4     60   Affected      AU
#5     70 Unaffected     USA
#6     80 Unaffected      AU

#create a list with two data frames (male and female)
#To split by country again, then lapply(l1, function(i) split(i, i$country))

l1 <- lapply(c('Male', 'Female'), function(i) setNames(data.frame(d1$ind, 
                                                 stack(df[grepl(i, names(df))])[-2],
                                                    df$Country, stringsAsFactors = F), 
                                              c('category', i, 'country')))

#[[1]]
#    category Male country
#1    Unknown  120     USA
#2    Unknown   80      AU
#3   Affected   80     USA
#4   Affected   30      AU
#5 Unaffected   40     USA
#6 Unaffected   50      AU

#[[2]]
#    category Female country
#1    Unknown     80     USA
#2    Unknown     60      AU
#3   Affected     50     USA
#4   Affected     30      AU
#5 Unaffected     30     USA
#6 Unaffected     30      AU

ДАННЫЕ

dput(df)
structure(list(Country = c("USA", "AU"), Unknown = c(200L, 140L
), Male = c(120L, 80L), Female = c(80L, 60L), Affected = c(130L, 
60L), Male.1 = c(80L, 30L), Female.1 = c(50L, 30L), Unaffected = c(70L, 
80L), Male.2 = c(40L, 50L), Female.2 = c(30L, 30L)), .Names = c("Country", 
"Unknown", "Male", "Female", "Affected", "Male.1", "Female.1", 
"Unaffected", "Male.2", "Female.2"), row.names = c(NA, -2L), class = "data.frame")
person Sotos    schedule 14.05.2017