Изменять новый столбец данных в кадре 1. и сопоставлять значения из другого кадра данных 2. на основе комбинаций переменных - PullRequest
0 голосов
/ 24 мая 2019

Я пытаюсь преобразовать новый столбец в очень большой фрейм данных, используя комбинации двух переменных для получения значения из другого фрейма данных. Я знаю, что могу сделать это без использования цикла for, но исходный фрейм данных составляет ~ 2,7 миллиона строк, поэтому цикл for занимает слишком много времени.

По сути, у меня есть два кадра данных, таких как:

df1 = data.frame(cbind(years = c(2001:2005), sites = c(1:5), var1 = rnorm(n=5)))
df1
   years sites        var1
1   2001     1 -0.01419947
2   2002     2  0.53729311
3   2003     3  0.89222231
4   2004     4  0.20600240
5   2005     5  0.24541548

df2 = data.frame(cbind(`2001` = rnorm(n = 5, mean = 2, sd = 1),
                       `2002` = rnorm(n = 5, mean = 2, sd = 1),
                       `2003` = rnorm(n = 5, mean = 2, sd = 1),
                       `2004` = rnorm(n = 5, mean = 2, sd = 1),
                       `2005` = rnorm(n = 5, mean = 2, sd = 1)))
colnames(df2) = c(2001:2005); rownames(df2) = c(1:5)
df2
      2001      2002      2003      2004      2005
1 -0.1217767 0.6703649 3.6074038 2.3048512 3.0022530
2  2.6167986 1.7673236 0.9467254 0.9903685 1.8533297
3  0.3192424 2.2183726 0.8783466 2.7741821 0.1847018
4  2.3599459 0.5653315 3.8843616 3.3171480 2.9135520
5  1.5399871 2.8127713 1.2168152 2.1788167 2.1153329

, где для df2 имена столбцов - это годы, имена строк - это сайты, а в фактической версии df1 каждая комбинация год / сайт присутствует много раз.

Я бы хотел закончить с:

   years sites        var1    NewVar
1   2001     1 -0.01419947  1.322451
2   2002     2  0.53729311  3.083238
3   2003     3  0.89222231  1.106300
4   2004     4  0.20600240  2.723593
5   2005     5  0.24541548  2.4919137

Так, что переменная NewVar создается на основе комбинации значений в years и sites, которые указывают соответствующее значение для извлечения из df2.

Что я хочу сделать, это mutate столбец в df1, который для каждой строки использует комбинацию переменных сайта и года, чтобы найти соответствующее значение в df2. Как я упоминал ранее, я знаю, что это возможно сделать с циклом for, но на компьютере, на котором я работаю, аналогичный цикл foreach занимал 6 часов на 3 ядрах, поэтому я действительно надеюсь на более быструю версию с мутированием.

Единственная другая мысль для решения, которое у меня было до сих пор, - это использовать комбинацию индексации и тонны операторов ifelse, но реальные версии фреймов данных, с которыми я работаю, имеют 702 уникальных комбинации сайта. и годы.

Любая помощь будет принята с благодарностью!

Ответы [ 3 ]

2 голосов
/ 24 мая 2019

Вы можете попытаться решить эту проблему с помощью пакета data.table. Это очень быстрый пакет для больших объемов данных.

Идея состоит в том, чтобы преобразовать df2 в длинный формат, чтобы значение X сайта каждого года сохранялось в отдельной строке в кадре данных. После этого df1 и df2_long могут быть объединены ключевыми элементами по годам и сайтам.

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

set.seed(123)
df1 = data.frame(cbind(years = c(2001:2005), sites = c(1:5), var1 = rnorm(n=5)))

df2 = data.frame(cbind(`2001` = rnorm(n = 5, mean = 2, sd = 1),
                       `2002` = rnorm(n = 5, mean = 2, sd = 1),
                       `2003` = rnorm(n = 5, mean = 2, sd = 1),
                       `2004` = rnorm(n = 5, mean = 2, sd = 1),
                       `2005` = rnorm(n = 5, mean = 2, sd = 1)))
colnames(df2) = c(2001:2005); rownames(df2) = c(1:5)

# helpercolum to melt the data
df2$site = rownames(df2)
# melt data and change varnames
df2_long = data.table::melt(df2, id.vars = "site")
names(df2_long) = c("sites", "years", "NewVar")
# set df1 as data.table
data.table::setDT(df1)
# set df2 as data.table and convert the factors to numerics, as @Gregor suggested in his post (this way you dont have to deal with common factor-struggles)
data.table::setDT(df2_long)
df2_long$sites = as.numeric(as.character(df2_long$sites))
df2_long$years = as.numeric(as.character(df2_long$years))
# set key-columns on which the join should be made
data.table::setkey(df1, years, sites)
data.table::setkey(df2_long, years, sites)
# leftjoin the data
df2_long[df1]

Спасибо за ваш вклад @Gregor относительно изменения коэффициентов в числовые, а не наоборот.

1 голос
/ 24 мая 2019

Я думаю, что data.table, вероятно, лучший вариант здесь, однако просто для иллюстрации той же логики в tidyverse:

library(tidyverse)

df2 %>%                                            # pipe in df2 
  rowid_to_column('sites') %>%                     # assign rownames to 'sites'
  gather(key = years, value = newVar, -sites) %>%  # transworm df2 to long form
  mutate(years = as.numeric(years)) %>%            # convert 'years' into numeric    
  right_join(df1, by = c('years', 'sites')) %>%    # join df1 and df2 
  select(years, sites, var1, newVar)               # rearrange columns

#   years sites       var1   newVar
# 1  2001     1 -0.2324031 3.652280
# 2  2002     2 -1.6015391 4.144123
# 3  2003     3 -1.9813792 3.514144
# 4  2004     4 -0.6039213 2.334821
# 5  2005     5  0.3302109 3.416026
0 голосов
/ 26 мая 2019

Однострочник без изменения формы и использования эффективного индексации row/column из base R будет

df1$newvar <- df2[cbind(df1$sites, match(df1$years,names(df2)))]  
df1
#  years sites        var1     newvar
#1  2001     1 -0.56047565 3.71506499
#2  2002     2 -0.23017749 2.35981383
#3  2003     3  1.55870831 0.03338284
#4  2004     4  0.07050839 1.27110877
#5  2005     5  0.12928774 3.25381492

данные

set.seed(123)
df1 <- data.frame(cbind(years = c(2001:2005), sites = c(1:5), var1 = rnorm(n=5)))

df2 <- data.frame(cbind(`2001` = rnorm(n = 5, mean = 2, sd = 1),
                       `2002` = rnorm(n = 5, mean = 2, sd = 1),
                       `2003` = rnorm(n = 5, mean = 2, sd = 1),
                       `2004` = rnorm(n = 5, mean = 2, sd = 1),
                       `2005` = rnorm(n = 5, mean = 2, sd = 1)))
colnames(df2) <- 2001:2005
rownames(df2) <- 1:5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...