Как сжимать/фильтровать данные для каждой группы?

У меня есть набор данных с некоторыми столбцами и переменной группировки. Я хочу уменьшить набор данных на переменную группировки, max_n строк на уровень группировки. В то же время я хочу сохранить распределение других столбцов. Под этим я подразумеваю, что хочу сохранить самые низкие и самые высокие значения a и b после того, как данные были отфильтрованы. Вот почему я использую функцию setorderv ниже.

library(data.table)

set.seed(22)
n=20
max_n = 6
dt <- data.table("grp"=sample(c("a", "b", "c"), n, replace=T),
                 "a"=sample(1:10, n, replace=T),
                 "b"=sample(1:20, n, replace=T),
                 "id"=1:n)
setorderv(dt, c("grp", "a", "b"))
dt

Мое временное решение, которое не очень элегантно или похоже на data.table, выглядит следующим образом:

dt_new <- data.table()
for (gr in unique(dt[["grp"]])) {
  tmp <- dt[grp == gr, ]
  n_tmp <- nrow(tmp)
  if (n_tmp > max_n) {
    tmp <- tmp[as.integer(seq(1, n_tmp, length.out=max_n)),]
  }
  dt_new <- rbindlist(list(dt_new, tmp))
}

Есть ли более элегантный способ сделать это? Обновлено: мне нужно решение data.table.

Код сейчас слишком громоздкий

Что вы подразумеваете под сохранением распределения других столбцов? Можете ли вы уточнить немного?

Shibaprasadb 12.07.2023 12:35

Да вы же видите, что я сортирую данные по переменным! Поэтому, когда я выбираю первый и последний индекс tmp, я в основном выбираю самое низкое и самое высокое значение комбинации c("a", "b"). Имеет смысл?

EnFiFa 12.07.2023 12:40

Хорошо. Итак, вы хотите уменьшить набор данных, но хотите, чтобы диапазон остался прежним? Как вы выбираете «самый высокий» и «самый низкий» в двух группах? а=5,б=7 и а=6,б=5. Что будет максимальным в этом случае? @ЭнФиФа

Shibaprasadb 12.07.2023 12:48

Привет, ЭнФиФа! Добро пожаловать в StackOverflow!

Mark 12.07.2023 12:54

Мне было интересно, не могли бы вы уточнить для меня, когда вы говорите, что хотите получить самые низкие и самые высокие значения a и b, вы (к сожалению) дали одно и то же имя переменным столбца группы и столбцам, поэтому трудно знать, на кого вы ссылаетесь

Mark 12.07.2023 12:55

Мне было интересно, не могли бы вы уточнить для меня, когда вы говорите, что хотите получить самые низкие и самые высокие значения a и b, вы (к сожалению) дали одно и то же имя переменным столбца группы и столбцам, поэтому трудно знать, на кого вы ссылаетесь

Mark 12.07.2023 12:55

@Shibaprasadb Ну, прямо сейчас, поскольку я сортирую по c («a», «b»), (5, 7) будет меньше, чем (6, 5).

EnFiFa 12.07.2023 12:56

@Shibaprasadb Ну, прямо сейчас, поскольку я сортирую по c («a», «b»), (5, 7) будет меньше, чем (6, 5).

EnFiFa 12.07.2023 12:56

Когда вы говорите, что хотите сохранить распределение других столбцов, я предполагаю, что вы ссылаетесь на эту часть своего кода as.integer(seq(1, n_tmp, length.out=max_n)) правильно? Вы не хотите брать первые 6 рядов, как в seq_len(max_n), верно?

TimTeaFan 12.07.2023 12:57

Когда вы говорите, что хотите сохранить распределение других столбцов, я предполагаю, что вы ссылаетесь на эту часть своего кода as.integer(seq(1, n_tmp, length.out=max_n)) правильно? Вы не хотите брать первые 6 рядов, как в seq_len(max_n), верно?

TimTeaFan 12.07.2023 12:57

Я не знаю, слежу ли я за EnFiFa. Возможно, вы могли бы переименовать группы или столбцы, чтобы их было легче различать.

Mark 12.07.2023 12:58

Я не знаю, слежу ли я за EnFiFa. Возможно, вы могли бы переименовать группы или столбцы, чтобы их было легче различать.

Mark 12.07.2023 12:58

@TimTeaFan точно!

EnFiFa 12.07.2023 12:58

@TimTeaFan точно!

EnFiFa 12.07.2023 12:58

@Отметьте, что именно вы имеете в виду, «присвоив одно и то же имя переменным столбца группы и столбцам»?

EnFiFa 12.07.2023 12:59

@Отметьте, что именно вы имеете в виду, «присвоив одно и то же имя переменным столбца группы и столбцам»?

EnFiFa 12.07.2023 12:59

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

Mark 12.07.2023 13:00

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

Mark 12.07.2023 13:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
11
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете сделать что-то вроде этого

Во-первых, давайте определим строки, которые нужно сохранить.

library(tidyverse)


dt %>%
  group_by(grp) %>%
  arrange(grp, a, b) %>%
  mutate(
    new_id = 1:n(),
    id_desc = case_when(
      new_id == min(new_id) ~ 'Head',
      new_id == max(new_id) ~ 'Tail',
      .default = 'Other'
    )
  ) %>%
  ungroup() -> dt_modified

Затем выберите строки из раздела «Другие». Вы можете использовать slice_head(), если вам нужны лучшие наблюдения вместо случайной выборки.

dt_modified %>%
  filter(id_desc == 'Other') %>%
  group_by(grp) %>%
  slice_sample(n = max_n - 2) %>% #Because head and tail will be there in the dataframe
  ungroup() %>%
  bind_rows(dt_modified %>%
              filter(id_desc != 'Other')) %>%
  arrange(grp, a, b) %>%
  select(-new_id, -id_desc)-> dt_new

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

Я хотел бы решение data.table, но спасибо!

EnFiFa 12.07.2023 13:29

Вы можете сделать что-то вроде этого

Во-первых, давайте определим строки, которые нужно сохранить.

library(tidyverse)


dt %>%
  group_by(grp) %>%
  arrange(grp, a, b) %>%
  mutate(
    new_id = 1:n(),
    id_desc = case_when(
      new_id == min(new_id) ~ 'Head',
      new_id == max(new_id) ~ 'Tail',
      .default = 'Other'
    )
  ) %>%
  ungroup() -> dt_modified

Затем выберите строки из раздела «Другие». Вы можете использовать slice_head(), если вам нужны лучшие наблюдения вместо случайной выборки.

dt_modified %>%
  filter(id_desc == 'Other') %>%
  group_by(grp) %>%
  slice_sample(n = max_n - 2) %>% #Because head and tail will be there in the dataframe
  ungroup() %>%
  bind_rows(dt_modified %>%
              filter(id_desc != 'Other')) %>%
  arrange(grp, a, b) %>%
  select(-new_id, -id_desc)-> dt_new

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

Я хотел бы решение data.table, но спасибо!

EnFiFa 12.07.2023 13:29
Ответ принят как подходящий

Чтобы сохранить минимальную (из a и b), максимальную (то же самое) и общую max_n строк случайным образом из data.table:

dt[, minmax := a %in% range(a) | b %in% range(b), by = grp]
set.seed(42)
dt[, .SD[minmax | 1:.N %in% head(sample(which(!minmax)), max_n - sum(minmax)),], grp]
#        grp     a        id minmax
#     <char> <int> <int> <int> <lgcl>
#  1:      a     1    11    14   TRUE
#  2:      a     2     9    13  FALSE
#  3:      a     2    19    17   TRUE
#  4:      a     5     7     6   TRUE
#  5:      a     8    12    19  FALSE
#  6:      a     9    11     7   TRUE
#  7:      b     1    20     1   TRUE
#  8:      b     2     1    16   TRUE
#  9:      b     3    19     3  FALSE
# 10:      b     4     3    11  FALSE
# 11:      b     7    10    18  FALSE
# 12:      b     9    17    10   TRUE
# 13:      c     1    16    12   TRUE
# 14:      c     3    14    20  FALSE
# 15:      c     5    18     9  FALSE
# 16:      c     6    20     5   TRUE
# 17:      c     7    13     8   TRUE
dt[, minmax := NULL] # cleanup

Прохождение:

  • minmax истинно, где либо a, либо b — это минимальное/максимальное значение для каждой группы (минимальное/максимальное значение по переменной по группе)
  • which(!minmax) возвращает индексы оставшихся строк (где a и b не мин./макс.)
  • sample(.) рандомизирует список оставшихся индексов строк, а head(., max_n - sum(minmax)) возвращает количество строк, не превышающее количество строк, необходимое для получения max_n строк.
  • minmax | 1:.N %in% .. сводится к рядам; в особом случае, когда количество строк, не включая минимальное/максимальное количество строк a/b, меньше max_n, это гарантирует возврат всех строк

Данные

dt <- data.table::as.data.table(structure(list(grp = c("a", "a", "a", "a", "a", "a", "a", "b", "b", "b", "b", "b", "b", "b", "b", "c", "c", "c", "c", "c"), a = c(1L, 2L, 2L, 5L, 8L, 8L, 9L, 1L, 2L, 3L, 4L, 6L, 7L, 8L, 9L, 1L, 3L, 5L, 6L, 7L), b = c(11L, 9L, 19L, 7L, 11L, 12L, 11L, 20L, 1L, 19L, 3L, 3L, 10L, 10L, 17L, 16L, 14L, 18L, 20L, 13L), id = c(14L, 13L, 17L, 6L, 2L, 19L, 7L, 1L, 16L, 3L, 11L, 15L, 18L, 4L, 10L, 12L, 20L, 9L, 5L, 8L)), row.names = c(NA, -20L), class = c("data.table", "data.frame")))

Хорошее решение, спасибо!

EnFiFa 12.07.2023 13:50

Добро пожаловать в SO, ЭнФиФа!

r2evans 12.07.2023 13:53
Ответ принят как подходящий

Чтобы сохранить минимальную (из a и b), максимальную (то же самое) и общую max_n строк случайным образом из data.table:

dt[, minmax := a %in% range(a) | b %in% range(b), by = grp]
set.seed(42)
dt[, .SD[minmax | 1:.N %in% head(sample(which(!minmax)), max_n - sum(minmax)),], grp]
#        grp     a        id minmax
#     <char> <int> <int> <int> <lgcl>
#  1:      a     1    11    14   TRUE
#  2:      a     2     9    13  FALSE
#  3:      a     2    19    17   TRUE
#  4:      a     5     7     6   TRUE
#  5:      a     8    12    19  FALSE
#  6:      a     9    11     7   TRUE
#  7:      b     1    20     1   TRUE
#  8:      b     2     1    16   TRUE
#  9:      b     3    19     3  FALSE
# 10:      b     4     3    11  FALSE
# 11:      b     7    10    18  FALSE
# 12:      b     9    17    10   TRUE
# 13:      c     1    16    12   TRUE
# 14:      c     3    14    20  FALSE
# 15:      c     5    18     9  FALSE
# 16:      c     6    20     5   TRUE
# 17:      c     7    13     8   TRUE
dt[, minmax := NULL] # cleanup

Прохождение:

  • minmax истинно, где либо a, либо b — это минимальное/максимальное значение для каждой группы (минимальное/максимальное значение по переменной по группе)
  • which(!minmax) возвращает индексы оставшихся строк (где a и b не мин./макс.)
  • sample(.) рандомизирует список оставшихся индексов строк, а head(., max_n - sum(minmax)) возвращает количество строк, не превышающее количество строк, необходимое для получения max_n строк.
  • minmax | 1:.N %in% .. сводится к рядам; в особом случае, когда количество строк, не включая минимальное/максимальное количество строк a/b, меньше max_n, это гарантирует возврат всех строк

Данные

dt <- data.table::as.data.table(structure(list(grp = c("a", "a", "a", "a", "a", "a", "a", "b", "b", "b", "b", "b", "b", "b", "b", "c", "c", "c", "c", "c"), a = c(1L, 2L, 2L, 5L, 8L, 8L, 9L, 1L, 2L, 3L, 4L, 6L, 7L, 8L, 9L, 1L, 3L, 5L, 6L, 7L), b = c(11L, 9L, 19L, 7L, 11L, 12L, 11L, 20L, 1L, 19L, 3L, 3L, 10L, 10L, 17L, 16L, 14L, 18L, 20L, 13L), id = c(14L, 13L, 17L, 6L, 2L, 19L, 7L, 1L, 16L, 3L, 11L, 15L, 18L, 4L, 10L, 12L, 20L, 9L, 5L, 8L)), row.names = c(NA, -20L), class = c("data.table", "data.frame")))

Хорошее решение, спасибо!

EnFiFa 12.07.2023 13:50

Добро пожаловать в SO, ЭнФиФа!

r2evans 12.07.2023 13:53

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