ডেটা.ট্যাবেলে প্রতিটি নির্দিষ্ট কলামে কীভাবে একই ফাংশনটি প্রয়োগ করতে হয়


86

আমার একটি ডেটা টেবিল রয়েছে যার সাথে আমি নির্দিষ্ট কলামগুলিতে একই ক্রিয়াকলাপটি সম্পাদন করতে চাই। এই কলামগুলির নামগুলি একটি চরিত্র ভেক্টরে দেওয়া আছে। এই বিশেষ উদাহরণে, আমি এই সমস্ত কলাম -1 দ্বারা গুণ করতে চাই।

কিছু খেলনা তথ্য এবং একটি ভেক্টর প্রাসঙ্গিক কলামগুলি নির্দিষ্ট করে:

library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")

এই মুহূর্তে আমি এটি এইভাবে করছি, চরিত্রের ভেক্টরটির লুপিং:

for (col in 1:length(cols)) {
   dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}

লুপের জন্য সরাসরি ছাড়া এটি করার কোনও উপায় আছে?

উত্তর:


151

এটি কাজ করে বলে মনে হচ্ছে:

dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols]

ফলাফল হলো

    a  b d
1: -1 -1 1
2: -2 -2 2
3: -3 -3 3

এখানে কয়েকটি কৌশল রয়েছে:

  • কারণ এখানে বন্ধনী রয়েছে (cols) :=, ফলাফলটি নির্দিষ্ট করা কলামগুলিতে বরাদ্দ করা হয়েছে cols, "কলস" নামে কিছু নতুন ভেরিয়েবলের পরিবর্তে।
  • .SDcolsকল যে আমরা কেবল সেই কলাম এ খুঁজছেন, এবং আমাদের ব্যবহার করার অনুমতি দেয় বলে .SD, Sএর ubset Dযারা কলাম যুক্ত ATA।
  • lapply(.SD, ...)এটি পরিচালনা করে .SD, যা কলামগুলির একটি তালিকা (সমস্ত ডেটা ফ্রেম এবং ডেটা. টেবিলের মতো)। lapplyএকটি তালিকা ফেরত দেয়, তাই শেষ পর্যন্ত jদেখতে cols := list(...)

সম্পাদনা : এখানে আরও একটি উপায় সম্ভবত দ্রুত যা সম্ভবত @ অরুন উল্লেখ করেছেন:

for (j in cols) set(dt, j = j, value = -dt[[j]])

22
অন্য উপায় একটি সঙ্গে ব্যবহার করা setহয় for-loop। আমি সন্দেহ করি এটি আরও দ্রুত হবে।
অরুণ

4
অরুন আমি একটি সম্পাদনা করেছি। আপনি কি বোঝাতে চেয়েছিলেন? আমি setআগে ব্যবহার করিনি।
ফ্রাঙ্ক

8
+1 দুর্দান্ত উত্তর। হ্যাঁ আমি এই জাতীয় ক্ষেত্রেও একটি forলুপ পছন্দ setকরি।
ম্যাট ডাওল

4
হ্যাঁ, ব্যবহারটি set()দ্রুত বলে মনে হচ্ছে, আমার ডেটাসেটের জন্য 4 গুণ দ্রুত! বিস্ময়কর।
কনস্টান্টিনোস

4
ধন্যবাদ, @ জেমসহির্সকর্ন আমি নিশ্চিত নই, তবে আমি সন্দেহ করি যে সেভাবে কলামগুলি সাবসেট করার চেয়ে আরও বেশি ওভারহেড রয়েছে over এসডি, যেভাবেই হোক না কেন স্ট্যান্ডার্ড আইডিয়ম, ইন্ট্রো ভিগনেট github.com/Rdatatable/data.table/wiki/Getting-start এ উপস্থিত মূর্খতার কারণগুলির একটি অংশ, আমার মনে হয়, টেবিলের নামটি দু'বার টাইপ করা এড়ানো।
ফ্রাঙ্ক

20

আমি একটি উত্তর যুক্ত করতে চাই, যখন আপনি কলামগুলির নামও পরিবর্তন করতে চান। আপনি একাধিক কলামের লগারিদম গণনা করতে চাইলে এটি বেশ কার্যকর হয়, যা প্রায়শই অভিজ্ঞতা অভিজ্ঞতাতে ঘটে।

cols <- c("a", "b")
out_cols = paste("log", cols, sep = ".")
dt[, c(out_cols) := lapply(.SD, function(x){log(x = x, base = exp(1))}), .SDcols = cols]

4
কোনও নিয়মের ভিত্তিতে নাম পরিবর্তন করার কোনও উপায় আছে কি? Dplyr এ, উদাহরণস্বরূপ, আপনি আইরিস%>% মিউটেট_্যাট (ওয়ার্স (ম্যাচগুলি ("সেপাল")), তালিকা (টাইম_টুই = ~। * 2)) করতে পারেন এবং এটি নতুন নামগুলিতে "_টাইম_টুই" যুক্ত করবে।
kennyB

4
আমি মনে করি না এটি সম্ভব, তবে এটি সম্পর্কে সত্যই নিশ্চিত নয়।
hannes101

এটি স্থানে out_colsরেখে যাওয়ার সাথে সাথে নামগুলির সাথে কলাম যুক্ত করবে cols। সুতরাং, আপনাকে সেগুলি স্পষ্টভাবে 1 দ্বারা বিলোপ করতে হবে) কেবল লগ.এ এবং লগ.বি: বিয়ের [,.(outcols)]শেষে চেইন এ এবং এর dtমাধ্যমে পুনরায় স্টোর করতে হবে <-। 2) একটি শৃঙ্খলযুক্ত সঙ্গে পুরানো কলামগুলি সরান [,c(cols):=NULL]। একটি dt[,c(cols):=...]setnames(dt, cols, newcols)
চেইনবিহীন

@ এমপ্যাগ, হ্যাঁ এটি সত্য, তবে অভিজ্ঞতাবাদী গবেষণার ক্ষেত্রে আমার ব্যবহারের ক্ষেত্রে আমার বেশিরভাগ সময় ডেটা সেটে উভয় সিরিজের প্রয়োজন হয়।
hannes101

11

আপডেট: লুপ ছাড়া এটি করার একটি পরিষ্কার উপায় নিম্নলিখিত

dt[,(cols):= - dt[,..cols]]

এটি সহজ কোড পঠনযোগ্যতার জন্য একটি ঝরঝরে উপায়। পারফরম্যান্সের ক্ষেত্রে এটি নীচের মাইক্রোব্যাঙ্কমার্ক ফলাফল অনুযায়ী ফ্র্যাঙ্কের সমাধানের পিছনে থেকে যায়

mbm = microbenchmark(
  base = for (col in 1:length(cols)) {
    dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
  },
  franks_solution1 = dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols],
  franks_solution2 =  for (j in cols) set(dt, j = j, value = -dt[[j]]),
  hannes_solution = dt[, c(out_cols) := lapply(.SD, function(x){log(x = x, base = exp(1))}), .SDcols = cols],
  orhans_solution = for (j in cols) dt[,(j):= -1 * dt[,  ..j]],
  orhans_solution2 = dt[,(cols):= - dt[,..cols]],
  times=1000
)
mbm

Unit: microseconds
expr                  min        lq      mean    median       uq       max neval
base_solution    3874.048 4184.4070 5205.8782 4452.5090 5127.586 69641.789  1000  
franks_solution1  313.846  349.1285  448.4770  379.8970  447.384  5654.149  1000    
franks_solution2 1500.306 1667.6910 2041.6134 1774.3580 1961.229  9723.070  1000    
hannes_solution   326.154  405.5385  561.8263  495.1795  576.000 12432.400  1000
orhans_solution  3747.690 4008.8175 5029.8333 4299.4840 4933.739 35025.202  1000  
orhans_solution2  752.000  831.5900 1061.6974  897.6405 1026.872  9913.018  1000

যেমন নিচের চার্টে দেখানো হয়েছে

পারফরম্যান্স_মিলন_চার্ট t

আমার পূর্ববর্তী উত্তর: নিম্নলিখিতটিও কাজ করে

for (j in cols)
  dt[,(j):= -1 * dt[,  ..j]]

দেড় বছর আগে ফ্র্যাঙ্কের উত্তর হিসাবে এটি মূলত একই জিনিস।
ডিন ম্যাকগ্রিগোর

4
ধন্যবাদ, ফ্র্যাঙ্কের উত্তরটি সেটটি ব্যবহার করছিল। আমি যখন কয়েক মিলিয়ন সারি দিয়ে বৃহত ডেটা. টেবিলের সাথে কাজ করি, তখন আমি দেখতে পাই: = অপারেটর ফাংশনগুলিকে ছাড়িয়ে যায়
অরহান সেলিক

4
আমি কোনও পুরানো প্রশ্নের উত্তর যুক্ত করার কারণটি নিম্নরূপ: আমারও একই রকম সমস্যা ছিল, আমি গুগল অনুসন্ধান নিয়ে এই পোস্টটি জুড়ে এসেছি। এরপরে আমি আমার সমস্যার সমাধান খুঁজে পেয়েছি এবং আমি দেখতে পাই এটি এখানেও প্রযোজ্য। প্রকৃতপক্ষে আমার পরামর্শটি ডেটা.টিবেলের একটি নতুন ফাংশন ব্যবহার করে যা গ্রন্থাগারের নতুন সংস্করণে পাওয়া যায় যা প্রশ্নের সময় উপস্থিত ছিল না। আমি ভাবলাম এটি ভাগ করে নেওয়া ভাল ধারণা, অনুরূপ সমস্যা নিয়ে অন্যদের ভাবার কথা গুগল অনুসন্ধানের সাথে এখানেই শেষ হবে।
অরহান সেলিক

4
আপনি কি dt3 টি সারি নিয়ে বেঞ্চমার্ক করছেন ?
উয়েউ

4
হ্যানসের উত্তরটি আলাদা গণনা করছে এবং তাই অন্যের সাথে তুলনা করা উচিত নয়, তাই না?
ফ্রাঙ্ক

2

উপরের সমাধানগুলির কোনওটিই গ্রুপ অনুসারে গণনা নিয়ে কাজ করছে বলে মনে হয় না। নিম্নলিখিতটি আমি পেয়েছি সেরা:

for(col in cols)
{
    DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g]
}

1

কলামগুলির একটি স্ট্রিং ভেক্টরের ভিত্তিতে নতুন কলামগুলি তৈরি করতে উদাহরণ যুক্ত করতে। জেফ্লাই উত্তরের ভিত্তিতে:

dt <- data.table(a = rnorm(1:100), b = rnorm(1:100), c = rnorm(1:100), g = c(rep(1:10, 10)))

col0 <- c("a", "b", "c")
col1 <- paste0("max.", col0)  

for(i in seq_along(col0)) {
  dt[, (col1[i]) := max(get(col0[i])), g]
}

dt[,.N, c("g", col1)]

0
library(data.table)
(dt <- data.table(a = 1:3, b = 1:3, d = 1:3))

Hence:

   a b d
1: 1 1 1
2: 2 2 2
3: 3 3 3

Whereas (dt*(-1)) yields:

    a  b  d
1: -1 -1 -1
2: -2 -2 -2
3: -3 -3 -3

4
Fyi, শিরোনামে "প্রতিটি নির্দিষ্ট কলাম" এর অর্থ হ'ল প্রশ্নকর্তা এটি কলামগুলির একটি উপসেটে প্রয়োগ করতে আগ্রহী ছিলেন (সম্ভবত তারা সমস্তই নয়)।
ফ্রাঙ্ক

4
নিশ্চিত! সেক্ষেত্রে ওপি dt [, c ("a", "b")] * (- 1) সম্পাদন করতে পারে।
amonk

4
ঠিক আছে, আসুন আমরা সম্পূর্ণ হয়ে যাই এবং বলিdt[, cols] <- dt[, cols] * (-1)
গ্রেগর থমাস

নতুন সিনট্যাক্সটি প্রয়োজনীয় বলে মনে হচ্ছে ডিটি [, কলস] <- ডিটি [, ..কলস] * (-1)
আর্থার ইপ

0

dplyrফাংশনগুলি এস এ কাজ করে data.table, সুতরাং এখানে একটি dplyrসমাধান রয়েছে যা "ফর-লুপ এড়িয়ে যায়" :)

dt %>% mutate(across(all_of(cols), ~ -1 * .))

আমি Orhan এর কোড (সারি এবং কলাম যোগ) ব্যবহার করে এটা benchmarked এবং আপনি দেখতে পাবেন dplyr::mutateসঙ্গে acrossবেশিরভাগই ধীর অন্যান্য সমাধানের অধিকাংশ তুলনায় দ্রুততর এবং data.table সমাধান lapply ব্যবহার না করে সঞ্চালন করে।

library(data.table); library(dplyr)
dt <- data.table(a = 1:100000, b = 1:100000, d = 1:100000) %>% 
  mutate(a2 = a, a3 = a, a4 = a, a5 = a, a6 = a)
cols <- c("a", "b", "a2", "a3", "a4", "a5", "a6")

dt %>% mutate(across(all_of(cols), ~ -1 * .))
#>               a       b      d      a2      a3      a4      a5      a6
#>      1:      -1      -1      1      -1      -1      -1      -1      -1
#>      2:      -2      -2      2      -2      -2      -2      -2      -2
#>      3:      -3      -3      3      -3      -3      -3      -3      -3
#>      4:      -4      -4      4      -4      -4      -4      -4      -4
#>      5:      -5      -5      5      -5      -5      -5      -5      -5
#>     ---                                                               
#>  99996:  -99996  -99996  99996  -99996  -99996  -99996  -99996  -99996
#>  99997:  -99997  -99997  99997  -99997  -99997  -99997  -99997  -99997
#>  99998:  -99998  -99998  99998  -99998  -99998  -99998  -99998  -99998
#>  99999:  -99999  -99999  99999  -99999  -99999  -99999  -99999  -99999
#> 100000: -100000 -100000 100000 -100000 -100000 -100000 -100000 -100000

library(microbenchmark)
mbm = microbenchmark(
  base_with_forloop = for (col in 1:length(cols)) {
    dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
  },
  franks_soln1_w_lapply = dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols],
  franks_soln2_w_forloop =  for (j in cols) set(dt, j = j, value = -dt[[j]]),
  orhans_soln_w_forloop = for (j in cols) dt[,(j):= -1 * dt[,  ..j]],
  orhans_soln2 = dt[,(cols):= - dt[,..cols]],
  dplyr_soln = (dt %>% mutate(across(all_of(cols), ~ -1 * .))),
  times=1000
)

library(ggplot2)
ggplot(mbm) +
  geom_violin(aes(x = expr, y = time)) +
  coord_flip()

2020-10-16 তারিখে ডিপেক্স প্যাকেজ দ্বারা নির্মিত (v0.3.0)

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.