0 Câu hỏi: trợ giúp nhóm dplyr, cách chọn một giá trị từ một cột khác dựa trên giá trị tối thiểu của một [trùng lặp] khác

câu hỏi được tạo ra tại Wed, May 8, 2019 12:00 AM

Tôi có dữ liệu sau:

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)

Và tôi muốn sử dụng dplyr để

(1) nhóm dữ liệu theo "Nhóm" (2) hiển thị Tuổi tối thiểu và tối đa trong mỗi Nhóm (3) hiển thị Tên của người có độ tuổi tối thiểu và tối đa

Đoạn mã sau thực hiện việc này:

data %>% group_by(Group) %>%
     summarize(minAge = min(Age), minAgeName = Name[which(Age == min(Age))], 
               maxAge = max(Age), maxAgeName = Name[which(Age == max(Age))])

Cái nào hoạt động tốt:

  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

Tuy nhiên, tôi gặp sự cố nếu có nhiều giá trị tối thiểu hoặc tối đa:

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

Tôi đang tìm kiếm hai giải pháp:

(1) trong đó không quan trọng tên hiển thị tối thiểu hoặc tối đa, chỉ tên đó được hiển thị (nghĩa là, giá trị đầu tiên được tìm thấy) (2) trong đó nếu có "mối quan hệ" thì tất cả các giá trị tối thiểu và giá trị tối đa được hiển thị

Vui lòng cho tôi biết nếu điều này không rõ ràng và cảm ơn trước!

    
15
  1. which.minwhich.max lấy giá trị đầu tiên.
    2015-05-12 16: 25: 42Z
  2. Điều đó rất tốt cho giải pháp đầu tiên, cảm ơn bạn!
    2015-05-12 16: 28: 20Z
  3. Sử dụng data.table. setDT(data)[, c(.SD[which.min(Age)], .SD[which.max(Age)]), Group] và thay đổi tên phù hợp
    2015-05-12 16: 31: 41Z
  4. @ akrun one .SD: setDT(data)[, .SD[c(which.min(Age),which.max(Age))], Group]. câu trả lời của nó).
    2015-05-12 16: 36: 43Z
  5. @ Frank Đó là một cách tốt khi sử dụng setDT(data)[data[, .I[c(which.min(Age),which.max(Age))], Group]$V1], nhưng giải pháp của bạn ở dạng dài phải không.
    2015-05-12 16: 37: 43Z
3 Câu trả lời                              3                         

Tôi thực sự khuyên bạn nên giữ dữ liệu của mình ở định dạng "dài". Đây là cách tôi sẽ tiếp cận điều này:

c

Giữ tất cả các giá trị khi có quan hệ:

library(dplyr)

Chỉ giữ một giá trị khi có quan hệ:

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

Nếu bạn thực sự muốn có bộ dữ liệu "rộng", khái niệm cơ bản sẽ là

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
gather dữ liệu, sử dụng "tidyr": spread

Mặc dù hình thức rộng mà bạn muốn có quan hệ không rõ ràng.

    
11
2015-05-12 16: 40: 20Z
  1. Hơi khó đọc (và có thể hoạt động với) hơn một hàng trên mỗi nhóm với các cột tối đa và tối thiểu như trong OP.
    2015-05-12 16: 35: 58Z
  2. @ Frank, bạn sẽ giải quyết nhiều trường hợp như thế nào? Dán với nhau có vẻ khó hơn với tôi.
    2015-05-12 16: 36: 36Z
  3. @ Frank, đi trước bạn: -)
    2015-05-12 16: 41: 12Z
  4. @ Frank, ý tưởng minmax đã được nhúng trong câu trả lời cuối cùng của tôi. Chỉ cần dừng lại ở dòng
    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
    
    . Khi có bội số, bạn có thể sử dụng mutatedense_rank.
    2015-05-12 17: 26: 52Z
  5. Rất cám ơn! Tôi đặc biệt thích sự đơn giản của bộ lọc% trong giải pháp phạm vi%.
    2015-05-12 17: 52: 16Z

Bạn có thể sử dụng factorwhich.min để nhận giá trị đầu tiên.

which.max

Để nhận tất cả các giá trị, hãy sử dụng, ví dụ: dán với một đối số

data %>% group_by(Group) %>%
  summarize(minAge = min(Age), minAgeName = Name[which.min(Age)], 
            maxAge = max(Age), maxAgeName = Name[which.max(Age)])
thích hợp. collapse     
16
2015-05-12 16: 27: 39Z
  1. Cảm ơn một lần nữa - đây chính xác là những gì tôi đang tìm kiếm. Tôi không rõ về định dạng rộng mà tôi muốn dữ liệu. Điều này hoạt động tốt với dữ liệu "thử nghiệm" tôi đã đăng, nhưng trong một ví dụ bạn có 100 mối quan hệ (như một ví dụ cực đoan), thu gọn mọi thứ thành một hàng trở nên khó sử dụng Nhưng đó là lỗi của tôi vì đã không giải thích, không phải của bạn - giải pháp này hoạt động rất tốt.
    2015-05-12 17: 54: 20Z

Dưới đây là một số cách tiếp cận

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 = ", "))
, phương pháp đầu tiên được mượn từ @akrun: data.table

Tôi nghĩ định dạng dài là tốt nhất, vì nó cho phép bạn lọc với

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    
, nhưng mã bị tra tấn và không hiệu quả.

Dưới đây là một số cách ít được cho là tốt hơn:

minmax     
3
2015-05-12 17: 44: 11Z
  1. Đối với bộ mã thứ hai, bạn đã sử dụng dữ liệu ví dụ thứ 2 (tôi nhận được 8 hàng làm đầu ra)
    2015-05-12 17: 38: 39Z
  2. @ akrun Bắt tốt. Tôi đã chuyển sang mã ban đầu, không hiệu quả, tạo ra chín hàng. Sự thay thế duy nhất tôi có thể nghĩ đến là sự lựa chọn không hiệu quả tương đương với việc đặt
    # 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)
    
    trước đó trong mã. Tôi nghĩ rằng ifelse thắng vòng này, vì việc tạo ra kết quả trong bảng thứ hai của tôi không quá khó ở đó (như Ananda đã giải thích trong các bình luận).
    2015-05-12 17: 45: 28Z
  3. Rất hay, cảm ơn bạn! Tôi không sử dụng data.table trong trường hợp này vì hiệu suất không quan trọng, nhưng tôi đánh giá cao bài đăng.
    2015-05-12 17: 52: 59Z
  4. Lưu ý với người chỉnh sửa tự /sẽ là: mã xấu trong khối thứ hai có thể được dọn sạch bằng chức năng dplyr.
    2015-05-12 18: 36: 52Z
frank()
nguồn đặt đây