как найти верхние значения N по группам или внутри категории (по группам) в R.frame
мои извинения, если это дубликат. это похоже на вопрос, что SO давно бы ответил, но я довольно много искал и не мог найти ничего, что конкретно отвечало бы на это. есть много связанных вопросов, которые могут быть использованы для ответа на это, но я решил, что на него должен быть дан ответ официально.
это в ответ на этот вопрос, заданный в списке рассылки r-help .
вот много примеров того, как это сделать, используя sql
, поэтому я думаю, что легко преобразовать эти знания с sqldf
пакета R sqldf
.. но есть несколько способов сделать это с помощью R, и я хотел проверить, были ли у других идеи.
- Как вы кодируете функцию R так, чтобы она «знала», чтобы искать «данные» для переменных в других аргументах?
- определить параметр $ right с переменной в R
- Изменение имени переменной в цикле for с использованием R
- Определить все объекты данного classа для дальнейшей обработки
- Разделить вектор по его последовательностям
главный вопрос: используя пример mtcars
data.frame, как кто-то найдет верхнюю или нижнюю (максимальную или минимальную) N записей в указанной категории? результаты сверху или снизу N для каждой группы.
если вы откроете R и введите mtcars
вы получите mtcars
таблицу с 32 записями. когда сгруппированы цилиндрической колонной cyl
– здесь представлены три первые записи для каждого отдельного значения cyl
. обратите внимание, что в этом случае связи исключены, но было бы неплохо показать несколько способов лечения связей.
mpg cyl disp hp drat wt qsec vs am gear carb ranks Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1 2.0 Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2 1.0 Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 2.0 Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 3.0 Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 1.0 Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4 1.5 Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4 1.5 Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4 3.0
- Укажите количество последовательных значений
- Вычислить разницу между значениями в последовательных строках по группам
- Явное вызов return в функции или нет
- Как узнать R как язык программирования?
- Что делает функция invisible ()?
- Сплит-кадр данных по уровням фактора и именных кадров данных по этим уровням
- Удалите все знаки препинания, кроме апострофов в R
- Как создать пример данных из частных данных (заменяя имена переменных и уровни владельцами неинформативных мест)?
Это кажется более простым с использованием data.table
поскольку он выполняет сортировку при установке ключа.
Итак, если бы я должен был получить 3 лучших записи в сортировке (по возрастанию), тогда,
require(data.table) d <- data.table(mtcars, key="cyl") d[, head(.SD, 3), by=cyl]
Имеет ли это.
И если вы хотите по убыванию
d[, tail(.SD, 3), by=cyl] # Thanks @MatthewDowle
Изменить: для сортировки связей с использованием столбца mpg
:
d <- data.table(mtcars, key="cyl") d.out <- d[, .SD[mpg %in% head(sort(unique(mpg)), 3)], by=cyl] # cyl mpg disp hp drat wt qsec vs am gear carb rank # 1: 4 22.8 108.0 93 3.85 2.320 18.61 1 1 4 1 11 # 2: 4 22.8 140.8 95 3.92 3.150 22.90 1 0 4 2 1 # 3: 4 21.5 120.1 97 3.70 2.465 20.01 1 0 3 1 8 # 4: 4 21.4 121.0 109 4.11 2.780 18.60 1 1 4 2 6 # 5: 6 18.1 225.0 105 2.76 3.460 20.22 1 0 3 1 7 # 6: 6 19.2 167.6 123 3.92 3.440 18.30 1 0 4 4 1 # 7: 6 17.8 167.6 123 3.92 3.440 18.90 1 0 4 4 2 # 8: 8 14.3 360.0 245 3.21 3.570 15.84 0 0 3 4 7 # 9: 8 10.4 472.0 205 2.93 5.250 17.98 0 0 3 4 14 # 10: 8 10.4 460.0 215 3.00 5.424 17.82 0 0 3 4 5 # 11: 8 13.3 350.0 245 3.73 3.840 15.41 0 0 3 4 3 # and for last N elements, of course it is straightforward d.out <- d[, .SD[mpg %in% tail(sort(unique(mpg)), 3)], by=cyl]
Просто соберите что-нибудь (например, на галлон, вопрос на этом не ясен)
mt <- mtcars[order(mtcars$mpg), ]
затем используйте функцию by, чтобы получить верхние n строк в каждой группе
d <- by(mt, mt["cyl"], head, n=4)
Если вы хотите, чтобы результат был data.frame:
Reduce(rbind, d)
Изменить: Обработка связей сложнее, но если все связи желательны:
by(mt, mt["cyl"], function(x) x[rank(x$mpg) %in% sort(unique(rank(x$mpg)))[1:4], ])
Другой подход заключается в разрыве связей на основе другой информации, например,
mt <- mtcars[order(mtcars$mpg, mtcars$hp), ] by(mt, mt["cyl"], head, n=4)
dplyr
делает трюк
mtcars %>% arrange(desc(mpg)) %>% group_by(cyl) %>% slice(1:2) mpg cyl disp hp drat wt qsec vs am gear carb 1 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1 2 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1 3 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 5 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2 6 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Если бы была четвертая позиция для mtcars $ mpg, то это должно было вернуть все связи:
top_mpg <- mtcars[ mtcars$mpg >= mtcars$mpg[order(mtcars$mpg, decreasing=TRUE)][4] , ] > top_mpg mpg cyl disp hp drat wt qsec vs am gear carb Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1 Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2 Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1 Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Так как есть связь в 3-4 позиции, вы можете проверить ее, изменив 4 на 3, и она по-прежнему возвращает 4 элемента. Это логическое индексирование, и вам может потребоваться добавить предложение, которое удаляет NA или wrap, которые () вокруг логического выражения. Это не намного сложнее сделать это «by» cyl:
Reduce(rbind, by(mtcars, mtcars$cyl, function(d) d[ d$mpg >= d$mpg[order(d$mpg, decreasing=TRUE)][4] , ]) ) #------------- mpg cyl disp hp drat wt qsec vs am gear carb Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1 Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2 Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1 Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2 Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6 Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3 Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3 Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
Включение моего предложения в @Ista:
Reduce(rbind, by(mtcars, mtcars$cyl, function(d) d[ d$mpg <= sort( d$mpg )[3] , ]) )
Вы можете написать функцию, которая разбивает базу данных на коэффициент, заказывает другую желаемую переменную, извлекает количество строк, которые вы хотите в каждом коэффициенте (категории), и объединяете их в базу данных.
top<-function(x, num, c1,c2){ sorted<-x[with(x,order(x[,c1],x[,c2],decreasing=T)),] splits<-split(sorted,sorted[,c1]) df<-lapply(splits,head,num) do.call(rbind.data.frame,df)}
x - это кадр данных;
num - количество строк, которые вы хотели бы видеть;
c1 - номер столбца переменной, которую вы хотели бы разделить;
c2 - номер столбца переменной, которую вы хотели бы ранжировать или обрабатывать связи.
Используя данные mtcars, функция извлекает 3 самых тяжелых автомобиля (mtcars $ wt - 6- й столбец) в каждом classе цилиндров (mtcars $ cyl - 2- й столбец)
top(mtcars,3,2,6) mpg cyl disp hp drat wt qsec vs am gear carb 4.Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 4.Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2 4.Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2 6.Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 6.Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 6.Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 8.Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4 8.Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4 8.Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
частьtop(mtcars,3,2,6) mpg cyl disp hp drat wt qsec vs am gear carb 4.Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 4.Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2 4.Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2 6.Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 6.Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 6.Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 8.Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4 8.Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4 8.Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
Вы также можете легко получить самое легкое в classе, изменив голову в функции lapply на хвост ИЛИ, удалив аргумент убывающего = T в функции порядка, который вернет его по умолчанию, уменьшив = F.
Я предпочитаю решение @Ista, потому что вам не нужен дополнительный пакет и он прост.
Модификация решения data.table
также решает мою проблему и является более общей.
Мой data.frame
> str(df) 'data.frame': 579 obs. of 11 variables: $ trees : num 2000 5000 1000 2000 1000 1000 2000 5000 5000 1000 ... $ interDepth: num 2 3 5 2 3 4 4 2 3 5 ... $ minObs : num 6 4 1 4 10 6 10 10 6 6 ... $ shrinkage : num 0.01 0.001 0.01 0.005 0.01 0.01 0.001 0.005 0.005 0.001 ... $ G1 : num 0 2 2 2 2 2 8 8 8 8 ... $ G2 : logi FALSE FALSE FALSE FALSE FALSE FALSE ... $ qx : num 0.44 0.43 0.419 0.439 0.43 ... $ efet : num 43.1 40.6 39.9 39.2 38.6 ... $ prec : num 0.606 0.593 0.587 0.582 0.574 0.578 0.576 0.579 0.588 0.585 ... $ sens : num 0.575 0.57 0.573 0.575 0.587 0.574 0.576 0.566 0.542 0.545 ... $ acu : num 0.631 0.645 0.647 0.648 0.655 0.647 0.619 0.611 0.591 0.594 ...
Решение data.table
требует, order
i
выполнял эту работу:
> require(data.table) > dt1 <- data.table(df) > dt2 = dt1[order(-efet, G1, G2), head(.SD, 3), by = .(G1, G2)] > dt2 G1 G2 trees interDepth minObs shrinkage qx efet prec sens acu 1: 0 FALSE 2000 2 6 0.010 0.4395953 43.066 0.606 0.575 0.631 2: 0 FALSE 2000 5 1 0.005 0.4294718 37.554 0.583 0.548 0.607 3: 0 FALSE 5000 2 6 0.005 0.4395753 36.981 0.575 0.559 0.616 4: 2 FALSE 5000 3 4 0.001 0.4296346 40.624 0.593 0.570 0.645 5: 2 FALSE 1000 5 1 0.010 0.4186802 39.915 0.587 0.573 0.647 6: 2 FALSE 2000 2 4 0.005 0.4390503 39.164 0.582 0.575 0.648 7: 8 FALSE 2000 4 10 0.001 0.4511349 38.240 0.576 0.576 0.619 8: 8 FALSE 5000 2 10 0.005 0.4469665 38.064 0.579 0.566 0.611 9: 8 FALSE 5000 3 6 0.005 0.4426952 37.888 0.588 0.542 0.591 10: 2 TRUE 5000 3 4 0.001 0.3812878 21.057 0.510 0.479 0.615 11: 2 TRUE 2000 3 10 0.005 0.3790536 20.127 0.507 0.470 0.608 12: 2 TRUE 1000 5 4 0.001 0.3690911 18.981 0.500 0.475 0.611 13: 8 TRUE 5000 6 10 0.010 0.2865042 16.870 0.497 0.435 0.635 14: 0 TRUE 2000 6 4 0.010 0.3192862 9.779 0.460 0.433 0.621
По какой-то причине он не приказывает указать направление (вероятно, потому, что упорядочивает группы). Итак, другой заказ сделан.
> dt2[order(G1, G2)] G1 G2 trees interDepth minObs shrinkage qx efet prec sens acu 1: 0 FALSE 2000 2 6 0.010 0.4395953 43.066 0.606 0.575 0.631 2: 0 FALSE 2000 5 1 0.005 0.4294718 37.554 0.583 0.548 0.607 3: 0 FALSE 5000 2 6 0.005 0.4395753 36.981 0.575 0.559 0.616 4: 0 TRUE 2000 6 4 0.010 0.3192862 9.779 0.460 0.433 0.621 5: 2 FALSE 5000 3 4 0.001 0.4296346 40.624 0.593 0.570 0.645 6: 2 FALSE 1000 5 1 0.010 0.4186802 39.915 0.587 0.573 0.647 7: 2 FALSE 2000 2 4 0.005 0.4390503 39.164 0.582 0.575 0.648 8: 2 TRUE 5000 3 4 0.001 0.3812878 21.057 0.510 0.479 0.615 9: 2 TRUE 2000 3 10 0.005 0.3790536 20.127 0.507 0.470 0.608 10: 2 TRUE 1000 5 4 0.001 0.3690911 18.981 0.500 0.475 0.611 11: 8 FALSE 2000 4 10 0.001 0.4511349 38.240 0.576 0.576 0.619 12: 8 FALSE 5000 2 10 0.005 0.4469665 38.064 0.579 0.566 0.611 13: 8 FALSE 5000 3 6 0.005 0.4426952 37.888 0.588 0.542 0.591 14: 8 TRUE 5000 6 10 0.010 0.2865042 16.870 0.497 0.435 0.635
# start with the mtcars data frame (included with your installation of R) mtcars # pick your 'group by' variable gbv <- 'cyl' # IMPORTANT NOTE: you can only include one group by variable here # ..if you need more, the `order` function below will need # one per inputted parameter: order( x$cyl , x$am ) # choose whether you want to find the minimum or maximum find.maximum <- FALSE # create a simple data frame with only two columns x <- mtcars # order it based on x <- x[ order( x[ , gbv ] , decreasing = find.maximum ) , ] # figure out the ranks of each miles-per-gallon, within cyl columns if ( find.maximum ){ # note the negative sign (which changes the order of mpg) # *and* the `rev` function, which flips the order of the `tapply` result x$ranks <- unlist( rev( tapply( -x$mpg , x[ , gbv ] , rank ) ) ) } else { x$ranks <- unlist( tapply( x$mpg , x[ , gbv ] , rank ) ) } # now just subset it based on the rank column result <- x[ x$ranks <= 3 , ] # look at your results result # done! # but note only *two* values where cyl == 4 were kept, # because there was a tie for third smallest, and the `rank` function gave both '3.5' x[ x$ranks == 3.5 , ] # ..if you instead wanted to keep all ties, you could change the # tie-breaking behavior of the `rank` function. # using the `min` *includes* all ties. using `max` would *exclude* all ties if ( find.maximum ){ # note the negative sign (which changes the order of mpg) # *and* the `rev` function, which flips the order of the `tapply` result x$ranks <- unlist( rev( tapply( -x$mpg , x[ , gbv ] , rank , ties.method = 'min' ) ) ) } else { x$ranks <- unlist( tapply( x$mpg , x[ , gbv ] , rank , ties.method = 'min' ) ) } # and there are even more options.. # see ?rank for more methods # now just subset it based on the rank column result <- x[ x$ranks <= 3 , ] # look at your results result # and notice *both* cyl == 4 and ranks == 3 were included in your results # because of the tie-breaking behavior chosen.