преобразовать фрейм данных в другой фрейм данных с 0 и 1 в R - PullRequest
0 голосов
/ 28 декабря 2018

Я хотел бы преобразовать фрейм данных A в фрейм данных B

A = data.frame(male = c(3, 5), female = c(1,2))

B = data.frame(male = c(1,1,1,1,1,1,1,1,0,0,0), female = c(0,0,0,0,0,0,0,0,1,1,1))

У меня есть этот метод

new <- data.frame(male = c(rep(1, sum(male)), rep(0, sum(female))), female = c(rep(0, sum(male)), rep(1, sum(female))))

, который дает мне желаемый фрейм данных.

Однако есть ли лучший способ сделать это, поскольку мой исходный кадр данных (A) является более сложным, чем пример?

ОБНОВЛЕНИЕ

Кадр данных может быть более сложным, например,

A = data.frame(month = c("July", "August"), male = c(5, 3), female = c(2,1))

, который необходимо преобразовать в

data.frame(month = c(rep("July", 5), rep("July", 2), rep("Aug", 3), rep("Aug", 1)),
       male = c(rep(1, 5), rep(0, 2), rep(1, 3), rep(0, 1)),
       female = c(rep(0, 5), rep(1, 2), rep(0, 3), rep(1, 1)))

#    month male female
#1    July    1      0
#2    July    1      0
#3    July    1      0
#4    July    1      0
#5    July    1      0
#6    July    0      1
#7    July    0      1
#8  August    1      0
#9  August    1      0
#10 August    1      0
#11 August    0      1

Спасибо.

Ответы [ 3 ]

0 голосов
/ 28 декабря 2018

Не уверен, есть ли пакет, который имеет дело с этим, но используя базу R, мы можем использовать apply

do.call(rbind, apply(A, 1, function(x) {
   y <- as.numeric(x[-1])
  data.frame(month = rep(x[1], sum(y)), male = rep(c(1, 0), c(y[1], y[2])), 
             female = rep(c(0, 1), c(y[1], y[2]))) #Thanks @iod for simplifying
})) 


#    month male female
#1    July    1      0
#2    July    1      0
#3    July    1      0
#4    July    1      0
#5    July    1      0
#6    July    0      1
#7    July    0      1
#8  August    1      0
#9  August    1      0
#10 August    1      0
#11 August    0      1

Здесь для каждой строки мы создаем фрейм данных, где первый столбец - месяц.Мы рассчитываем число 1 для мужчины из столбца "мужчина", а число 0 вычитается из общей суммы - количество мужчин и наоборот для женщин.

0 голосов
/ 28 декабря 2018

Мы можем сделать это за tidyverse.gather данные в формате 'long', затем разверните строки на uncount с помощью столбца 'val', создайте столбец из 1 с, сгруппированный по 'month', создайте столбец последовательности ('ind'), spread от 'длинного' до 'широкого'

library(tidyverse)
gather(A, sex, val, -month) %>%
    uncount(val) %>% 
    mutate(val = 1) %>%
    group_by(month = factor(month, levels = month.name)) %>% 
    mutate(ind = row_number()) %>%
    spread(sex, val, fill = 0) %>%
    select(month, male, female)
# A tibble: 11 x 3
# Groups:   month [2]
#   month   male female
#   <fct>  <dbl>  <dbl>
# 1 July       1      0
# 2 July       1      0
# 3 July       1      0
# 4 July       1      0
# 5 July       1      0
# 6 July       0      1
# 7 July       0      1
# 8 August     1      0
# 9 August     1      0
#10 August     1      0
#11 August     0      1

или с использованием аналогичной логики с data.table

library(data.table)
dcast(melt(setDT(A), id.var = 'month')[, rep(1, value), 
 .(month, variable)], month + rowid(month) ~ variable, 
    value.var = 'V1', fill = 0)[, month_1 := NULL][]

данными

A <- data.frame(month = c("July", "August"), male = c(5, 3), female = c(2,1))
0 голосов
/ 28 декабря 2018

Вы можете использовать inverse.rle:

male<-c(1,0)
female<-c(0,1)
inverse.rle(list(lengths=sapply(A,sum),values=male))
 [1] 1 1 1 1 1 1 1 1 0 0 0
inverse.rle(list(lengths=sapply(A,sum),values=female))
 [1] 0 0 0 0 0 0 0 0 1 1 1

Теперь давайте применим этот метод к вашим сложным данным:

split(A,A$month) %>% # split the data by months
lapply(function(x) data.frame(month=x[,1], # take each month's data, and create a data.frame for it with a month column, and the male and female columns with zeros and ones
  male=inverse.rle(list(lengths=sapply(x[,2:3],sum),values=c(1,0))), # if the data is very big, you might want to do they sapply here outside of this lapply, but I doubt this would make a big difference
  female=inverse.rle(list(lengths=sapply(x[,2:3],sum),values=c(0,1))))) %>%
do.call(dplyr::bind_rows, .) %>% # use do.call to take the list we created and bind it. I'm using dplyr's bind.rows because rbind formats the rows poorly.
arrange(sapply(test$month, function(x) which(x==month.name))) # the rows come out sorted by alphabetical order of months, so this fixes that.

результат:

    month male female
1    July    1      0
2    July    1      0
3    July    1      0
4    July    1      0
5    July    1      0
6    July    0      1
7    July    0      1
8  August    1      0
9  August    1      0
10 August    1      0
11 August    0      1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...