একটি বৃহত ডেটা.টিবেলে এনএ প্রতিস্থাপনের দ্রুততম উপায়


150

আমার কাছে একটি বিশাল ডেটা টেবিল রয়েছে , যার প্রায় 200 ডলার সারি এবং 200 কলামে ছড়িয়ে থাকা অনেকগুলি মান রয়েছে। আমি সেই জাতীয় মানগুলি যথাসম্ভব দক্ষতার সাথে শূন্যগুলিতে কোড করতে চাই।

আমি দুটি অপশন দেখতে পাচ্ছি:
1: একটি ডেটা.ফ্রেমে রূপান্তর করুন এবং এই জাতীয়
2 ব্যবহার করুন : কিছু ধরণের শীতল ডেটা

আমি টাইপ 1 এর মোটামুটি দক্ষ সমাধানে খুশি হব। একটি ডেটা.ফ্রেমে রূপান্তর করা এবং তারপরে কোনও ডেটাতে ফিরে আসা t টেবিলটি খুব বেশি সময় নেয় না।


5
কেন তুমি রূপান্তর করতে চান না data.tableএকটি থেকে data.frame? ক data.table হয়data.frame। যে কোনও ডেটা ফ্রেম অপারেশন কেবল কাজ করবে will
অ্যান্ড্রি

5
@Andrie। মূল পার্থক্য হ'ল আপনি data.tableকলাম নম্বর নির্দিষ্ট করে একটি কলামে প্রবেশ করতে পারবেন না । তাই DT[,3]তৃতীয় কলামে দেবে না। আমি মনে করি এটি এখানে লিঙ্কে প্রস্তাবিত সমাধানটিকে অগ্রহণযোগ্য করে তোলে। আমি নিশ্চিত যে কিছু data.tableউইজার্ডারি ব্যবহার করে একটি মার্জিত পদ্ধতির উপস্থিতি রয়েছে !
রামনাথ

6
@ রমনাথ, এএফাইক, DT[, 3, with=FALSE]তৃতীয় কলামটি ফিরিয়ে দেয়।
অ্যান্ড্রি

2
@Andrie। তবে mydf[is.na(mydf) == TRUE]ডেটা ফ্রেমে কাজ করার পরেও একটি সমস্যা রয়েছে , mydt[is.na(mydt) == TRUE]আমি ব্যবহার করলেও আমাকে অদ্ভুত কিছু দেয়with=FALSE
রামনাথ

2
@ রমনাথ, পয়েন্ট নেওয়া হয়েছে। আমার আগের বক্তব্যটি খুব বিস্তৃত ছিল, অর্থাৎ আমি ভুল ছিল। দুঃখিত। ডেটা. টেবিলগুলি কেবল তখনই ডেটাফ্রেমগুলির মতো আচরণ করে যখন কোনও ডেটা টেবিল পদ্ধতি থাকে না।
অ্যান্ড্রি

উত্তর:


184

এখানে একটি সলিউশন ব্যবহার এর data.table এর :=অপারেটর, Andrie এবং রামনাথ এর উত্তরের উপর বিল্ডিং।

require(data.table)  # v1.6.6
require(gdata)       # v2.8.2

set.seed(1)
dt1 = create_dt(2e5, 200, 0.1)
dim(dt1)
[1] 200000    200    # more columns than Ramnath's answer which had 5 not 200

f_andrie = function(dt) remove_na(dt)

f_gdata = function(dt, un = 0) gdata::NAToUnknown(dt, un)

f_dowle = function(dt) {     # see EDIT later for more elegant solution
  na.replace = function(v,value=0) { v[is.na(v)] = value; v }
  for (i in names(dt))
    eval(parse(text=paste("dt[,",i,":=na.replace(",i,")]")))
}

system.time(a_gdata = f_gdata(dt1)) 
   user  system elapsed 
 18.805  12.301 134.985 

system.time(a_andrie = f_andrie(dt1))
Error: cannot allocate vector of size 305.2 Mb
Timing stopped at: 14.541 7.764 68.285 

system.time(f_dowle(dt1))
  user  system elapsed 
 7.452   4.144  19.590     # EDIT has faster than this

identical(a_gdata, dt1)   
[1] TRUE

নোট করুন যে রেফারেন্স দ্বারা f_dowle dt1 আপডেট হয়েছে। যদি কোনও স্থানীয় অনুলিপি প্রয়োজন হয় তবে copyপুরো ডেটাসেটের স্থানীয় অনুলিপি তৈরি করতে ফাংশনে একটি স্পষ্ট কল প্রয়োজন। ডেটা.টাবলের setkey, key<-এবং :=অনুলিপি অনুলিপি না।

এরপরে, আসুন দেখা যাক f_dowle কোথায় সময় কাটাচ্ছে।

Rprof()
f_dowle(dt1)
Rprof(NULL)
summaryRprof()
$by.self
                  self.time self.pct total.time total.pct
"na.replace"           5.10    49.71       6.62     64.52
"[.data.table"         2.48    24.17       9.86     96.10
"is.na"                1.52    14.81       1.52     14.81
"gc"                   0.22     2.14       0.22      2.14
"unique"               0.14     1.36       0.16      1.56
... snip ...

সেখানে আমি মনোনিবেশ করব na.replaceএবং is.naযেখানে কয়েকটি ভেক্টর অনুলিপি এবং ভেক্টর স্ক্যান রয়েছে। এগুলি খুব সহজেই একটি ছোট না.রেপ্লেস সি ফাংশন লিখে ভ্যাক্টরের NAরেফারেন্স অনুসারে আপডেট করা যায়। এটি আমার মনে হয় কমপক্ষে 20 সেকেন্ডে অর্ধেক হয়ে যাবে। কোনও আর প্যাকেজে কি এই জাতীয় ফাংশন বিদ্যমান?

কারণ f_andrieব্যর্থ হতে পারে, কারণ এটি কপি পুরো dt1বা পুরো হিসাবে হিসাবে বড় একটি লজিক্যাল ম্যাট্রিক্স সৃষ্টি dt1, কয়েক বার। অন্যান্য 2 টি পদ্ধতি একবারে একটি কলামে কাজ করে (যদিও আমি কেবল সংক্ষেপে তাকিয়েছি NAToUnknown)।

সম্পাদনা (মন্তব্যগুলিতে রামনাথের অনুরোধ হিসাবে আরও মার্জিত সমাধান):

f_dowle2 = function(DT) {
  for (i in names(DT))
    DT[is.na(get(i)), (i):=0]
}

system.time(f_dowle2(dt1))
  user  system elapsed 
 6.468   0.760   7.250   # faster, too

identical(a_gdata, dt1)   
[1] TRUE

আমি আশা করি আমি এটি দিয়ে শুরু করেছিলাম!

EDIT2 (এক বছরেরও বেশি পরে, এখন)

এছাড়াও আছে set()। এটি যদি [,:=,]লুপের মধ্যে কলিংয়ের (ছোট) ওভারহেডকে এড়িয়ে চলে তবে প্রচুর কলামটি লুপ করা হচ্ছে এমনটি দ্রুত হতে পারে । setএকটি লুপযোগ্য :=। দেখুন ?set

f_dowle3 = function(DT) {
  # either of the following for loops

  # by name :
  for (j in names(DT))
    set(DT,which(is.na(DT[[j]])),j,0)

  # or by number (slightly faster than by name) :
  for (j in seq_len(ncol(DT)))
    set(DT,which(is.na(DT[[j]])),j,0)
}

5
+ +! দুর্দান্ত উত্তর! স্টাফের আরও স্বজ্ঞাত সমতুল্য থাকা কি সম্ভব eval(parse)...? একটি বৃহত্তর নোট, আমি মনে করি এটা অপারেশনের সব উপাদান যা কাজ আছে দরকারী হবে data.table
রামনাথ

1
আপনার কোডের ২ য় ব্লক এটি করার পক্ষে সবচেয়ে data.tableউপযুক্ত উপায় বলে মনে হচ্ছে । ধন্যবাদ!
জাচ

3
@ স্টাটভনক আমি অনুমান করি যে এই পরীক্ষার উদাহরণগুলির থেকে ভিন্ন, আপনার DTধরণের কলাম রয়েছে । কলটির চতুর্থ যুক্তি (যা আপনার উদাহরণে রয়েছে এবং আর ডাবল টাইপ করুন) তে পরিবর্তন করুন এবং এটি কোনও সতর্কতা ছাড়াই কাজ করা উচিত। logicalcreate_dt()set()0FALSE
ম্যাট ডাউলে

2
@ স্ট্যাটভনক এবং আমি এই কেসটি শিথিল করার জন্য একটি বৈশিষ্ট্য অনুরোধ পেশ করেছি এবং দৈর্ঘ্য -১ ভেক্টরকে 0 এবং 1 টি যৌক্তিকভাবে যুক্ত করার সময় এই সতর্কতাটি ছেড়ে দিতে হবে: # 996 । গতি জন্য আপনি এটি অপ্রয়োজনীয় পুনরাবৃত্তিমূলক কর্কিং সম্পর্কে সতর্ক হতে চান।
ম্যাট ডাউলে

1
@ স্টেফানএফ সত্য এবং আমি seq_along(DT)খুব পছন্দ করি। তবে পাঠককে জানতে হবে যে seq_alongকলামগুলির সাথে থাকবে এবং সারিগুলির নীচে থাকবে না। seq_len(col(DT))এই কারণে একটি সামান্য বিট আরও স্পষ্ট।
ম্যাট ডাউলে

28

আমি এখানে আসতে পারি এমন সহজতমটি এখানে:

dt[is.na(dt)] <- 0

এটি দক্ষ এবং ফাংশন এবং অন্যান্য আঠালো কোড লেখার দরকার নেই।


বড় ডেটাসেট এবং সাধারণ ওয়ার্কস্টেশন কম্পিউটারে কাজ করে না (মেমরি বরাদ্দ ত্রুটি)
জেক

3
@ জ্যাকটি 16 গিগাবাইট র‍্যাম সহ একটি মেশিনে জেক করুন আমি এটিকে 31 এম সারি, ~ 20 কলামে চালাতে সক্ষম হয়েছি। অবশ্যই ওয়াইএমএমভি।
বার

আমি আপনার অভিজ্ঞতা অভিজ্ঞতা মুলতবি। ধন্যবাদ।
জ্যাক

10
ডেটা এর সর্বশেষতম সংস্করণে দুর্ভাগ্যজনকভাবে। এটি কার্যকর হয় না। এটি [.data.tableত্রুটিটি (ডিটি, ইস.না (ডিটি)) এ বলেছে: আমি অবৈধ টাইপ (ম্যাট্রিক্স)। সম্ভবত ভবিষ্যতে একটি 2 কলামের ম্যাট্রিক্স টিটি উপাদানগুলির একটি তালিকা ফিরে আসতে পারে (এফএকিউ 2.14-এ এ [বি] এর আত্মায়)। আপনি এটি পছন্দ করেন কিনা তা দয়া করে ডেটাটেবল-সহায়তা জানান, বা আপনার মন্তব্যগুলি এফআর # 657 এ যুক্ত করুন। >

এটা মজার! আমি সর্বদা ব্যবহার করিset
মার্বেল

15

এই উদ্দেশ্যে ডেডিকেটেড ফাংশন ( nafillএবং setnafill) পাওয়া যায়data.table প্যাকেজে (সংস্করণ> = 1.12.4):

এটি সমান্তরালভাবে কলামগুলি প্রক্রিয়া করে যাতে পূর্বে পোস্ট হওয়া বেঞ্চমার্কগুলির ঠিক নীচে, এখন অবধি দ্রুততম পদ্ধতির তুলনায়, এবং 40 কোরের মেশিন ব্যবহার করে এটিও ছোট করে দেওয়া হয়।

library(data.table)
create_dt <- function(nrow=5, ncol=5, propNA = 0.5){
  v <- runif(nrow * ncol)
  v[sample(seq_len(nrow*ncol), propNA * nrow*ncol)] <- NA
  data.table(matrix(v, ncol=ncol))
}
f_dowle3 = function(DT) {
  for (j in seq_len(ncol(DT)))
    set(DT,which(is.na(DT[[j]])),j,0)
}

set.seed(1)
dt1 = create_dt(2e5, 200, 0.1)
dim(dt1)
#[1] 200000    200
dt2 = copy(dt1)
system.time(f_dowle3(dt1))
#   user  system elapsed 
#  0.193   0.062   0.254 
system.time(setnafill(dt2, fill=0))
#   user  system elapsed 
#  0.633   0.000   0.020   ## setDTthreads(1) elapsed: 0.149
all.equal(dt1, dt2)
#[1] TRUE

set.seed(1)
dt1 = create_dt(2e7, 200, 0.1)
dim(dt1)
#[1] 20000000    200
dt2 = copy(dt1)
system.time(f_dowle3(dt1))
#   user  system elapsed 
# 22.997  18.179  41.496
system.time(setnafill(dt2, fill=0))
#   user  system elapsed 
# 39.604  36.805   3.798 
all.equal(dt1, dt2)
#[1] TRUE

এটি একটি দুর্দান্ত বৈশিষ্ট্য! আপনি কি চরিত্রের কলামগুলির জন্য সমর্থন যুক্ত করার পরিকল্পনা করছেন? তাহলে এটি এখানে ব্যবহার করা যেতে পারে ।
ismirsehregal

1
@ ইমিসিরহেরেগাল হ্যাঁ, আপনি এখানে এই বৈশিষ্ট্যটি ট্র্যাক করতে পারবেন github.com/Rdatatable/data.table/issues/3992
jangorecki

12
library(data.table)

DT = data.table(a=c(1,"A",NA),b=c(4,NA,"B"))

DT
    a  b
1:  1  4
2:  A NA
3: NA  B

DT[,lapply(.SD,function(x){ifelse(is.na(x),0,x)})]
   a b
1: 1 4
2: A 0
3: 0 B

কেবল রেফারেন্সের জন্য, জিডিটা বা ডেটা.ম্যাট্রিক্সের তুলনায় ধীর গতিতে, তবে কেবল ডেটা টেবিল প্যাকেজটি ব্যবহার করে এবং সংখ্যাসূচক এন্ট্রিগুলি মোকাবেলা করতে পারে।


5
আপনি সম্ভবত উভয় এড়ানো ifelseএবং রেফারেন্স দ্বারা আপডেট করে DT[, names(DT) := lapply(.SD, function(x) {x[is.na(x)] <- "0" ; x})]। এবং আমি সন্দেহ করি যে আপনি যে উত্তরগুলি উল্লেখ করেছেন তার চেয়ে ধীর হবে।
ডেভিড আরেনবুর্গ

11

এখানে একটি সমাধান ব্যবহার করছে NAToUnknowngdataপ্যাকেজ। আমি একটি বিশাল ডেটা টেবিল তৈরি করতে অ্যান্ডির সমাধানটি ব্যবহার করেছি এবং অ্যান্ড্রির সমাধানের সাথে সময়ের তুলনাও অন্তর্ভুক্ত করেছি।

# CREATE DATA TABLE
dt1 = create_dt(2e5, 200, 0.1)

# FUNCTIONS TO SET NA TO ZERO   
f_gdata  = function(dt, un = 0) gdata::NAToUnknown(dt, un)
f_Andrie = function(dt) remove_na(dt)

# COMPARE SOLUTIONS AND TIMES
system.time(a_gdata  <- f_gdata(dt1))

user  system elapsed 
4.224   2.962   7.388 

system.time(a_andrie <- f_Andrie(dt1))

 user  system elapsed 
4.635   4.730  20.060 

identical(a_gdata, g_andrie)  

TRUE

+1 ভাল সন্ধান করুন। আকর্ষণীয় - আমি প্রথমবারের মতো সময়গুলির সাথে সময় দেখছি তবে সময়ের সাথে userসত্যই বড় পার্থক্য রয়েছে elapsed
অ্যান্ড্রি

@ অ্যান্ড্রি আমি rbenchmarkআরও প্রতিলিপি ব্যবহার করে সমাধানগুলি বেঞ্চমার্কে ব্যবহার করার চেষ্টা করেছি , তবে সম্ভবত ডেটা ফ্রেমের আকারের কারণে মেমরির ত্রুটিটি ছাড়িয়ে গেছে। আপনি যদি benchmarkউভয় সমাধানকে একাধিক প্রতিলিপি দিয়ে চালাতে পারেন তবে সেগুলি ফলাফল আকর্ষণীয় হবে কারণ আমি সত্যিই নিশ্চিত নই কেন আমি 3x স্পিডআপ পাচ্ছি
রামনাথ

@ রমনাথ জিনিসগুলি সঠিক করার জন্য, এই উত্তরটির সময়গুলি ncol=5আমার মনে হয় (বাগ অনেক বেশি সময় নেওয়া উচিত) create_dt
ম্যাট ডাউলে

5

সম্পূর্ণতার স্বার্থে, 0 এর সাথে এনএ প্রতিস্থাপনের অন্য উপায়টি ব্যবহার করা

f_rep <- function(dt) {
dt[is.na(dt)] <- 0
return(dt)
}

ফলাফল এবং সময়গুলির তুলনা করতে আমি এখন পর্যন্ত উল্লিখিত সমস্ত পন্থাকে একত্রিত করেছি।

set.seed(1)
dt1 <- create_dt(2e5, 200, 0.1)
dt2 <- dt1
dt3 <- dt1

system.time(res1 <- f_gdata(dt1))
   User      System verstrichen 
   3.62        0.22        3.84 
system.time(res2 <- f_andrie(dt1))
   User      System verstrichen 
   2.95        0.33        3.28 
system.time(f_dowle2(dt2))
   User      System verstrichen 
   0.78        0.00        0.78 
system.time(f_dowle3(dt3))
   User      System verstrichen 
   0.17        0.00        0.17 
system.time(res3 <- f_unknown(dt1))
   User      System verstrichen 
   6.71        0.84        7.55 
system.time(res4 <- f_rep(dt1))
   User      System verstrichen 
   0.32        0.00        0.32 

identical(res1, res2) & identical(res2, res3) & identical(res3, res4) & identical(res4, dt2) & identical(dt2, dt3)
[1] TRUE

সুতরাং নতুন পদ্ধতির তুলনায় সামান্য ধীর f_dowle3কিন্তু অন্য সমস্ত পদ্ধতির চেয়ে দ্রুত। তবে সত্যি বলতে কী, এটি আমার ডেটা.ট্যাবিল সিনট্যাক্সের অন্তর্দৃষ্টিবিরোধী এবং এটি কেন কাজ করে তা আমার কোনও ধারণা নেই। আমাকে কেউ আলোকিত করতে পারে?


1
হ্যাঁ আমি সেগুলি পরীক্ষা করেছিলাম, এ কারণেই আমি জুটিযুক্ত পরিচয় অন্তর্ভুক্ত করেছি।
bratwoorst711

1
এখানে একটি কারণ কেন এটিকে কথ্য পথ নয় নেই - stackoverflow.com/a/20545629
Naumz

4

আমার বোধগম্যতা হল যে আর এর দ্রুত অপারেশনের গোপনীয়তা হল ভেক্টর (বা অ্যারেগুলি যা হুডের নীচে ভেক্টর রয়েছে) ব্যবহার করা)

এই সমাধানে আমি একটি ব্যবহার করি data.matrixযা একটি arrayতবে কিছুটা এর মতো আচরণ করে data.frame। এটি একটি অ্যারে হওয়ায় আপনি এর প্রতিস্থাপনের জন্য খুব সাধারণ ভেক্টর বিকল্প ব্যবহার করতে পারেন NA:

NAগুলি সরাতে একটি সামান্য সহায়ক ফাংশন । সারমর্মটি কোডের একক লাইন। আমি কেবল মৃত্যুদন্ড কার্যকর করার সময়টি পরিমাপ করার জন্য এটি করি।

remove_na <- function(x){
  dm <- data.matrix(x)
  dm[is.na(dm)] <- 0
  data.table(dm)
}

data.tableপ্রদত্ত আকারের একটি তৈরি করতে একটু সহায়ক ফাংশন ।

create_dt <- function(nrow=5, ncol=5, propNA = 0.5){
  v <- runif(nrow * ncol)
  v[sample(seq_len(nrow*ncol), propNA * nrow*ncol)] <- NA
  data.table(matrix(v, ncol=ncol))
}

একটি ক্ষুদ্র নমুনার উপর বিক্ষোভ:

library(data.table)
set.seed(1)
dt <- create_dt(5, 5, 0.5)

dt
            V1        V2        V3        V4        V5
[1,]        NA 0.8983897        NA 0.4976992 0.9347052
[2,] 0.3721239 0.9446753        NA 0.7176185 0.2121425
[3,] 0.5728534        NA 0.6870228 0.9919061        NA
[4,]        NA        NA        NA        NA 0.1255551
[5,] 0.2016819        NA 0.7698414        NA        NA

remove_na(dt)
            V1        V2        V3        V4        V5
[1,] 0.0000000 0.8983897 0.0000000 0.4976992 0.9347052
[2,] 0.3721239 0.9446753 0.0000000 0.7176185 0.2121425
[3,] 0.5728534 0.0000000 0.6870228 0.9919061 0.0000000
[4,] 0.0000000 0.0000000 0.0000000 0.0000000 0.1255551
[5,] 0.2016819 0.0000000 0.7698414 0.0000000 0.0000000

এটি একটি খুব সুন্দর উদাহরণ ডেটাসেট। আমি চেষ্টা করব এবং উন্নতি করব remove_na। 21.57 এর সময়কালে এর সাথে create_dt(সহ runifএবং sample) একসাথে অন্তর্ভুক্ত থাকে remove_na। কোনও সুযোগ আপনি 2 বার বিভক্ত করতে সম্পাদনা করতে পারেন?
ম্যাট ডাওল

একটি ছোট বাগ আছে create_dt? এটি সর্বদা 5 টি কলামের ডেটা তৈরি করে বলে মনে হচ্ছে t এটি নির্বিশেষে ncolপাস করুন
ম্যাট ডাউল

পছন্দ করুন ত্রুটি সরানো হয়েছে (পাশাপাশি সময়গুলিও)
অ্যান্ড্রি

ম্যাট্রিক্সে রূপান্তর করা কেবলমাত্র সঠিকভাবে কাজ করবে যদি সমস্ত কলাম একই ধরণের হয়।
স্ক্যান

2

অনেক কলামে সাধারণ করার জন্য আপনি এই পদ্ধতির ব্যবহার করতে পারেন (পূর্ববর্তী নমুনা ডেটা ব্যবহার করে তবে একটি কলাম যুক্ত করে):

z = data.table(x = sample(c(NA_integer_, 1), 2e7, TRUE), y = sample(c(NA_integer_, 1), 2e7, TRUE))

z[, names(z) := lapply(.SD, function(x) fifelse(is.na(x), 0, x))]

যদিও গতির জন্য পরীক্ষা করিনি


1
> DT = data.table(a=LETTERS[c(1,1:3,4:7)],b=sample(c(15,51,NA,12,21),8,T),key="a")
> DT
   a  b
1: A 12
2: A NA
3: B 15
4: C NA
5: D 51
6: E NA
7: F 15
8: G 51
> DT[is.na(b),b:=0]
> DT
   a  b
1: A 12
2: A  0
3: B 15
4: C  0
5: D 51
6: E  0
7: F 15
8: G 51
> 

3
এবং কীভাবে আপনি একাধিক কলামে এটি সাধারণ করবেন?
ডেভিড আরেনবুর্গ

@ ডেভিড আরেনবুর্গ কেবল লুপের জন্য একটি লিখুন। এটি গ্রহণযোগ্য উত্তর হওয়া উচিত: এটি সবচেয়ে সহজ!
বাইবো

1

ব্যবহার fifelseনবীনতম থেকে ফাংশন data.tableসংস্করণ 1.12.6, এটা চেয়ে আরও 10 গুণ বেশি দ্রুত NAToUnknownমধ্যে gdataপ্যাকেজ:

z = data.table(x = sample(c(NA_integer_, 1), 2e7, TRUE))
system.time(z[,x1 := gdata::NAToUnknown(x, 0)])

#   user  system elapsed 
#  0.798   0.323   1.173 
system.time(z[,x2:= fifelse(is.na(x), 0, x)])

#   user  system elapsed 
#  0.172   0.093   0.113 

আপনি কি এই উত্তরের সাথে কিছু সময়ের তুলনা যুক্ত করতে পারেন? আমার ধারণা f_dowle3এখনও তত দ্রুত হবে: stackoverflow.com/a/7249454/345660
Zach
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.