У меня есть набор данных с некоторыми столбцами и переменной группировки. Я хочу уменьшить набор данных на переменную группировки, 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.
Код сейчас слишком громоздкий
Да вы же видите, что я сортирую данные по переменным! Поэтому, когда я выбираю первый и последний индекс tmp, я в основном выбираю самое низкое и самое высокое значение комбинации c("a", "b"). Имеет смысл?
Хорошо. Итак, вы хотите уменьшить набор данных, но хотите, чтобы диапазон остался прежним? Как вы выбираете «самый высокий» и «самый низкий» в двух группах? а=5,б=7 и а=6,б=5. Что будет максимальным в этом случае? @ЭнФиФа
Привет, ЭнФиФа! Добро пожаловать в StackOverflow!
Мне было интересно, не могли бы вы уточнить для меня, когда вы говорите, что хотите получить самые низкие и самые высокие значения a и b, вы (к сожалению) дали одно и то же имя переменным столбца группы и столбцам, поэтому трудно знать, на кого вы ссылаетесь
Мне было интересно, не могли бы вы уточнить для меня, когда вы говорите, что хотите получить самые низкие и самые высокие значения a и b, вы (к сожалению) дали одно и то же имя переменным столбца группы и столбцам, поэтому трудно знать, на кого вы ссылаетесь
@Shibaprasadb Ну, прямо сейчас, поскольку я сортирую по c («a», «b»), (5, 7) будет меньше, чем (6, 5).
@Shibaprasadb Ну, прямо сейчас, поскольку я сортирую по c («a», «b»), (5, 7) будет меньше, чем (6, 5).
Когда вы говорите, что хотите сохранить распределение других столбцов, я предполагаю, что вы ссылаетесь на эту часть своего кода as.integer(seq(1, n_tmp, length.out=max_n)) правильно? Вы не хотите брать первые 6 рядов, как в seq_len(max_n), верно?
Когда вы говорите, что хотите сохранить распределение других столбцов, я предполагаю, что вы ссылаетесь на эту часть своего кода as.integer(seq(1, n_tmp, length.out=max_n)) правильно? Вы не хотите брать первые 6 рядов, как в seq_len(max_n), верно?
Я не знаю, слежу ли я за EnFiFa. Возможно, вы могли бы переименовать группы или столбцы, чтобы их было легче различать.
Я не знаю, слежу ли я за EnFiFa. Возможно, вы могли бы переименовать группы или столбцы, чтобы их было легче различать.
@TimTeaFan точно!
@TimTeaFan точно!
@Отметьте, что именно вы имеете в виду, «присвоив одно и то же имя переменным столбца группы и столбцам»?
@Отметьте, что именно вы имеете в виду, «присвоив одно и то же имя переменным столбца группы и столбцам»?
я имею в виду, что когда вы говорите a и b, это может относиться к группам или столбцам.
я имею в виду, что когда вы говорите a и b, это может относиться к группам или столбцам.





Вы можете сделать что-то вроде этого
Во-первых, давайте определим строки, которые нужно сохранить.
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, но спасибо!
Вы можете сделать что-то вроде этого
Во-первых, давайте определим строки, которые нужно сохранить.
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, но спасибо!
Чтобы сохранить минимальную (из 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")))
Хорошее решение, спасибо!
Добро пожаловать в SO, ЭнФиФа!
Чтобы сохранить минимальную (из 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")))
Хорошее решение, спасибо!
Добро пожаловать в SO, ЭнФиФа!
Что вы подразумеваете под сохранением распределения других столбцов? Можете ли вы уточнить немного?