গ্রুপ অনুসারে প্রথম সারিটি নির্বাচন করুন


87

এটির মতো ডেটাফ্রেম থেকে

test <- data.frame('id'= rep(1:5,2), 'string'= LETTERS[1:10])
test <- test[order(test$id), ]
rownames(test) <- 1:10

> test
    id string
 1   1      A
 2   1      F
 3   2      B
 4   2      G
 5   3      C
 6   3      H
 7   4      D
 8   4      I
 9   5      E
 10  5      J

আমি প্রতিটি আইডি / স্ট্রিং জুটির প্রথম সারিতে একটি নতুন তৈরি করতে চাই। Sqldf যদি এর মধ্যে আর কোড গ্রহণ করে, ক্যোয়ারীটি এর মতো দেখতে পারে:

res <- sqldf("select id, min(rownames(test)), string 
              from test 
              group by id, string")

> res
    id string
 1   1      A
 3   2      B
 5   3      C
 7   4      D
 9   5      E

এর মতো কোনও নতুন কলাম তৈরির কোনও সমাধান কি সংক্ষিপ্ত রয়েছে

test$row <- rownames(test)

এবং মিনিট (সারি) দিয়ে একই স্কেলডিএফ কোয়েরি চালাচ্ছেন?



4
@ ম্যাথহে, আমার প্রশ্নটি আরও পুরনো।
ডিএমভিয়ান

4
আপনার প্রশ্নটি 1 বছরের পুরনো, এবং অন্য প্রশ্নটি 4 বছর বয়সী, না? এই প্রশ্নের অনেকগুলি নকল রয়েছে
ম্যাথু

@ ম্যাথু দুঃখিত, আমি অবশ্যই তারিখগুলি ভুল লিখে ফেলেছি।
ডিএমভিয়ান

উত্তর:


120

আপনি duplicatedএটি খুব দ্রুত করতে ব্যবহার করতে পারেন ।

test[!duplicated(test$id),]

গতি freaks জন্য মানদণ্ড:

ju <- function() test[!duplicated(test$id),]
gs1 <- function() do.call(rbind, lapply(split(test, test$id), head, 1))
gs2 <- function() do.call(rbind, lapply(split(test, test$id), `[`, 1, ))
jply <- function() ddply(test,.(id),function(x) head(x,1))
jdt <- function() {
  testd <- as.data.table(test)
  setkey(testd,id)
  # Initial solution (slow)
  # testd[,lapply(.SD,function(x) head(x,1)),by = key(testd)]
  # Faster options :
  testd[!duplicated(id)]               # (1)
  # testd[, .SD[1L], by=key(testd)]    # (2)
  # testd[J(unique(id)),mult="first"]  # (3)
  # testd[ testd[,.I[1L],by=id] ]      # (4) needs v1.8.3. Allows 2nd, 3rd etc
}

library(plyr)
library(data.table)
library(rbenchmark)

# sample data
set.seed(21)
test <- data.frame(id=sample(1e3, 1e5, TRUE), string=sample(LETTERS, 1e5, TRUE))
test <- test[order(test$id), ]

benchmark(ju(), gs1(), gs2(), jply(), jdt(),
    replications=5, order="relative")[,1:6]
#     test replications elapsed relative user.self sys.self
# 1   ju()            5    0.03    1.000      0.03     0.00
# 5  jdt()            5    0.03    1.000      0.03     0.00
# 3  gs2()            5    3.49  116.333      2.87     0.58
# 2  gs1()            5    3.58  119.333      3.00     0.58
# 4 jply()            5    3.69  123.000      3.11     0.51

আসুন এটি আবার চেষ্টা করুন, তবে প্রথম তাপ থেকে প্রাপ্ত প্রতিযোগীদের সাথে এবং আরও ডেটা এবং আরও অনুলিপি সহ

set.seed(21)
test <- data.frame(id=sample(1e4, 1e6, TRUE), string=sample(LETTERS, 1e6, TRUE))
test <- test[order(test$id), ]
benchmark(ju(), jdt(), order="relative")[,1:6]
#    test replications elapsed relative user.self sys.self
# 1  ju()          100    5.48    1.000      4.44     1.00
# 2 jdt()          100    6.92    1.263      5.70     1.15

বিজয়ী: system.time (dat3 [! নকল (dat3 $ id),]) ব্যবহারকারী সিস্টেমটি 0.07 0.00 0.07 কেটে গেছে
ডেমভিয়ান্না

4
@ ডমুভিয়ানা: আমার এটি ইনস্টল নেই এবং এটি নিয়ে বিরক্ত করার মতো মনে হয় না। :)
জোশুয়া উলরিচ

আমরা কি নিশ্চিত যে আমার ডেটা টেবিল কোডটি যথাসম্ভব দক্ষ? আমি সেই সরঞ্জামটি থেকে সর্বোত্তম পারফরম্যান্স উপস্থাপনের জন্য আমার ক্ষমতায় আত্মবিশ্বাসী নই।
জোরান

4
এছাড়াও, আমি গণনা করি, আপনি যদি ডেটা বেঞ্চমার্ক করতে চলেছেন able টেবিল, কী করে আপনার বেস কলগুলির মধ্যে আইডির মাধ্যমে ক্রমটি অন্তর্ভুক্ত করা উচিত।
মেল

4
@ জোশুয়া অলরিচ আরও একটি প্রশ্ন: প্রথম বাক্যটি কেন প্রয়োজন তা বোঝা যাচ্ছে যে ডেটা ইতিমধ্যে সাজানো হয়েছে। !duplicated(x)বাছাই না করা হলেও প্রতিটি গ্রুপের প্রথমটি সন্ধান করে, iiuc।
ম্যাট ডাউল

38

আমি dplyr পদ্ধতির পক্ষে।

group_by(id) হয় অনুসরণ করে

  • filter(row_number()==1) বা
  • slice(1) বা
  • slice_head(1) # (dplyr => 1.0)
  • top_n(n = -1)
    • top_n()অভ্যন্তরীণভাবে র‌্যাঙ্ক ফাংশন ব্যবহার করে। র‌্যাঙ্কের নীচ থেকে নেতিবাচক নির্বাচন করে।

কিছু ক্ষেত্রে গ্রুপ_বাইয়ের পরে আইডির ব্যবস্থা করা প্রয়োজনীয় হতে পারে।

library(dplyr)

# using filter(), top_n() or slice()

m1 <-
test %>% 
  group_by(id) %>% 
  filter(row_number()==1)

m2 <-
test %>% 
  group_by(id) %>% 
  slice(1)

m3 <-
test %>% 
  group_by(id) %>% 
  top_n(n = -1)

তিনটি পদ্ধতিই একই ফল দেয়

# A tibble: 5 x 2
# Groups:   id [5]
     id string
  <int> <fct> 
1     1 A     
2     2 B     
3     3 C     
4     4 D     
5     5 E

4
sliceপাশাপাশি একটি চিৎকার আউট মূল্যবান । slice(x)এর জন্য একটি শর্টকাট filter(row_number() %in% x)
গ্রেগোর টমাস

খুবই ভদ্র. তুমি কি জানো কেন আমি আমার রুপান্তরিত করতে হবে data.tableএকটি করতে data.frameএটির জন্য কাজ করতে?
জেমস হিরশর্ন

@ জেমসহির্সকর্ন আমি সমস্ত পার্থক্যের বিশেষজ্ঞ নই। তবে data.tableউত্তরাধিকার হিসাবে data.frameঅনেক ক্ষেত্রে আপনি একটিতে dplyr কমান্ড ব্যবহার করতে পারেন data.table। উদাহরণস্বরূপ উদাহরণস্বরূপ যদি testএকটি হয় তবে তাও কাজ করে data.table। আরও গভীর ব্যাখ্যার জন্য উদাহরণস্বরূপ stackoverflow.com/questions/13618488/… দেখুন
ক্রেস্টেন

এটি করার একটি স্বাচ্ছন্দ্যময় উপায় এবং আপনি দেখতে পাচ্ছেন যে ডেটা.ফ্রেম আসলে এখানে একটি টিব্লস। আমি আপনাকে ব্যক্তিগতভাবে সর্বদা ছোট ছোট জিনিসগুলির সাথে কাজ করার পরামর্শ দিই কারণ জিজিপ্লট 2 একইভাবে নির্মিত।
গারিনী

17

কি সম্পর্কে

DT <- data.table(test)
setkey(DT, id)

DT[J(unique(id)), mult = "first"]

সম্পাদনা করুন

এছাড়াও একটি অনন্য পদ্ধতি রয়েছে data.tablesযার জন্য প্রথম সারিটি কী দ্বারা ফিরে আসবে

jdtu <- function() unique(DT)

আমি মনে করি, যদি আপনি ক্রম হয় test, বেঞ্চমার্ক বাহিরে তারপর আপনি সরানোর করতে setkeyএবং data.tableপাশাপাশি বেঞ্চমার্ক থেকে রুপান্তরের (যেমন setkey মূলত আইডি, একই দ্বারা বাছাই করে order)।

set.seed(21)
test <- data.frame(id=sample(1e3, 1e5, TRUE), string=sample(LETTERS, 1e5, TRUE))
test <- test[order(test$id), ]
DT <- data.table(DT, key = 'id')
ju <- function() test[!duplicated(test$id),]

jdt <- function() DT[J(unique(id)),mult = 'first']


 library(rbenchmark)
benchmark(ju(), jdt(), replications = 5)
##    test replications elapsed relative user.self sys.self 
## 2 jdt()            5    0.01        1      0.02        0        
## 1  ju()            5    0.05        5      0.05        0         

এবং আরও তথ্য সহ

** অনন্য পদ্ধতি সহ সম্পাদনা করুন **

set.seed(21)
test <- data.frame(id=sample(1e4, 1e6, TRUE), string=sample(LETTERS, 1e6, TRUE))
test <- test[order(test$id), ]
DT <- data.table(test, key = 'id')
       test replications elapsed relative user.self sys.self 
2  jdt()            5    0.09     2.25      0.09     0.00    
3 jdtu()            5    0.04     1.00      0.05     0.00      
1   ju()            5    0.22     5.50      0.19     0.03        

অনন্য পদ্ধতিটি এখানে দ্রুত।


4
আপনাকে কীটি সেট করতে হবে না। unique(DT,by="id")সরাসরি কাজ করে
ম্যাথিউ

অবগতির জন্য হিসাবে data.tableসংস্করণ> = 1.9.8, ডিফল্ট byজন্য যুক্তি uniqueহল by = seq_along(x)(সমস্ত কলাম), পূর্বের ডিফল্ট পরিবর্তেby = key(x)
IceCreamToucan

12

একটি সহজ ddplyবিকল্প:

ddply(test,.(id),function(x) head(x,1))

গতি যদি সমস্যা হয় তবে একই ধরণের পদ্ধতির সাথে নেওয়া যেতে পারে data.table:

testd <- data.table(test)
setkey(testd,id)
testd[,.SD[1],by = key(testd)]

অথবা এটি যথেষ্ট দ্রুত হতে পারে:

testd[testd[, .I[1], by = key(testd]$V1]

আশ্চর্যজনকভাবে, স্কোয়াডলফ এটি দ্রুত করে: 1.77 0.13 1.92 বনাম 10.53 0.00 10.79 ডেটা.ট্যাবল সহ
ডেমভিয়ান্না

4
@ dmvianna- এ অগত্যা আমি ডেটা টেবিল গণনা করব না। আমি সেই সরঞ্জামটির সাথে বিশেষজ্ঞ নই, সুতরাং আমার ডেটা টেবিল কোডটি এটি সম্পর্কে সবচেয়ে কার্যকর উপায় নাও হতে পারে।
23:25 এ জোরান

আমি অসময়ে এটিকে উন্নত করেছিলাম। যখন আমি এটি একটি বড় ডেটাতে চালিত করি able টেবিল, এটি হাস্যকরভাবে ধীর ছিল এবং এটি কার্যকর হয়নি: সারিগুলির সংখ্যা একই পরে ছিল।
জেমস হিরশর্ন

@ জেমসহিরাকর্ন আমি এটি অনেক আগে লিখেছিলাম, প্যাকেজটি অনেক পরিবর্তন হয়েছে এবং আমি খুব কমই ডেটা টেবিল ব্যবহার করি। যদি আপনি এই প্যাকেজটির সাহায্যে সঠিক উপায় খুঁজে পান তবে এটিকে আরও ভাল করার জন্য নির্দ্বিধায় একটি সম্পাদনার পরামর্শ দিন।
জোড়ান

8

এখন জন্য dplyr, একটি পৃথক কাউন্টার যুক্ত।

df %>%
    group_by(aa, bb) %>%
    summarise(first=head(value,1), count=n_distinct(value))

আপনি গ্রুপ তৈরি করেন, তাদের গোষ্ঠীগুলির মধ্যে সংক্ষিপ্তসার দিন।

যদি ডেটা সংখ্যাসূচক হয় তবে আপনি ব্যবহার করতে পারেন: এর জায়গায়
first(value)[এছাড়াও রয়েছে last(value)]head(value, 1)

দেখুন: http://cran.rstudio.com/web/packages/dplyr/vignettes/intrication.html

সম্পূর্ণ:

> df
Source: local data frame [16 x 3]

   aa bb value
1   1  1   GUT
2   1  1   PER
3   1  2   SUT
4   1  2   GUT
5   1  3   SUT
6   1  3   GUT
7   1  3   PER
8   2  1   221
9   2  1   224
10  2  1   239
11  2  2   217
12  2  2   221
13  2  2   224
14  3  1   GUT
15  3  1   HUL
16  3  1   GUT

> library(dplyr)
> df %>%
>   group_by(aa, bb) %>%
>   summarise(first=head(value,1), count=n_distinct(value))

Source: local data frame [6 x 4]
Groups: aa

  aa bb first count
1  1  1   GUT     2
2  1  2   SUT     2
3  1  3   SUT     3
4  2  1   221     3
5  2  2   217     3
6  3  1   GUT     2

এই উত্তরটি বেশ তারিখযুক্ত - এটি করার আরও ভাল উপায় রয়েছে যেগুলি dplyrপ্রতিটি কলামের অন্তর্ভুক্ত করার জন্য একটি বিবৃতি লেখার প্রয়োজন নেই (নীচে পরমাণুর উত্তর দেখুন, উদাহরণস্বরূপ) . Also I'm not sure what *"if data is numeric"* has anything to do with whether or not one would use প্রথম (মান) `বনাম head(value)(বা ন্যায়সঙ্গত value[1])
গ্রেগর থমাস

7

(1) এসকিউএলাইটের rowidসিউডো-কলামে একটি বিল্ট রয়েছে তাই এটি কাজ করে:

sqldf("select min(rowid) rowid, id, string 
               from test 
               group by id")

প্রদান:

  rowid id string
1     1  1      A
2     3  2      B
3     5  3      C
4     7  4      D
5     9  5      E

(২) এছাড়াও sqldfনিজস্ব একটি row.names=যুক্তি রয়েছে:

sqldf("select min(cast(row_names as real)) row_names, id, string 
              from test 
              group by id", row.names = TRUE)

প্রদান:

  id string
1  1      A
3  2      B
5  3      C
7  4      D
9  5      E

(3) তৃতীয় বিকল্প যা উপরের দুটি উপাদানের সাথে মিশে তা আরও ভাল হতে পারে:

sqldf("select min(rowid) row_names, id, string 
               from test 
               group by id", row.names = TRUE)

প্রদান:

  id string
1  1      A
3  2      B
5  3      C
7  4      D
9  5      E

নোট করুন যে এই তিনটিই এসকিউএল-তে এসকিউএলাইট এক্সটেনশনের উপর নির্ভর করে যেখানে অন্য কলামগুলি একই সারি থেকে বেছে নেওয়ার ফলস্বরূপ minবা ব্যবহারের maxগ্যারান্টিযুক্ত। (অন্যান্য এসকিউএল-ভিত্তিক ডাটাবেসে যার নিশ্চয়তা দেওয়া যায় না))


ধন্যবাদ! এটি স্বীকৃত উত্তর আইএমওর চেয়ে অনেক ভাল কারণ একাধিক সমষ্টিগত ফাংশন ব্যবহার করে একটি সামগ্রিক পদক্ষেপে প্রথম / শেষ উপাদানটি গ্রহণ করা সাধারণ (যেমন এই পরিবর্তনকের প্রথমটি গ্রহণ করুন, যোগফলটি যোগ করুন ইত্যাদি) it's
ব্রিজবার্নার্স

4

একটি বেস আর বিকল্পটি হ'ল split()- lapply()- do.call()প্রতিমা:

> do.call(rbind, lapply(split(test, test$id), head, 1))
  id string
1  1      A
2  2      B
3  3      C
4  4      D
5  5      E

একটি অধিক প্রত্যক্ষ বিকল্প হয় ফাংশন:lapply()[

> do.call(rbind, lapply(split(test, test$id), `[`, 1, ))
  id string
1  1      A
2  2      B
3  3      C
4  4      D
5  5      E

কলের 1, )শেষে কমা স্থানটি প্রয়োজনীয় কারণ এটি প্রথম সারির এবং সমস্ত কলাম নির্বাচন করার জন্য কল করার সমতুল্য ।lapply()[1, ]


এটি খুব ধীর ছিল, গ্যাভিন: ব্যবহারকারী সিস্টেমটি 91.84 6.02 101.10
ডেমভিয়ানা

ডেটা ফ্রেমের সাথে জড়িত যে কোনও কিছু হবে। তাদের ইউটিলিটি একটি মূল্যে আসে। উদাহরণস্বরূপ, ডেটা টেবিল।
গ্যাভিন সিম্পসন

আমার প্রতিরক্ষা এবং আর এর ক্ষেত্রে, আপনি প্রশ্নের দক্ষতা সম্পর্কে কিছুই উল্লেখ করেননি। প্রায়ই ব্যবহার কর্মের হয় একটি বৈশিষ্ট্য। প্লাইয়ের জনপ্রিয়তার সাক্ষ্য দিন, যা "ধীর" খুব কমপক্ষে পরবর্তী সংস্করণ পর্যন্ত ডেটা.টেল সমর্থন রয়েছে until
গ্যাভিন সিম্পসন

4
আমি রাজী. আমি আপনাকে অপমান করার কথা বলতে চাইনি। যদিও আমি পেয়েছি যে @ জোশুয়া-উলরিচের পদ্ধতিটি দ্রুত এবং সহজ উভয়ই ছিল । : 7)
ডেমোভিয়ান

ক্ষমা চাওয়ার দরকার নেই এবং আমি এটিকে অপমান হিসাবে গ্রহণ করি নি। এটি কেবল নির্দেশ করছে যে কোনও দক্ষতার দাবি ছাড়াই এটি দেওয়া হয়েছিল। মনে রাখবেন এই স্ট্যাক ওভারফ্লো প্রশ্নোত্তর কেবল আপনার সুবিধার জন্য নয় অন্যান্য ব্যবহারকারীদের যারা একই প্রশ্নে আপনার প্রশ্ন জুড়ে আসে তাদের জন্য।
গ্যাভিন সিম্পসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.