Как разделить подмножество нескольких столбцов в кадре данных со значениями в другом кадре данных - PullRequest
0 голосов
/ 13 октября 2019

У меня есть 2 больших кадра данных, структурированных как показано ниже. Я хочу разделить значения в столбцах sec1 - sec3, соответствующих переменной VA, только в столбце переменных для каждого имени в df1 на соответствующие значения по имени в столбце значений df2 для каждого имени в df1.

Df1

name    variable    year    Sec1    Sec2    Sec3    
CHN VA  1950        23    45    32   
CHN VA  1951        43    45    67    
CHN E   1950        45    67    87    
CHN E   1951    34  53  62    
IND VA  1950    45  56  24

DF2

name  value    
CHN 3    
IND 6    
MLY 7    
EUR 4

Результат должен выглядеть примерно так:

DF1

name  variable   year   Sec1    Sec2    Sec3   
CHN VA  1950    23/3    45/3    32/3    
CHN VA  1951    43/3    45/3    67/3    
CHN E   1950    45  67  87    
CHN E   1951    34  53  62   
IND VA  1950    45/6    56/6    24/6

У меня естьпопытался установить и использовать cbind(), но у меня проблемы.

Ответы [ 2 ]

4 голосов
/ 13 октября 2019

1) Соединение влево DF1 и DF2, а затем для каждого столбца Sec разделите строку на value. Наконец, удалите столбец value.

library(dplyr)

DF1 %>%
  left_join(DF2, by = "name") %>%
  mutate(value = if_else(variable == "VA", value, 1L)) %>%
  mutate_at(vars(starts_with("Sec")), ~ .x / value) %>%
  select(-value)

, получив:

  name variable year      Sec1      Sec2     Sec3
1  CHN       VA 1950  7.666667 15.000000 10.66667
2  CHN       VA 1951 14.333333 15.000000 22.33333
3  CHN        E 1950 45.000000 67.000000 87.00000
4  CHN        E 1951 34.000000 53.000000 62.00000
5  IND       VA 1950  7.500000  9.333333  4.00000

2) Базовая версия R будет:

m <- merge(DF1, DF2, by = "name", all.x = TRUE, all.y = FALSE)
ix <- m$variable == "VA"
jx <- grep("^Sec", names(m))
m[ix, jx] <- m[ix, jx] / m$value[ix]
m <- m[names(DF1)]

3) Другой подход заключается в преобразовании в длинную форму, выполнении объединения и деления и обратном преобразовании. Обратите внимание, что это приводит к переупорядочению строк.

library(dplyr)
library(tidyr)

DF1 %>%
  gather(key, val, -name, -variable, -year) %>%
  left_join(DF2, by = "name") %>%
  mutate(value = if_else(variable == "VA", value, 1L)) %>%
  mutate(val = val / value) %>%
  spread(key, val)

4) Если вы не против написать это:

library(dplyr)

DF1 %>%
  left_join(DF2, by = "name") %>%
  mutate(value = if_else(variable == "VA", value, 1L)) %>%
  mutate(Sec1 = Sec1 / value, Sec2 = Sec2 / value, Sec3 = Sec3 / value, value = NULL)

4a) или с основанием R:

m <- merge(DF1, DF2, by = "name", all.x = TRUE, all.y = FALSE)
m <- transform(m, value = ifelse(variable == "VA", value, 1))
transform(m, Sec1 = Sec1 / value, Sec2 = Sec2 / value, Sec3 = Sec3 / value, value = NULL)

Примечание

Lines1 <- "name variable year Sec1 Sec2 Sec3
CHN VA 1950 23 45 32
CHN VA 1951 43 45 67
CHN E 1950 45 67 87
CHN E 1951 34 53 62
IND VA 1950 45 56 24"
DF1 <- read.table(text = Lines1, header = TRUE, as.is = TRUE)

Lines2 <- "name value
CHN 3
IND 6
MLY 7
EUR 4"
DF2 <- read.table(text = Lines2, header = TRUE, as.is = TRUE)
1 голос
/ 13 октября 2019

1) Использование data.table Вот один вариант с data.table объединение

library(data.table)# v 1.12.4
nm1 <- paste0("Sec", 1:3)
setDT(df1)[df2, (nm1) := lapply(mget(nm1), function(x) 
        fifelse(variable == 'VA', x/value, x)), on = .(name)]
df1
#   name variable year      Sec1      Sec2     Sec3
#1:  CHN       VA 1950  7.666667 15.000000 10.66667
#2:  CHN       VA 1951 14.333333 15.000000 22.33333
#3:  CHN        E 1950 45.000000 67.000000 87.00000
#4:  CHN        E 1951 34.000000 53.000000 62.00000
#5:  IND       VA 1950  7.500000  9.333333  4.00000

2) Использование dplyr

library(dplyr)
df1 %>%
      mutate_at(vars(starts_with('Sec')), ~ 
        case_when(variable == 'VA' ~ ./ df2$value[match(name, df2$name)], TRUE ~ .))
#  name variable year      Sec1      Sec2     Sec3
#1  CHN       VA 1950  7.666667 15.000000 10.66667
#2  CHN       VA 1951 14.333333 15.000000 22.33333
#3  CHN        E 1950 45.000000 67.000000 87.00000
#4  CHN        E 1951 34.000000 53.000000 62.00000
#5  IND       VA 1950  7.500000  9.333333  4.00000

3) Использование базового R-соответствия

i1 <- df1$variable == 'VA'
df1[i1, nm1] <- df1[i1,nm1]/with(df1, df2$value[match(name[i1], df2$name)])
df1
#  name variable year      Sec1      Sec2     Sec3
#1  CHN       VA 1950  7.666667 15.000000 10.66667
#2  CHN       VA 1951 14.333333 15.000000 22.33333
#3  CHN        E 1950 45.000000 67.000000 87.00000
#4  CHN        E 1951 34.000000 53.000000 62.00000
#5  IND       VA 1950  7.500000  9.333333  4.00000

data

df1 <-structure(list(name = c("CHN", "CHN", "CHN", "CHN", "IND"), 
  variable = c("VA", 
 "VA", "E", "E", "VA"), year = c(1950L, 1951L, 1950L, 1951L, 1950L
), Sec1 = c(23, 43, 45, 34, 45), Sec2 = c(45, 45, 67, 53, 56), 
Sec3 = c(32, 67, 87, 62, 24)), row.names = c(NA, -5L), class = "data.frame")

df2 <- structure(list(name = c("CHN", "IND", "MLY", "EUR"), value = c(3L, 
 6L, 7L, 4L)), class = "data.frame", row.names = c(NA, -4L))
...