如何使用 Dplyr 的 Summarize 和 which() 来查找 min/max 值
How to use Dplyr's Summarize and which() to lookup min/max values
我有以下数据:
Name <- c("Sam", "Sarah", "Jim", "Fred", "James", "Sally", "Andrew", "John", "Mairin", "Kate", "Sasha", "Ray", "Ed")
Age <- c(22,12,31,35,58,82,17,34,12,24,44,67,43)
Group <- c("A", "B", "B", "B", "B", "C", "C", "D", "D", "D", "D", "D", "D")
data <- data.frame(Name, Age, Group)
我想使用 dplyr
(1) 按 "Group" 对数据进行分组
(2) 显示每个组内的最小和最大年龄
(3) 显示最小和最大年龄的人的名字
以下代码执行此操作:
data %>% group_by(Group) %>%
summarize(minAge = min(Age), minAgeName = Name[which(Age == min(Age))],
maxAge = max(Age), maxAgeName = Name[which(Age == max(Age))])
哪个效果好:
Group minAge minAgeName maxAge maxAgeName
1 A 22 Sam 22 Sam
2 B 12 Sarah 58 James
3 C 17 Andrew 82 Sally
4 D 12 Mairin 67 Ray
但是,如果有多个最小值或最大值,我会遇到问题:
Name <- c("Sam", "Sarah", "Jim", "Fred", "James", "Sally", "Andrew", "John", "Mairin", "Kate", "Sasha", "Ray", "Ed")
Age <- c(22,31,31,35,58,82,17,34,12,24,44,67,43)
Group <- c("A", "B", "B", "B", "B", "C", "C", "D", "D", "D", "D", "D", "D")
data <- data.frame(Name, Age, Group)
> data %>% group_by(Group) %>%
+ summarize(minAge = min(Age), minAgeName = Name[which(Age == min(Age))],
+ maxAge = max(Age), maxAgeName = Name[which(Age == max(Age))])
Error: expecting a single value
我正在寻找两个解决方案:
(1) 显示哪个最小或最大名称无关紧要,只显示那个(即找到的第一个值)
(2) 如果有 "ties",则显示所有最小值和最大值
如果不清楚,请告诉我,在此先感谢!
您可以使用 which.min
和 which.max
来获取第一个值。
data %>% group_by(Group) %>%
summarize(minAge = min(Age), minAgeName = Name[which.min(Age)],
maxAge = max(Age), maxAgeName = Name[which.max(Age)])
要获取所有值,请使用例如使用适当的 collapse
参数粘贴。
data %>% group_by(Group) %>%
summarize(minAge = min(Age), minAgeName = paste(Name[which(Age == min(Age))], collapse = ", "),
maxAge = max(Age), maxAgeName = paste(Name[which(Age == max(Age))], collapse = ", "))
我实际上建议将您的数据保存为 "long" 格式。以下是我的处理方法:
library(dplyr)
有关系时保留所有值:
data %>%
group_by(Group) %>%
arrange(Age) %>% ## optional
filter(Age %in% range(Age))
# Source: local data frame [8 x 3]
# Groups: Group
#
# Name Age Group
# 1 Sam 22 A
# 2 Sarah 31 B
# 3 Jim 31 B
# 4 James 58 B
# 5 Andrew 17 C
# 6 Sally 82 C
# 7 Mairin 12 D
# 8 Ray 67 D
有联系时只保留一个值:
data %>%
group_by(Group) %>%
arrange(Age) %>%
slice(if (length(Age) == 1) 1 else c(1, n())) ## maybe overkill?
# Source: local data frame [7 x 3]
# Groups: Group
#
# Name Age Group
# 1 Sam 22 A
# 2 Sarah 31 B
# 3 James 58 B
# 4 Andrew 17 C
# 5 Sally 82 C
# 6 Mairin 12 D
# 7 Ray 67 D
如果你真的想要一个 "wide" 数据集,基本概念是 gather
和 spread
数据,使用 "tidyr":
library(dplyr)
library(tidyr)
data %>%
group_by(Group) %>%
arrange(Age) %>%
slice(c(1, n())) %>%
mutate(minmax = c("min", "max")) %>%
gather(var, val, Name:Age) %>%
unite(key, minmax, var) %>%
spread(key, val)
# Source: local data frame [4 x 5]
#
# Group max_Age max_Name min_Age min_Name
# 1 A 22 Sam 22 Sam
# 2 B 58 James 31 Sarah
# 3 C 82 Sally 17 Andrew
# 4 D 67 Ray 12 Mairin
虽然不清楚您想要领带的宽度形式。
这里有一些 data.table
方法,第一个是从@akrun 那里借来的:
setDT(data)
# show one, wide format
data[,c(min=.SD[which.min(Age)],max=.SD[which.max(Age)]),by=Group]
# Group min.Name min.Age max.Name max.Age
# 1: A Sam 22 Sam 22
# 2: B Sarah 31 James 58
# 3: C Andrew 17 Sally 82
# 4: D Mairin 12 Ray 67
# show all, long format
data[,{
mina=min(Age)
maxa=max(Age)
rbind(
data.table(minmax="min",Age=mina,Name=Name[which(Age==mina)]),
data.table(minmax="max",Age=maxa,Name=Name[which(Age==maxa)])
)},by=Group]
# Group minmax Age Name
# 1: A min 22 Sam
# 2: A max 22 Sam
# 3: B min 31 Sarah
# 4: B min 31 Jim
# 5: B max 58 James
# 6: C min 17 Andrew
# 7: C max 82 Sally
# 8: D min 12 Mairin
# 9: D max 67 Ray
我认为长格式是最好的,因为它允许您使用 minmax
进行过滤,但是代码很复杂且效率低下。
这里有一些可以说不太好的方法:
# show all, wide format (with a list column)
data[,{
mina=min(Age)
maxa=max(Age)
list(
minAge=mina,
maxAge=maxa,
minNames=list(Name[Age==mina]),
maxNames=list(Name[Age==maxa]))
},by=Group]
# Group minAge maxAge minNames maxNames
# 1: A 22 22 Sam Sam
# 2: B 31 58 Sarah,Jim James
# 3: C 17 82 Andrew Sally
# 4: D 12 67 Mairin Ray
# show all, wide format (with a string column)
# (just look at @shadow's answer)
我有以下数据:
Name <- c("Sam", "Sarah", "Jim", "Fred", "James", "Sally", "Andrew", "John", "Mairin", "Kate", "Sasha", "Ray", "Ed")
Age <- c(22,12,31,35,58,82,17,34,12,24,44,67,43)
Group <- c("A", "B", "B", "B", "B", "C", "C", "D", "D", "D", "D", "D", "D")
data <- data.frame(Name, Age, Group)
我想使用 dplyr
(1) 按 "Group" 对数据进行分组 (2) 显示每个组内的最小和最大年龄 (3) 显示最小和最大年龄的人的名字
以下代码执行此操作:
data %>% group_by(Group) %>%
summarize(minAge = min(Age), minAgeName = Name[which(Age == min(Age))],
maxAge = max(Age), maxAgeName = Name[which(Age == max(Age))])
哪个效果好:
Group minAge minAgeName maxAge maxAgeName
1 A 22 Sam 22 Sam
2 B 12 Sarah 58 James
3 C 17 Andrew 82 Sally
4 D 12 Mairin 67 Ray
但是,如果有多个最小值或最大值,我会遇到问题:
Name <- c("Sam", "Sarah", "Jim", "Fred", "James", "Sally", "Andrew", "John", "Mairin", "Kate", "Sasha", "Ray", "Ed")
Age <- c(22,31,31,35,58,82,17,34,12,24,44,67,43)
Group <- c("A", "B", "B", "B", "B", "C", "C", "D", "D", "D", "D", "D", "D")
data <- data.frame(Name, Age, Group)
> data %>% group_by(Group) %>%
+ summarize(minAge = min(Age), minAgeName = Name[which(Age == min(Age))],
+ maxAge = max(Age), maxAgeName = Name[which(Age == max(Age))])
Error: expecting a single value
我正在寻找两个解决方案:
(1) 显示哪个最小或最大名称无关紧要,只显示那个(即找到的第一个值) (2) 如果有 "ties",则显示所有最小值和最大值
如果不清楚,请告诉我,在此先感谢!
您可以使用 which.min
和 which.max
来获取第一个值。
data %>% group_by(Group) %>%
summarize(minAge = min(Age), minAgeName = Name[which.min(Age)],
maxAge = max(Age), maxAgeName = Name[which.max(Age)])
要获取所有值,请使用例如使用适当的 collapse
参数粘贴。
data %>% group_by(Group) %>%
summarize(minAge = min(Age), minAgeName = paste(Name[which(Age == min(Age))], collapse = ", "),
maxAge = max(Age), maxAgeName = paste(Name[which(Age == max(Age))], collapse = ", "))
我实际上建议将您的数据保存为 "long" 格式。以下是我的处理方法:
library(dplyr)
有关系时保留所有值:
data %>%
group_by(Group) %>%
arrange(Age) %>% ## optional
filter(Age %in% range(Age))
# Source: local data frame [8 x 3]
# Groups: Group
#
# Name Age Group
# 1 Sam 22 A
# 2 Sarah 31 B
# 3 Jim 31 B
# 4 James 58 B
# 5 Andrew 17 C
# 6 Sally 82 C
# 7 Mairin 12 D
# 8 Ray 67 D
有联系时只保留一个值:
data %>%
group_by(Group) %>%
arrange(Age) %>%
slice(if (length(Age) == 1) 1 else c(1, n())) ## maybe overkill?
# Source: local data frame [7 x 3]
# Groups: Group
#
# Name Age Group
# 1 Sam 22 A
# 2 Sarah 31 B
# 3 James 58 B
# 4 Andrew 17 C
# 5 Sally 82 C
# 6 Mairin 12 D
# 7 Ray 67 D
如果你真的想要一个 "wide" 数据集,基本概念是 gather
和 spread
数据,使用 "tidyr":
library(dplyr)
library(tidyr)
data %>%
group_by(Group) %>%
arrange(Age) %>%
slice(c(1, n())) %>%
mutate(minmax = c("min", "max")) %>%
gather(var, val, Name:Age) %>%
unite(key, minmax, var) %>%
spread(key, val)
# Source: local data frame [4 x 5]
#
# Group max_Age max_Name min_Age min_Name
# 1 A 22 Sam 22 Sam
# 2 B 58 James 31 Sarah
# 3 C 82 Sally 17 Andrew
# 4 D 67 Ray 12 Mairin
虽然不清楚您想要领带的宽度形式。
这里有一些 data.table
方法,第一个是从@akrun 那里借来的:
setDT(data)
# show one, wide format
data[,c(min=.SD[which.min(Age)],max=.SD[which.max(Age)]),by=Group]
# Group min.Name min.Age max.Name max.Age
# 1: A Sam 22 Sam 22
# 2: B Sarah 31 James 58
# 3: C Andrew 17 Sally 82
# 4: D Mairin 12 Ray 67
# show all, long format
data[,{
mina=min(Age)
maxa=max(Age)
rbind(
data.table(minmax="min",Age=mina,Name=Name[which(Age==mina)]),
data.table(minmax="max",Age=maxa,Name=Name[which(Age==maxa)])
)},by=Group]
# Group minmax Age Name
# 1: A min 22 Sam
# 2: A max 22 Sam
# 3: B min 31 Sarah
# 4: B min 31 Jim
# 5: B max 58 James
# 6: C min 17 Andrew
# 7: C max 82 Sally
# 8: D min 12 Mairin
# 9: D max 67 Ray
我认为长格式是最好的,因为它允许您使用 minmax
进行过滤,但是代码很复杂且效率低下。
这里有一些可以说不太好的方法:
# show all, wide format (with a list column)
data[,{
mina=min(Age)
maxa=max(Age)
list(
minAge=mina,
maxAge=maxa,
minNames=list(Name[Age==mina]),
maxNames=list(Name[Age==maxa]))
},by=Group]
# Group minAge maxAge minNames maxNames
# 1: A 22 22 Sam Sam
# 2: B 31 58 Sarah,Jim James
# 3: C 17 82 Andrew Sally
# 4: D 12 67 Mairin Ray
# show all, wide format (with a string column)
# (just look at @shadow's answer)