Извлечь строку, соответствующую минимальному значению переменной по группе

Я хочу (1) группировать данные по одной переменной ( State ), (2) внутри каждой группы найти строку минимального значения другой переменной ( Employees ) и (3) извлечь всю строку.

(1) и (2) являются легкими однострочными, и мне кажется, что (3) тоже должно быть, но я не могу это получить.

Вот пример набора данных:

 > data State Company Employees 1 AK A 82 2 AK B 104 3 AK C 37 4 AK D 24 5 RI E 19 6 RI F 118 7 RI G 88 8 RI H 42 data <- structure(list(State = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L), .Label = c("AK", "RI"), class = "factor"), Company = structure(1:8, .Label = c("A", "B", "C", "D", "E", "F", "G", "H"), class = "factor"), Employees = c(82L, 104L, 37L, 24L, 19L, 118L, 88L, 42L)), .Names = c("State", "Company", "Employees"), class = "data.frame", row.names = c(NA, -8L)) 

Вычислить min по группе легко, используя aggregate :

 > aggregate(Employees ~ State, data, function(x) min(x)) State Employees 1 AK 24 2 RI 19 

… или data.table :

 > library(data.table) > DT  DT[ , list(Employees = min(Employees)), by = State] State Employees 1: AK 24 2: RI 19 

Но как мне извлечь всю строку, соответствующую этим min значениям, т. Е. Также в том числе и Company в результате?

Чуть более элегантный:

 library(data.table) DT[ , .SD[which.min(Employees)], by = State] State Company Employees 1: AK D 24 2: RI E 19 

Slighly менее изящный, чем использование .SD , но немного быстрее (для данных со многими группами):

 DT[DT[ , .I[which.min(Employees)], by = State]$V1] 

Кроме того, просто замените выражение which.min(Employees) Employees == min(Employees) , если ваш dataset имеет несколько одинаковых минимальных значений, и вы хотите подмножить все из них.

См. Также Подмножество по группам с data.table .

Решение dplyr :

 library(dplyr) data %>% group_by(State) %>% slice(which.min(Employees)) 

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

Либо используя data.table

 library(data.table) unique(setDT(data)[order(Employees)], by = "State") # State Company Employees # 1: RI E 19 # 2: AK D 24 

В качестве альтернативы мы могли бы также сначала заказать, а затем подмножество .SD . Обе эти операции были оптимизированы в версиях resent. Табличные версии и order по-видимому, запускают data.table:::forderv , тогда как .SD[1L] запускает Gforce

 setDT(data)[order(Employees), .SD[1L], by = State, verbose = TRUE] # <- Added verbose # order optimisation is on, i changed from 'order(...)' to 'forder(DT, ...)'. # i clause present and columns used in by detected, only these subset: State # Finding groups using forderv ... 0 sec # Finding group sizes from the positions (can be avoided to save RAM) ... 0 sec # Getting back original order ... 0 sec # lapply optimization changed j from '.SD[1L]' to 'list(Company[1L], Employees[1L])' # GForce optimized j to 'list(`g[`(Company, 1L), `g[`(Employees, 1L))' # Making each group and running j (GForce TRUE) ... 0 secs # State Company Employees # 1: RI E 19 # 2: AK D 24 

Или dplyr

 library(dplyr) data %>% arrange(Employees) %>% distinct(State, .keep_all = TRUE) # State Company Employees # 1 RI E 19 # 2 AK D 24 

Еще одна интересная идея, заимствованная из замечательного ответа @Khashaas (с небольшой модификацией в форме mult = "first" для обработки нескольких совпадений) заключается в том, чтобы сначала найти минимум для каждой группы, а затем выполнить двоичное соединение назад. Преимуществом этого является как использование функции g.dables data.tables (которая пропускает служебные данные оценки), так и функцию двоичного соединения

 tmp <- setDT(data)[, .(Employees = min(Employees)), by = State] data[tmp, on = .(State, Employees), mult = "first"] # State Company Employees # 1: AK D 24 # 2: RI E 19 

Некоторые контрольные показатели

 library(data.table) library(dplyr) library(plyr) library(stringi) library(microbenchmark) set.seed(123) N <- 1e6 data <- data.frame(State = stri_rand_strings(N, 2, '[AZ]'), Employees = sample(N*10, N, replace = TRUE)) DT <- copy(data) setDT(DT) DT2 <- copy(DT) str(DT) str(DT2) microbenchmark("(data.table) .SD[which.min]: " = DT[ , .SD[which.min(Employees)], by = State], "(data.table) .I[which.min]: " = DT[DT[ , .I[which.min(Employees)], by = State]$V1], "(data.table) order/unique: " = unique(DT[order(Employees)], by = "State"), "(data.table) order/.SD[1L]: " = DT[order(Employees), .SD[1L], by = State], "(data.table) self join (on):" = { tmp <- DT[, .(Employees = min(Employees)), by = State] DT[tmp, on = .(State, Employees), mult = "first"]}, "(data.table) self join (setkey):" = { tmp <- DT2[, .(Employees = min(Employees)), by = State] setkey(tmp, State, Employees) setkey(DT2, State, Employees) DT2[tmp, mult = "first"]}, "(dplyr) slice(which.min): " = data %>% group_by(State) %>% slice(which.min(Employees)), "(dplyr) arrange/distinct: " = data %>% arrange(Employees) %>% distinct(State, .keep_all = TRUE), "(dplyr) arrange/group_by/slice: " = data %>% arrange(Employees) %>% group_by(State) %>% slice(1), "(plyr) ddply/which.min: " = ddply(data, .(State), function(x) x[which.min(x$Employees),]), "(base) by: " = do.call(rbind, by(data, data$State, function(x) x[which.min(x$Employees), ]))) # Unit: milliseconds # expr min lq mean median uq max neval cld # (data.table) .SD[which.min]: 119.66086 125.49202 145.57369 129.61172 152.02872 267.5713 100 d # (data.table) .I[which.min]: 12.84948 13.66673 19.51432 13.97584 15.17900 109.5438 100 a # (data.table) order/unique: 52.91915 54.63989 64.39212 59.15254 61.71133 177.1248 100 b # (data.table) order/.SD[1L]: 51.41872 53.22794 58.17123 55.00228 59.00966 145.0341 100 b # (data.table) self join (on): 44.37256 45.67364 50.32378 46.24578 50.69411 137.4724 100 b # (data.table) self join (setkey): 14.30543 15.28924 18.63739 15.58667 16.01017 106.0069 100 a # (dplyr) slice(which.min): 82.60453 83.64146 94.06307 84.82078 90.09772 186.0848 100 c # (dplyr) arrange/distinct: 344.81603 360.09167 385.52661 379.55676 395.29463 491.3893 100 e # (dplyr) arrange/group_by/slice: 367.95924 383.52719 414.99081 397.93646 425.92478 557.9553 100 f # (plyr) ddply/which.min: 506.55354 530.22569 568.99493 552.65068 601.04582 727.9248 100 g # (base) by: 1220.38286 1291.70601 1340.56985 1344.86291 1382.38067 1512.5377 100 h 

Базовая функция часто используется для работы с блочными данными в data.frames. Например

 by(data, data$State, function(x) x[which.min(x$Employees), ] ) 

Он возвращает данные в списке, но вы можете

 do.call(rbind, by(data, data$State, function(x) x[which.min(x$Employees), ] )) 

Исправленное решение plyr :

 ddply(df, .(State), function(x) x[which.min(x$Employees),]) # State Company Employees # 1 AK D 24 # 2 RI E 19 

благодаря @ joel.wilson

  • Получение предупреждения: «« newdata »имеет 1 строку, но найденные переменные содержат 32 строки» в файле outlookа.lm
  • Последовательность повторяющихся значений в R
  • Самый простой способ сгруппировать барплот
  • Как отсортировать один вектор, основанный на значениях другого
  • Как вы можете прочитать CSV-файл в R с различным количеством столбцов
  • Частичное совпадение аргумента функции
  • Как эффективно рассчитать расстояние между парой координат с помощью data.table: =
  • Булевы операторы && и ||
  • Карта Choropleth в ggplot с полигонами с отверстиями
  • Развернуть диапазоны, определенные столбцами «от» и «до»
  • Объединение функций paste () и expression () в метках графика
  • Давайте будем гением компьютера.