গোষ্ঠীযুক্ত ডেটা থেকে প্রথম এবং শেষ সারিটি নির্বাচন করুন


137

প্রশ্ন

ব্যবহার করে dplyr, আমি কীভাবে একটি বিবৃতিতে গোষ্ঠীযুক্ত ডেটার শীর্ষ এবং নীচের পর্যবেক্ষণ / সারিগুলি নির্বাচন করব?

ডেটা ও উদাহরণ

একটি ডেটা ফ্রেম দেওয়া হয়েছে

df <- data.frame(id=c(1,1,1,2,2,2,3,3,3), 
                 stopId=c("a","b","c","a","b","c","a","b","c"), 
                 stopSequence=c(1,2,3,3,1,4,3,1,2))

আমি প্রতিটি গ্রুপের উপরের এবং নীচের পর্যবেক্ষণগুলি ব্যবহার করে পেতে পারি slice, তবে দুটি পৃথক স্ট্যাটাস ব্যবহার করে:

firstStop <- df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  slice(1) %>%
  ungroup

lastStop <- df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  slice(n()) %>%
  ungroup

আমি কি এই দুটি স্ট্যাটামিনেটকে একের সাথে সংযুক্ত করতে পারি যা উপরে এবং নীচের উভয় পর্যবেক্ষণকে নির্বাচন করে ?


উত্তর:


232

সম্ভবত একটি দ্রুত উপায় আছে:

df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  filter(row_number()==1 | row_number()==n())

66
rownumber() %in% c(1, n())দুবার ভেক্টর স্ক্যান চালানোর প্রয়োজনকে বাধা দেবে
মাইকেলচিরিকো

13
@ মিশেলচিরিকো আমার সন্দেহ হয় আপনি একটি বাদ দিয়েছেন _? অর্থাত্filter(row_number() %in% c(1, n()))
এরিক

107

কেবল সম্পূর্ণতার জন্য: আপনি sliceসূচকগুলির একটি ভেক্টর পাস করতে পারেন :

df %>% arrange(stopSequence) %>% group_by(id) %>% slice(c(1,n()))

যা দেয়

  id stopId stopSequence
1  1      a            1
2  1      c            3
3  2      b            1
4  2      c            4
5  3      b            1
6  3      a            3

এমনকি filter
এটির

1
@ তেজেবো ফিল্টারের বিপরীতে, স্লাইস একই সারিতে একাধিকবার ফিরে আসতে পারে, উদাহরণস্বরূপ mtcars[1, ] %>% slice(c(1, n()))সেই অর্থে তাদের মধ্যে পছন্দটি আপনি কী ফিরে চান তার উপর নির্ভর করে। আমি আশা করতাম সময়গুলি nখুব বেশি না হলে কাছাকাছি থাকত (যেখানে টুকরো পছন্দ হতে পারে) তবে তা পরীক্ষাও করে নি।
ফ্রাঙ্ক

15

না dplyr, তবে এটি ব্যবহার করে আরও সরাসরি data.table:

library(data.table)
setDT(df)
df[ df[order(id, stopSequence), .I[c(1L,.N)], by=id]$V1 ]
#    id stopId stopSequence
# 1:  1      a            1
# 2:  1      c            3
# 3:  2      b            1
# 4:  2      c            4
# 5:  3      b            1
# 6:  3      a            3

আরও বিস্তারিত ব্যাখ্যা:

# 1) get row numbers of first/last observations from each group
#    * basically, we sort the table by id/stopSequence, then,
#      grouping by id, name the row numbers of the first/last
#      observations for each id; since this operation produces
#      a data.table
#    * .I is data.table shorthand for the row number
#    * here, to be maximally explicit, I've named the variable V1
#      as row_num to give other readers of my code a clearer
#      understanding of what operation is producing what variable
first_last = df[order(id, stopSequence), .(row_num = .I[c(1L,.N)]), by=id]
idx = first_last$row_num

# 2) extract rows by number
df[idx]

বেসিকগুলি আচ্ছাদন করার জন্য প্রারম্ভিক উইকিটি পরীক্ষা করে দেখুন কিনা নিশ্চিত হনdata.table


1
বা df[ df[order(stopSequence), .I[c(1,.N)], keyby=id]$V1 ]। দেখতে দেখতে idদুইবার প্রদর্শিত আমাকে অদ্ভুত।
ফ্রাঙ্ক

আপনি setDTকলটিতে কীগুলি সেট করতে পারেন । সুতরাং orderএখানে কল করার দরকার নেই।
আর্টেম ক্লিভতসভ

1
@ আর্টেমক্লেভসভ - যদিও আপনি সবসময় কীগুলি সেট করতে চান না।
সিম্বলিক্সএইউ

2
বা df[order(stopSequence), .SD[c(1L,.N)], by = id]এখানে
জেভিলিম্যান

@ জেভিলিম্যান যে পুনরায় অর্ডার হবে না এটি অগত্যা ঠিক একই রকম হবে না id। আমার মনে df[order(stopSequence), .SD[c(1L, .N)], keyby = id]হয় কৌশলটি করা উচিত (উপরের সমাধানের সামান্য পার্থক্যের সাথে যে ফলাফলটি keyসম্পাদিত হবে
মাইকেলচিরিকো

8

কিছুটা এইরকম:

library(dplyr)

df <- data.frame(id=c(1,1,1,2,2,2,3,3,3),
                 stopId=c("a","b","c","a","b","c","a","b","c"),
                 stopSequence=c(1,2,3,3,1,4,3,1,2))

first_last <- function(x) {
  bind_rows(slice(x, 1), slice(x, n()))
}

df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  do(first_last(.)) %>%
  ungroup

## Source: local data frame [6 x 3]
## 
##   id stopId stopSequence
## 1  1      a            1
## 2  1      c            3
## 3  2      b            1
## 4  2      c            4
## 5  3      b            1
## 6  3      a            3

আপনার সাথে doগ্রুপে বেশ কয়েকটি সংখ্যক অপারেশন করতে পারবেন তবে @ জেরেমাইকস এর উত্তর কেবল এই কাজের জন্য আরও উপযুক্ত।


1
কোনও ফাংশন লেখার বিষয়টি বিবেচনা করেনি - অবশ্যই আরও জটিল কিছু করার ভাল উপায়।
tospig

1
এটি কেবলমাত্র ব্যবহারের সাথে তুলনা করে খুব বেশি জটিল বলে মনে sliceহচ্ছেdf %>% arrange(stopSequence) %>% group_by(id) %>% slice(c(1,n()))
ফ্র্যাঙ্ক

4
না দ্বিমত পোষণ (এবং আমার আরও ভালো উত্তর হিসাবে jeremycg এর প্রতি ইঙ্গিত মধ্যে পোস্ট) কিন্তু একটি থাকার doএখানে উদাহরণস্বরূপ অন্যদের যখন সাহায্য করতে পারে sliceহবে কাজ না (অর্থাত একটি দলের উপর আরো জটিল অপারেশন)। এবং, আপনি আপনার মন্তব্য পোস্ট করে উত্তর হিসাবে (এটি সেরা)।
hrbrmstr

6

আমি নির্দিষ্ট প্রশ্ন জানি dplyr। তবে, যেহেতু অন্যরা ইতিমধ্যে অন্যান্য প্যাকেজগুলি ব্যবহার করে সমাধান পোস্ট করেছে, তাই আমি অন্য প্যাকেজগুলিও ব্যবহার করার সিদ্ধান্ত নিয়েছি:

বেস প্যাকেজ:

df <- df[with(df, order(id, stopSequence, stopId)), ]
merge(df[!duplicated(df$id), ], 
      df[!duplicated(df$id, fromLast = TRUE), ], 
      all = TRUE)

data.table:

df <-  setDT(df)
df[order(id, stopSequence)][, .SD[c(1,.N)], by=id]

sqldf:

library(sqldf)
min <- sqldf("SELECT id, stopId, min(stopSequence) AS StopSequence
      FROM df GROUP BY id 
      ORDER BY id, StopSequence, stopId")
max <- sqldf("SELECT id, stopId, max(stopSequence) AS StopSequence
      FROM df GROUP BY id 
      ORDER BY id, StopSequence, stopId")
sqldf("SELECT * FROM min
      UNION
      SELECT * FROM max")

একটি প্রশ্নে:

sqldf("SELECT * 
        FROM (SELECT id, stopId, min(stopSequence) AS StopSequence
              FROM df GROUP BY id 
              ORDER BY id, StopSequence, stopId)
        UNION
        SELECT *
        FROM (SELECT id, stopId, max(stopSequence) AS StopSequence
              FROM df GROUP BY id 
              ORDER BY id, StopSequence, stopId)")

আউটপুট:

  id stopId StopSequence
1  1      a            1
2  1      c            3
3  2      b            1
4  2      c            4
5  3      a            3
6  3      b            1

3

ব্যবহার which.minএবং which.max:

library(dplyr, warn.conflicts = F)
df %>% 
  group_by(id) %>% 
  slice(c(which.min(stopSequence), which.max(stopSequence)))

#> # A tibble: 6 x 3
#> # Groups:   id [3]
#>      id stopId stopSequence
#>   <dbl> <fct>         <dbl>
#> 1     1 a                 1
#> 2     1 c                 3
#> 3     2 b                 1
#> 4     2 c                 4
#> 5     3 b                 1
#> 6     3 a                 3

মাপকাঠি

এটি বর্তমান গৃহীত উত্তরের চেয়েও তাত্পর্যপূর্ণ কারণ পুরো স্টপসেকেন্স কলামটি বাছাইয়ের পরিবর্তে আমরা গ্রুপ অনুসারে ন্যূনতম এবং সর্বাধিক মান খুঁজে পাই।

# create a 100k times longer data frame
df2 <- bind_rows(replicate(1e5, df, F)) 
bench::mark(
  mm =df2 %>% 
    group_by(id) %>% 
    slice(c(which.min(stopSequence), which.max(stopSequence))),
  jeremy = df2 %>%
    group_by(id) %>%
    arrange(stopSequence) %>%
    filter(row_number()==1 | row_number()==n()))
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 mm           22.6ms     27ms     34.9     14.2MB     21.3
#> 2 jeremy      254.3ms    273ms      3.66    58.4MB     11.0

2

ব্যবহার data.table:

# convert to data.table
setDT(df) 
# order, group, filter
df[order(stopSequence)][, .SD[c(1, .N)], by = id]

   id stopId stopSequence
1:  1      a            1
2:  1      c            3
3:  2      b            1
4:  2      c            4
5:  3      b            1
6:  3      a            3

1

ল্যাপ্লি এবং ডিপিপ্লায়ার স্টেটমেন্ট সহ আরেকটি পন্থা। একই বিবৃতিতে সংক্ষিপ্তসারগুলি যা কিছু কাজ করে তার একটি স্বেচ্ছাচারিত সংখ্যা আমরা প্রয়োগ করতে পারি:

lapply(c(first, last), 
       function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>% 
bind_rows()

আপনি উদাহরণস্বরূপ সর্বাধিক স্টপসেসেন্সেন্স মান সহ সারিগুলিতে আগ্রহী হতে পারেন এবং করতে পারেন:

lapply(c(first, last, max("stopSequence")), 
       function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>%
bind_rows()

0

একটি ভিন্ন বেস আর বিকল্প প্রথম হবে orderদ্বারা idএবং stopSequence,split তাদের উপর ভিত্তি করে idএবং প্রত্যেক জন্য idআমরা কেবল প্রথম ও শেষ সূচক নির্বাচন করুন ও dataframe ঐ সূচকের ব্যবহার উপসেট।

df[sapply(with(df, split(order(id, stopSequence), id)), function(x) 
                   c(x[1], x[length(x)])), ]


#  id stopId stopSequence
#1  1      a            1
#3  1      c            3
#5  2      b            1
#6  2      c            4
#8  3      b            1
#7  3      a            3

বা অনুরূপ ব্যবহার by

df[unlist(with(df, by(order(id, stopSequence), id, function(x) 
                   c(x[1], x[length(x)])))), ]
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.