2013-02-10 2 views
28

мои извинения, если это дубликат. это похоже на вопрос, что SO давно бы ответил, но я довольно много искал и не мог найти ничего, что конкретно отвечало бы на это. есть много связанных вопросов, которые могут быть использованы для ответа на это, но я решил, что на него должен быть дан ответ официально., как найти верхние значения N по группам или внутри категории (по группам) в R data.frame

это в ответ на this question asked on the r-help mailing list.

here are lots of examples о том, как это сделать с помощью sql, поэтому я предполагаю, что это легко преобразовать эти знания по сравнению с использованием R sqldf пакета .. но есть несколько способов сделать это с помощью R, и я хотел, чтобы проверить, если другие имели идеи.

Основной вопрос: используя пример mtcars data.frame, как кто-то найдет верхнюю или нижнюю (максимальную или минимальную) N записей в указанной категории? результаты сверху или снизу N для каждой группы.

Если вы откроете R и введите 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 
+0

В случае, если нужно, чтобы выбрать различные 'k' записей в группе, этот вопрос может помочь: http://stackoverflow.com/q/33988831/1840471 –

ответ

-1
# 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. 
+0

@Arun .. нет другого выбора? :) ps thanx для вашего удивительного ответа –

+1

Это сложная задача для такой простой задачи! – Ista

+0

@ Arun Я проголосовал за то, что это кажется слишком сложным, о чем я жаловался в своем комментарии выше. Может быть, я просто немного расстроен, потратив часы на лопатку ... – Ista

31

Это кажется более простым использованием 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 

Edit: Для разберется привязывает используя 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] 
+1

Привет. Я не следую за тем, что делает 'head (seq (.I))' inside' .SD [...] '. Почему бы не 'head (.SD, 3)'? Или 'd [,. SD [head (order (mpg))], by = cyl]'. Ключ 'd' - это один столбец (' cyl'), предполагалось ли включать в него ключ «mpg»? –

+0

@MatthewDowle, :) целью было ваше первое предложение 'head (.SD, 3)'. Мне кажется, что «голова» прямолинейна! Я отредактирую его. – Arun

+1

Хорошо, +1. Редко я нахожу что-нибудь, чтобы прокомментировать эти дни! –

17

Просто сортировать по любой (миль на галлон, например, вопрос в том, не ясно)

mt <- mtcars[order(mtcars$mpg), ] 

затем использовать функцией, чтобы получить верхние п строк в каждой группе

d <- by(mt, mt["cyl"], head, n=4) 

Если вы хотите результат быть data.frame:

Reduce(rbind, d) 

Edit: Обработка связи сложнее, но если требуются все связи:

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) 
+0

@ Арун Ум, что? Существует галстук, когда цил == 8 тоже ... который, по-видимому, игнорирует решение data.table. Используя мы можем сохранить оба совпадения в обоих случаях с помощью (mtcars, mtcars ["cyl"], function (x) x [rank (x $ mpg) Ista

+0

+1 для уменьшения (rbind, by (...)) –

+0

Исправлена ​​благодарность за предложение. – Ista

4

Если бы галстук на четвертой позиции по mtcars $ миль на галлон, то это должно вернуть все связи:

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, которые() вокруг логического выражения. Это не намного сложнее сделать это «по» цилу:

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] , ])) 
+0

Не знаю, что вы подразумеваете, не делая этого, если вы не знаете перед собой. Он вернет все строки с значениями mpg, равными или превышающими четвертое по величине значение. Опять же, если вы выбрали третий по величине в качестве цели, вы все равно получите 4 предмета в классе с четырьмя цилиндрами. Я думал, что это была одна из целей Энтони –

+0

Как я понял, задачи, требуемые, чтобы _ был правильный ответ для одного из них, связанного с галсами. –

+0

Мой код делает то, что вы, кажется, запрашиваете. –

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)} 

х является dataframe;

номер - количество номеров, которые вы хотели бы видеть;

c1 это столбец номер переменной вы хотели бы расщепляется;

c2 это столбец номер переменных вы хотели бы ранжировать по или обрабатывать связи.

Используя данные mtcars, функция извлекает легких автомобилей (mtcars $ мас является й столбец) в каждом классе цилиндра (mtcars $ цил это столбец й)

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 

Вы также можете легко получить самые легкие в классе, изменяя голову в lapply функции хвоста или удаления убывающего = T аргумента в порядке фунца ион, который вернет его по умолчанию, уменьшив = F.

0

Я предпочитаю решение @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 
2

dplyr делает трюк

mtcars %>% 
arrange(desc(mpg)) %>% 
group_by(cyl) %>% slice(1:2) 


mpg cyl disp hp drat wt qsec vs am gear carb 
    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 
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 
+0

Если пользователь заинтересован в результате, подобном SQL, то этот результат dplyr - это способ пойти –

+0

hey Azam, вы все еще активны здесь для последующего вопроса? Я использую этот ответ для чего-то – Mark

Смежные вопросы