Лучший способ вычислить большое количество пар столбцов data.frame

У меня есть два вектора, каждый из которых содержит имена столбцов в моем data.frame, data, то есть в широкой форме, например.

NSW_cols <- c("NSW_1", "NSW_2")   # Columns for two variables from New South Wales
AUS_cols <- c("AUS_1", "AUS_2")   # Columns for two variables from Australia
head(data)
         date       NSW_1       AUS_1        NSW_2      AUS_2
1  2000-01-01 -0.38358623 -1.78824221  0.995984590  1.0744594
2  2001-01-01 -1.95910318  2.03124252 -1.695764903  0.2605978
3  2002-01-01 -0.84170506 -0.70314433 -0.533372143 -0.3142720
4  2003-01-01  1.90354747  0.15816476 -1.372269451 -0.7496301
5  2004-01-01  0.62249393  0.50623480 -2.207919779 -0.8621983
6  2005-01-01  1.99092044 -0.81999511  1.822122519  2.0480403

Мне нужно вычислить share_i := NSW_i / AUS_i (то есть построчно или по date) для i = 1,2, ссылаясь на два моих вектора имен выше.

Например, используя data.table, я могу сделать что-то вроде:

setDT(data)

for (i in 1:2) {
    data[, paste0("share_", i) := get(NSW_cols[i]) / get(AUS_cols[i])]
}

Это кажется глупым только для нескольких столбцов, но на самом деле в моем приложении несколько тысяч столбцов. Более того, в моем реальном приложении мне нужно сделать больше, чем просто разделить один столбец на другой. Поэтому я пытаюсь найти решение, которое будет работать для любой функции, принимающей в качестве входных данных два числовых вектора.

Данные поступают ко мне в несколько случайном виде, поэтому удобный порядок столбцов в этом примере мне тоже не подходит.

В: Каков наилучший способ вычислений для множества пар столбцов с использованием векторов имен?

Подозрение: Может быть, есть хитрый способ сделать это, преобразовав данные в длинную форму? Я не смог заставить его работать.


Пример данных:

structure(list(date = structure(c(10957, 11323, 11688, 12053, 
12418, 12784, 13149, 13514, 13879, 14245, 14610, 14975, 15340, 
15706, 16071, 16436, 16801, 17167, 17532, 17897), class = "Date"), 
    NSW_1 = c(1.24119982707737, 0.138858419103515, 1.71063158823657, 
    -0.430640974722993, -1.04422958092706, 0.537579524580529, 
    -0.669585987150229, 0.638805611438309, -1.72398983449257, 
    -1.74243008034091, 0.689804173282994, 0.330963177173467, 
    0.871067708948055, -2.01624558221344, 1.21257910351036, 1.20049469882194, 
    1.03206832593544, 0.786410256177216, 2.11007351377927, -1.45380984681329
    ), AUS_1 = c(-0.58310384813065, 0.409723982550305, -0.806981635414238, 
    0.0855504408545073, 0.746243168639741, -0.653673061331084, 
    0.657105983301959, 0.549909235009709, -0.806729358432671, 
    -0.997379717276235, 0.97589063842626, -0.169423180700716, 
    0.72219177943747, -0.844418606912503, 1.27729368500115, -1.34311054918022, 
    0.765340668860696, 0.464202569980373, 0.267993278040529, 
    0.667522687135242), NSW_2 = c(0.398467283863779, -0.638071030930068, 
    -0.267712904023511, 0.359879564885712, -1.31286609394071, 
    -0.883969609668706, 2.07709479458465, -2.09922563220098, 
    -1.23850596532828, 0.990433089664036, 1.08866186319808, 0.839852254188259, 
    0.0568586386833875, 0.32387805118027, -0.904668667590953, 
    -0.65218384837012, -0.262454637960949, -0.934662840905703, 
    0.821161212634175, -1.62425917345994), AUS_2 = c(-1.03040366948029, 
    -1.261929311973, 0.39218462589648, -1.13143826165372, 0.544144484008162, 
    1.17660893473457, 0.0252285692390373, 0.515133169692034, 
    -0.654109760017422, 0.503641990827566, -1.2721192223284, 
    -0.0767711536986575, -1.34531937567941, -0.266317560246187, 
    1.08756299501947, 0.700567795415207, -0.4427595146404, -0.78851996588786, 
    -0.856775709774438, -0.746419014623393)), class = "data.frame", row.names = c(NA, 
-20L))
Шаблоны Angular PrimeNg
Шаблоны Angular PrimeNg
Как привнести проверку типов в наши шаблоны Angular, использующие компоненты библиотеки PrimeNg, и настроить их отображение с помощью встроенной...
Создайте ползком, похожим на звездные войны, с помощью CSS и Javascript
Создайте ползком, похожим на звездные войны, с помощью CSS и Javascript
Если вы веб-разработчик (или хотите им стать), то вы наверняка гик и вам нравятся "Звездные войны". А как бы вы хотели, чтобы фоном для вашего...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Начала с розового дизайна
Начала с розового дизайна
Pink Design - это система дизайна Appwrite с открытым исходным кодом для создания последовательных и многократно используемых пользовательских...
Шлюз в PHP
Шлюз в PHP
API-шлюз (AG) - это сервер, который действует как единая точка входа для набора микросервисов.
14 Задание: Типы данных и структуры данных Python для DevOps
14 Задание: Типы данных и структуры данных Python для DevOps
проверить тип данных используемой переменной, мы можем просто написать: your_variable=100
1
0
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот способ.
Используйте lapply не для векторов имен столбцов, а для их индекса.

library(data.table)
setDT(data)

i <- sub(".*(\\d+$)", "\\1", NSW_cols)
shares <- paste("share", i, sep = "_")

data[, (shares) := lapply(seq_along(NSW_cols), \(i) get(NSW_cols[i])/get(AUS_cols[i]))]
head(data)
#>           date      NSW_1       AUS_1       NSW_2       AUS_2    share_1      share_2
#>  1: 2000-01-01  1.2411998 -0.58310385  0.39846728 -1.03040367 -2.1286085  -0.38670988
#>  2: 2001-01-01  0.1388584  0.40972398 -0.63807103 -1.26192931  0.3389072   0.50563136
#>  3: 2002-01-01  1.7106316 -0.80698164 -0.26771290  0.39218463 -2.1197900  -0.68261958
#>  4: 2003-01-01 -0.4306410  0.08555044  0.35987956 -1.13143826 -5.0337669  -0.31807265
#>  5: 2004-01-01 -1.0442296  0.74624317 -1.31286609  0.54414448 -1.3993154  -2.41271598
#>  6: 2005-01-01  0.5375795 -0.65367306 -0.88396961  1.17660893 -0.8223982  -0.75128582

Created on 2022-11-18 with reprex v2.0.2

Жаль, что нельзя избежать get() (или подобных) звонков, делая такие вещи. Спасибо.

diomedesdata 19.11.2022 02:03

Используя tidyverse, я бы сделал что-то вроде этого:

library(dplyr)
library(purrr)
library(magrittr)

map_dfc(
  seq_along(NSW_cols),
  ~ {
    (data[[NSW_cols[.x]]] / data[[AUS_cols[.x]]]) %>%
      list() %>%
      set_names(paste0("share_", .x))
  }
) %>%
  bind_cols(data, .)

Без привязки:

library(dplyr)
library(purrr)
library(magrittr)

mutate(
  data,
  map_dfc(
    seq_along(NSW_cols),
    ~ {
      (data[[NSW_cols[.x]]] / data[[AUS_cols[.x]]]) %>%
        list() %>%
        set_names(paste0("share_", .x))
    }
  )
)

Мне нравится это решение, но я предпочту решение data.table, потому что оно позволяет избежать связывания столбцов после создания новых столбцов. Также мы должны иметь возможность удалить зависимость magrittr с помощью |> здесь, хотя впоследствии может потребоваться изменить порядок столбцов (я не думаю, что _ работает с аргументами ...?).

diomedesdata 19.11.2022 02:03

Обычно я использую |> в своих проектах, но по умолчанию использую %>% в Stack Overflow, так как у некоторых людей все еще может быть <4.1.0. В любом случае, (1) set_names находится на magrittr, а (2), как вы упомянули, _ можно использовать только для именованных аргументов. От зависимости magrittr можно отказаться, используя некоторые переменные для хранения промежуточных результатов.

Santiago 20.11.2022 06:41

Кроме того, вы можете избежать привязки столбцов после создания, вызвав функцию карты из mutate.

Santiago 20.11.2022 06:49

Я обновил ответ, чтобы включить это решение, но я думаю, что оно менее читабельно.

Santiago 20.11.2022 06:53

Другие вопросы по теме