একসাথে একাধিক কলাম আটকান


100

আমার কাছে একটি ডেটাফ্রেমে অনেকগুলি কলাম রয়েছে যা আমি একত্রে পেস্ট করতে চাই ("-" দ্বারা পৃথক করে):

data <- data.frame('a' = 1:3, 
                   'b' = c('a','b','c'), 
                   'c' = c('d', 'e', 'f'), 
                   'd' = c('g', 'h', 'i'))
i.e.     
     a   b   c  d  
     1   a   d   g  
     2   b   e   h  
     3   c   f   i  

যা আমি হতে চাই:

a x  
1 a-d-g  
2 b-e-h  
3 c-f-i  

আমি সাধারণত এটি দিয়ে করতে পারি:

within(data, x <- paste(b,c,d,sep='-'))

এবং তারপরে পুরানো কলামগুলি সরিয়ে ফেলছি, তবে দুর্ভাগ্যক্রমে আমি কলামগুলির নামগুলি বিশেষভাবে জানি না, সমস্ত কলামগুলির জন্য কেবল একটি সম্মিলিত নাম, যেমন আমি জানতাম cols <- c('b','c','d')

কেউ কি এটি করার একটি উপায় জানেন?

উত্তর:


104
# your starting data..
data <- data.frame('a' = 1:3, 'b' = c('a','b','c'), 'c' = c('d', 'e', 'f'), 'd' = c('g', 'h', 'i')) 

# columns to paste together
cols <- c( 'b' , 'c' , 'd' )

# create a new column `x` with the three columns collapsed together
data$x <- apply( data[ , cols ] , 1 , paste , collapse = "-" )

# remove the unnecessary columns
data <- data[ , !( names( data ) %in% cols ) ]

8
এখানে আবেদনের দরকার নেই; পেস্ট vectorised করা হয়, এবং যে আরো কার্যকর
— Baptiste

4
@ ব্যাপটিস্ট .. ছাড়া সম্ভব do.call?
— অ্যান্থনি দামিকো

4
নিশ্চিত, আপনি উদাহরণস্বরূপ ব্যবহার করতে পারেন evil(parse(...)), তবে আমি বিশ্বাস করি do.callএখানে সঠিক কল।
— ব্যাপটিস্ট

এখানে ডোকল সবই আরও ভাল কৌশল; ভেক্টরাইজেশন বজায় রাখে।
— ক্লেটন স্ট্যানলি

4
হুম .. আপনি কিভাবে পাস করবেন collapse = "-"? থেকে paste?
— অ্যান্টনি দামিকো

48

একটি বৈকল্পিক হিসাবে Baptiste এর উত্তর , সঙ্গে dataএবং সংজ্ঞায়িত আপনি যেমন কলাম আপনি একত্র করা সংজ্ঞায়িত করতে চানcols

cols <- c("b", "c", "d")

আপনি নতুন কলামটি যুক্ত করতে dataএবং এর সাথে পুরানোগুলি মুছতে পারেন

data$x <- do.call(paste, c(data[cols], sep="-"))
for (co in cols) data[co] <- NULL

যা দেয়

> data
  a     x
1 1 a-d-g
2 2 b-e-h
3 3 c-f-i

"সি (ডেটা [কোলস], ..." এ অনুপস্থিত আছে কি? "লাইক:" সি (ডেটা [, কলস], ... "
— রোশু

4
@roschu হয় কাজ করবে। data.frameপ্রথম যুক্তি সাধারণত সারি সূচক হওয়া সত্ত্বেও একটি একক অক্ষর ভেক্টরের সাথে একটি সূচীকরণ একটি কলাম সূচীকরণ হবে।
— ব্রায়ান ডিগস

দ্রুত এবং স্মার্ট আপনাকে ধন্যবাদ
— আলী খসরো

33

tidyrপ্যাকেজ ব্যবহার করে , এটি সহজেই 1 ফাংশন কলে হ্যান্ডেল করা যায়।

data <- data.frame('a' = 1:3, 
                   'b' = c('a','b','c'), 
                   'c' = c('d', 'e', 'f'), 
                   'd' = c('g', 'h', 'i'))

tidyr::unite_(data, paste(colnames(data)[-1], collapse="_"), colnames(data)[-1])

  a b_c_d
1 1 a_d_g
2 2 b_e_h
3 3 c_f_i

সম্পাদনা: প্রথম কলামটি বাদ দিন, সমস্ত কিছু আটকানো হবে।

# tidyr_0.6.3

unite(data, newCol, -a) 
# or by column index unite(data, newCol, -1)

#   a newCol
# 1 1  a_d_g
# 2 2  b_e_h
# 3 3  c_f_i

4
আমি ওপি উল্লিখিত তারা অগ্রিম কলামের নামের জানি না। মনে করি, অন্যথায় শুধু সঙ্গে এটা করতে পারে within(data, x <- paste(b,c,d,sep='-'))হিসাবে তারা সচিত্র।
— ডেভিড আরেনবুর্গ

আমি @ ডেভিড আরেনবার্গের সাথে একমত, এটি ওপি-র পরিস্থিতি বিবেচনা করে না। আমার ধারণা unite_(data, "b_c_d", cols), বা তাদের আসল ডেটা ফ্রেমের উপর নির্ভর করে unite(data, b_c_d, -a)প্রার্থীও হতে পারে।
— স্যাম ফির্ক

14

আমি একটি নতুন ডেটা তৈরি করব ra ফ্রেম:

d <- data.frame('a' = 1:3, 'b' = c('a','b','c'), 'c' = c('d', 'e', 'f'), 'd' = c('g', 'h', 'i')) 

cols <- c( 'b' , 'c' , 'd' )

data.frame(a = d[, 'a'], x = do.call(paste, c(d[ , cols], list(sep = '-'))))

নোট করুন যে আপনার পরিবর্তে কলামটি বাদ দিয়ে যদি সমস্তগুলি আটকে দেওয়া হয় তবে d[ , cols]আপনি ব্যবহার করতে চাইতে পারেন । d[ , names(d) != 'a']a
— baptiste

4
এসও-তে একটি প্রচলিত সমাধান, আমার মনে হয় আপনি এটি সংক্ষিপ্ত করে রাখতে পারেন cbind(a = d['a'], x = do.call(paste, c(d[cols], sep = '-'))), উদাহরণস্বরূপ কমা এড়ানো listএবং data.frameএই data.frameপদ্ধতিটি ব্যবহার করার সময়cbind
— ডেভিড আরেনবার্গ

9

কেবলমাত্র অতিরিক্ত সমাধান যুক্ত করা Reduceযা সম্ভবত সম্ভবত do.callধীরের চেয়ে বেশি সম্ভবত সম্ভবত applyএটির চেয়ে ভাল কারণ এটি matrixরূপান্তর এড়াবে । এছাড়াও, পরিবর্তে একটি forলুপ আমরা কেবল setdiffঅযাচিত কলামগুলি সরাতে ব্যবহার করতে পারি

cols <- c('b','c','d')
data$x <- Reduce(function(...) paste(..., sep = "-"), data[cols])
data[setdiff(names(data), cols)]
#   a     x
# 1 1 a-d-g
# 2 2 b-e-h
# 3 3 c-f-i

বিকল্পভাবে আমরা প্যাকেজটি dataব্যবহার করে জায়গায় আপডেট করতে পারি data.table(নতুন তথ্য অনুমান করে)

library(data.table)
setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD[, mget(cols)])]
data[, (cols) := NULL]
data
#    a     x
# 1: 1 a-d-g
# 2: 2 b-e-h
# 3: 3 c-f-i

আর একটি বিকল্প হিসাবে ব্যবহার .SDcolsনা করে ব্যবহার mgetকরা হয়

setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols]

5

আমি অ্যান্টনি ড্যামিকো, ব্রায়ান ডিগস এবং ডেটা_স্টিভের উত্তরগুলিকে একটি ছোট নমুনার উপর ভিত্তি করে চিহ্নিত tbl_dfকরেছি এবং নিম্নলিখিত ফলাফল পেয়েছি।

> data <- data.frame('a' = 1:3, 
+                    'b' = c('a','b','c'), 
+                    'c' = c('d', 'e', 'f'), 
+                    'd' = c('g', 'h', 'i'))
> data <- tbl_df(data)
> cols <- c("b", "c", "d")
> microbenchmark(
+     do.call(paste, c(data[cols], sep="-")),
+     apply( data[ , cols ] , 1 , paste , collapse = "-" ),
+     tidyr::unite_(data, "x", cols, sep="-")$x,
+     times=1000
+ )
Unit: microseconds
                                         expr     min      lq      mean  median       uq       max neval
do.call(paste, c(data[cols], sep = "-"))       65.248  78.380  93.90888  86.177  99.3090   436.220  1000
apply(data[, cols], 1, paste, collapse = "-") 223.239 263.044 313.11977 289.514 338.5520   743.583  1000
tidyr::unite_(data, "x", cols, sep = "-")$x   376.716 448.120 556.65424 501.877 606.9315 11537.846  1000

যাইহোক, আমি যখন নিজের থেকে tbl_df1 মিলিয়ন ডলার সারি এবং 10 কলাম দিয়ে মূল্যায়ন করেছি ফলাফলগুলি একেবারেই আলাদা ছিল।

> microbenchmark(
+     do.call(paste, c(data[c("a", "b")], sep="-")),
+     apply( data[ , c("a", "b") ] , 1 , paste , collapse = "-" ),
+     tidyr::unite_(data, "c", c("a", "b"), sep="-")$c,
+     times=25
+ )
Unit: milliseconds
                                                       expr        min         lq      mean     median        uq       max neval
do.call(paste, c(data[c("a", "b")], sep="-"))                 930.7208   951.3048  1129.334   997.2744  1066.084  2169.147    25
apply( data[ , c("a", "b") ] , 1 , paste , collapse = "-" )  9368.2800 10948.0124 11678.393 11136.3756 11878.308 17587.617    25
tidyr::unite_(data, "c", c("a", "b"), sep="-")$c              968.5861  1008.4716  1095.886  1035.8348  1082.726  1759.349    25

5

আমার মতে- sprintfফাংশনও এই উত্তরগুলির মধ্যে একটি স্থানের দাবিদার। আপনি sprintfনিম্নলিখিত হিসাবে ব্যবহার করতে পারেন :

do.call(sprintf, c(d[cols], '%s-%s-%s'))

যা দেয়:

 [1] "a-d-g" "b-e-h" "c-f-i"

এবং প্রয়োজনীয় ডেটাফ্রেম তৈরি করতে:

data.frame(a = d$a, x = do.call(sprintf, c(d[cols], '%s-%s-%s')))

প্রদান:

  a     x
1 1 a-d-g
2 2 b-e-h
3 3 c-f-i

যদিও @ ব্রায়ানডিগস / এর সংমিশ্রণের বিষয়ে sprintfসুস্পষ্ট সুবিধা নেই তবে এটি বিশেষভাবে কার্যকর যখন আপনি পছন্দসই স্ট্রিংয়ের কিছু অংশও প্যাড করতে চান বা যখন আপনি অঙ্কের সংখ্যা নির্দিষ্ট করতে চান। দেখাdo.callpaste?sprintfবিভিন্ন বিকল্পের জন্য ।

আর একটি রূপ ব্যবহার করা হবে pmap করা হবে:

pmap(d[2:4], paste, sep = '-')

দ্রষ্টব্য: এই pmapসমাধানটি কেবল তখনই কাজ করে যখন কলামগুলি ফ্যাক্টর নয়।


বৃহত্তর ডেটাসেটের একটি মানদণ্ড:

# create a larger dataset
d2 <- d[sample(1:3,1e6,TRUE),]
# benchmark
library(microbenchmark)
microbenchmark(
  docp = do.call(paste, c(d2[cols], sep="-")),
  appl = apply( d2[, cols ] , 1 , paste , collapse = "-" ),
  tidr = tidyr::unite_(d2, "x", cols, sep="-")$x,
  docs = do.call(sprintf, c(d2[cols], '%s-%s-%s')),
  times=10)

ফলাফল স্বরূপ:

Unit: milliseconds
 expr       min        lq      mean    median        uq       max neval cld
 docp  214.1786  226.2835  297.1487  241.6150  409.2495  493.5036    10 a  
 appl 3832.3252 4048.9320 4131.6906 4072.4235 4255.1347 4486.9787    10   c
 tidr  206.9326  216.8619  275.4556  252.1381  318.4249  407.9816    10 a  
 docs  413.9073  443.1550  490.6520  453.1635  530.1318  659.8400    10  b 

ব্যবহৃত ডেটা:

d <- data.frame(a = 1:3, b = c('a','b','c'), c = c('d','e','f'), d = c('g','h','i')) 

3

এখানে একটি মোটামুটি রীতিবিরুদ্ধ (কিন্তু ফাস্ট) প্রবেশপথ: ব্যবহার fwriteথেকে data.tableএকসাথে "আটকান" কলাম, এবং freadএটা ফিরে পড়তে সুবিধার জন্য, আমি একটি ফাংশন বলা যেমন পদক্ষেপ লিখিত করেছি। fpaste:

fpaste <- function(dt, sep = ",") {
  x <- tempfile()
  fwrite(dt, file = x, sep = sep, col.names = FALSE)
  fread(x, sep = "\n", header = FALSE)
}

এখানে একটি উদাহরণ:

d <- data.frame(a = 1:3, b = c('a','b','c'), c = c('d','e','f'), d = c('g','h','i')) 
cols = c("b", "c", "d")

fpaste(d[cols], "-")
#       V1
# 1: a-d-g
# 2: b-e-h
# 3: c-f-i

এটি কিভাবে সম্পাদন করে?

d2 <- d[sample(1:3,1e6,TRUE),]
  
library(microbenchmark)
microbenchmark(
  docp = do.call(paste, c(d2[cols], sep="-")),
  tidr = tidyr::unite_(d2, "x", cols, sep="-")$x,
  docs = do.call(sprintf, c(d2[cols], '%s-%s-%s')),
  appl = apply( d2[, cols ] , 1 , paste , collapse = "-" ),
  fpaste = fpaste(d2[cols], "-")$V1,
  dt2 = as.data.table(d2)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols][],
  times=10)
# Unit: milliseconds
#    expr        min         lq      mean     median         uq       max neval
#    docp  215.34536  217.22102  220.3603  221.44104  223.27224  225.0906    10
#    tidr  215.19907  215.81210  220.7131  220.09636  225.32717  229.6822    10
#    docs  281.16679  285.49786  289.4514  286.68738  290.17249  312.5484    10
#    appl 2816.61899 3106.19944 3259.3924 3266.45186 3401.80291 3804.7263    10
#  fpaste   88.57108   89.67795  101.1524   90.59217   91.76415  197.1555    10
#     dt2  301.95508  310.79082  384.8247  316.29807  383.94993  874.4472    10

রমডিস্কে লিখতে পড়তে পড়লে কী হয়? তুলনা একটু বেশি ফর্সা হবে।
— জাঙ্গোরেকি

@ জাঙ্গোরেকি, নিশ্চিত না যে আমি এটি সঠিকভাবে করছি কিনা (আমি আর দিয়ে শুরু TMPDIR=/dev/shm Rকরেছি) তবে এই ফলাফলগুলির তুলনায় আমি একটি বিশাল পার্থক্য লক্ষ্য করি না। আমি ব্যবহৃত থ্রেডের সংখ্যার সাথে freadবা fwriteএটি ফলাফলকে কীভাবে প্রভাবিত করে তা দেখতে মোটেও খেলিনি ।
— A5C1D2H2I1M1N2O1R2T1

1
library(plyr)

ldply(apply(data, 1, function(x) data.frame(
                      x = paste(x[2:4],sep="",collapse="-"))))

#      x
#1 a-d-g
#2 b-e-h
#3 c-f-i

#  and with just the vector of names you have:

ldply(apply(data, 1, function(x) data.frame(
                      x = paste(x[c('b','c','d')],sep="",collapse="-"))))

# or equally:
mynames <-c('b','c','d')
ldply(apply(data, 1, function(x) data.frame(
                      x = paste(x[mynames],sep="",collapse="-"))))    

0

আমি জানি এটি একটি পুরানো প্রশ্ন, তবে আমি ভেবেছিলাম যে প্রশ্নকর্তার পরামর্শ অনুসারে পেস্ট () ফাংশনটি ব্যবহার করে আমার সহজ সমাধানটি উপস্থাপন করা উচিত:

data_1<-data.frame(a=data$a,"x"=paste(data$b,data$c,data$d,sep="-")) 
data_1
  a     x
1 1 a-d-g
2 2 b-e-h
3 3 c-f-i
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.