Использование списков для упрощения кода в R

Фон:

Всем привет,

Я надеялся, что вы поможете мне вывести мое понимание кода на новый уровень. Я пытаюсь осмыслить списки, их преимущества и использовать их для сокращения избыточного кода. Сколько бы я ни читал о списках и семействах приложений в Интернете, я до сих пор не могу понять, как реализовать их в своей повседневной работе.

Вот ситуация, которую, как мне кажется, можно значительно упростить — это тип кода, который я часто пишу, и который я хочу заменить более кратким форматом.

Пример:

Давайте представим, что все данные mtcars относятся к одному году наблюдения, скажем, к 2018. Давайте также представим, что у нас есть данные, возвращающиеся ежегодно к 2000 году. Итак, теперь есть «18» mtcars data.frames со столбцом года, в котором указан год, и мы берем 18 наблюдений и свяжите их по строкам в один data.frame. Это пример типа данных, с которыми я сейчас работаю. Наблюдения разделены по годам.

data <- mtcars %>%
group_by(date) %>%
  mutate(rank = dense_rank(desc(mpg))
         ))

Манипуляции с данными, которые я хотел бы упростить:

  • Фильтрация: я хочу отфильтровать все за данный год по разным рейтингам миль на галлон.

    data %>% filter(gear == 4, date == '2005') %>% filter(rank %in% c(1:5))

    data %>% filter(gear == 4, date == '2005') %>% filter(rank %in% c(6:10))

    data %>% filter(gear == 4, date == '2005') %>% filter(rank %in% c(11:15))

Как лучше всего упростить избыточный блок кода выше?

Например, я хотел бы использовать функцию seq и сделать что-то вроде:

    data %>% 
filter(gear == 4, date == '2005') %>%
filter(rank %in% seq(1, 100, by = 5))

и сохраните выходные данные каждой ранговой группы в список, а затем отобразите все эти списки в ggplot.

Любая помощь или совет будут высоко оценены, я пытаюсь улучшить свою игру здесь.


person datanalyst    schedule 14.05.2018    source источник
comment
Вопрос, который вы задаете, очень широк, т. Е. Как мне сделать мой код в целом менее избыточным? В конкретном случае фильтрации моя тактика заключается в создании вспомогательных фреймов данных, которые можно присоединять к основным. Например, у вас может быть фрейм данных со столбцами year, min и max, присоединить его к mtcars по годам, а затем filter(mtcars, year >= min & year <= max)   -  person jdobres    schedule 14.05.2018
comment
Вместо этого вам следует искать функции, которые разделяют, а не фильтруют.   -  person IRTFM    schedule 14.05.2018


Ответы (1)


Научиться аккуратно работать со списками и столбцами списков может быть довольно сложно. Я очень рекомендую purrr руководство Дженни Брайан. Здесь вы пытаетесь избежать повторения строк filter много раз для разных значений year, gear и rank. Есть несколько шагов:

  1. Выясните, как получить все комбинации значений, которые вы хотите. Мы делаем это здесь с помощью purrr::cross_df, что является очень удобным способом получения комбинаций переменных.
  2. На самом деле запустите операцию для каждой комбинации. Поскольку наши данные теперь хорошо настроены, чтобы каждая строка была набором входных данных, мы можем использовать pmap для хранения каждого отфильтрованного набора данных в виде элемента списка. В зависимости от того, какой график мы хотим, мы можем использовать другие инструменты, такие как unnest, чтобы фактически получить данные в формате, который мы хотим построить.

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

library(tidyverse)
data <- mtcars %>%
  bind_rows(
    mtcars %>% mutate(year = 2005),
    mtcars %>% mutate(year = 2006)
    ) %>% 
  group_by(year) %>%
  mutate(rank = dense_rank(desc(mpg)))

combos <- cross_df(list(
  year = 2005:2006,
  gear = 3:5,
  start = seq(1, 100, by = 5)
  ))

combos %>%
  mutate(
    rank_range = map(start, ~ .x:(.x + 4)),
    filtered = pmap(
      .l = list(year, gear, rank_range),
      .f = ~ data %>%
        filter(gear == ..2, year == ..1) %>%
        filter(rank %in% ..3)
        )
    )
#> # A tibble: 120 x 5
#>     year  gear start rank_range filtered         
#>    <int> <int> <dbl> <list>     <list>           
#>  1  2005     3     1 <int [5]>  <tibble [0 x 13]>
#>  2  2006     3     1 <int [5]>  <tibble [0 x 13]>
#>  3  2005     4     1 <int [5]>  <tibble [4 x 13]>
#>  4  2006     4     1 <int [5]>  <tibble [4 x 13]>
#>  5  2005     5     1 <int [5]>  <tibble [2 x 13]>
#>  6  2006     5     1 <int [5]>  <tibble [2 x 13]>
#>  7  2005     3     6 <int [5]>  <tibble [2 x 13]>
#>  8  2006     3     6 <int [5]>  <tibble [2 x 13]>
#>  9  2005     4     6 <int [5]>  <tibble [6 x 13]>
#> 10  2006     4     6 <int [5]>  <tibble [6 x 13]>
#> # ... with 110 more rows

Создано 14 мая 2018 г. с помощью пакета reprex (v0.2.0).

person Calum You    schedule 15.05.2018