R / dplyr: Mutate на основе нескольких имен динамических c переменных - PullRequest
1 голос
/ 29 апреля 2020

У меня есть список фреймов данных, каждый из которых содержит несколько переменных, которые содержат значения площади поверхности (оканчивающиеся на «_area»). Для каждой переменной площади поверхности есть соответствующий коэффициент преобразования (оканчивающийся на «_unit»), который я хочу использовать для вычисления третьей переменной, которая содержит площадь в стандартной единице измерения. Я хочу, чтобы эти переменные заканчивались на «_area_ha». Ниже приведены примеры фреймов данных:

a <- tibble(a1_area = c(1,1,1), a2_area_unit = c(1,1,0.5), a2_area = c(1,1,1),
            a1_area_unit = c(1,0.5,0.5), abc = c(1,2,3))

b <- tibble(b1_area = c(1,1,1), b1_area_unit = c(1,1,0.5), b2_area = c(1,1,1),
            b2_area_unit = c(1,0.5,0.5), abc = c(1,2,3))

ab_list <- list(a, b)

names(ab_list) <- c("a", "b")

Я знаю, как это сделать с помощью al oop, но хотел бы понять, как это можно сделать в логике tidyverse / dplyr c , Мой l oop (который дает мне желаемый результат) выглядит следующим образом:

df_names <- names(ab_list)

for (d in df_names) {

  df <- ab_list[[d]]
  var_names <- names(select(df, matches("_area$")))

    for (v in var_names) {
      int  <- df %>% select(all_of(v),)
      int2 <- df %>% select(matches(paste0(names(int), "_unit")))
      int3 <- int*int2
      names(int3) <- paste0(names(int), "_ha")
      df <- cbind(df, int3)
      rm(int, int2, int3)
      }

  ab_list[[d]] <- tibble(df)
  rm(df)
  }

> ab_list
$`a`
# A tibble: 3 x 7
  a1_area a2_area_unit a2_area a1_area_unit   abc a1_area_ha a2_area_ha
    <dbl>        <dbl>   <dbl>        <dbl> <dbl>      <dbl>      <dbl>
1       1          1         1          1       1        1          1  
2       1          1         1          0.5     2        0.5        1  
3       1          0.5       1          0.5     3        0.5        0.5

$b
# A tibble: 3 x 7
  b1_area b1_area_unit b2_area b2_area_unit   abc b1_area_ha b2_area_ha
    <dbl>        <dbl>   <dbl>        <dbl> <dbl>      <dbl>      <dbl>
1       1          1         1          1       1        1          1  
2       1          1         1          0.5     2        1          0.5
3       1          0.5       1          0.5     3        0.5        0.5

Я пытался использовать lapply и mutate_at, но мой подход не работает. Если я правильно понимаю, это потому, что моя среда является вложенной, и я не могу получить доступ к x в функции, которая вычисляет переменную "ha".

ab_list %>% 
  lapply(function(x) mutate_at(x, vars(matches("_area$")), list(ha = ~.*x[[paste0(names(.),"_unit")]])))

Error: Column `a1_area_ha` must be length 3 (the number of rows) or one, not 0 

Есть ли способ получить функцию в mutate_at для доступа к переменная из родительского фрейма данных на основе имени начальной переменной в функции?

Я, конечно, был бы рад любому другому предложению о подходе по принципу Tidyverse для вычисления переменных "_ha" на основе динамических c имен переменных.

Ответы [ 2 ]

0 голосов
/ 29 апреля 2020

Отличный вопрос. Ниже приведено базовое решение R. Я уверен, что он может быть адаптирован к решению Tidyverse (например, с purrr::map2()). Здесь я построил функцию, которая выполняет тест basi c, а затем использовал ее с lapply(). Примечание: ответ адаптирован для вашего примера, поэтому вам нужно адаптировать его, если у вас есть разные имена столбцов для значения / единиц. Надеюсь, это поможет !!

val_by_unit <- function(data) {

  df <- data[order(names(data))]

  # Selecting columns for values and units
  val <- df[endsWith(names(df), "area")]
  unit <- df[endsWith(names(df), "unit")]


  # Check names are multiplying correctly
  if(!all(names(val) == sub("_unit", "", names(unit)))) {
    stop("Not all areas have a corresponding unit")
  }

  # Multiplying corresponding columns
  output <- Map(`*`, val, unit)

  # Renaming output and adding columns   
  data[paste0(names(output), "_ha")] <- output
  data
}

Результаты :

lapply(ab_list, val_by_unit)

$a
# A tibble: 3 x 7
  a1_area a2_area_unit a2_area a1_area_unit   abc a1_area_ha a2_area_ha
    <dbl>        <dbl>   <dbl>        <dbl> <dbl>      <dbl>      <dbl>
1       1          1         1          1       1        1          1  
2       1          1         1          0.5     2        0.5        1  
3       1          0.5       1          0.5     3        0.5        0.5

$b
# A tibble: 3 x 7
  b1_area b1_area_unit b2_area b2_area_unit   abc b1_area_ha b2_area_ha
    <dbl>        <dbl>   <dbl>        <dbl> <dbl>      <dbl>      <dbl>
1       1          1         1          1       1        1          1  
2       1          1         1          0.5     2        1          0.5
3       1          0.5       1          0.5     3        0.5        0.5
0 голосов
/ 29 апреля 2020

Функции tidyverse лучше всего работают с «длинными» форматированными данными, где каждая из ваших строк представляет уникальную точку данных. Для этого вам нужно использовать функцию tidyr::pivot_longer:

# Join dataframes
dplyr::bind_cols(a, b) %>%
# Convert to area columns to long format
tidyr::pivot_longer(
  cols = dplyr::ends_with('area'),
  names_to = 'site',
  values_to = 'area'
) %>%
# Convert unit columns to long format
tidyr::pivot_longer(
  cols = dplyr::ends_with('unit'),
  names_to = 'site2',
  values_to = 'unit'
) %>%
# Just extract first 2 characters of the site column to get unique ID
dplyr::mutate(
  site = stringr::str_sub(site, 1, 2)
) %>%
# Remove redundant columns
dplyr::select(abc, site, area, unit) %>%
# Calculate area in HA
dplyr::mutate(
  area_ha = area * unit
)

Когда ваши данные в длинном формате, вы можете просто использовать dplyr::mutate, чтобы умножить столбец области на столбец единицы, чтобы получить столбец area_ha. Если вы хотите преобразовать ваши данные обратно в исходный формат, вы можете использовать tidyr::pivot_wider для преобразования данных обратно в широкий формат, который даст вам столбцы с именами a1_area_ha, a2_area_ha, et c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...