যখন কোনও ডেটা.ট্যাবলটি অন্য ডেটা.টেবেলের (বনামের অনুলিপি) একটি রেফারেন্স হয় ঠিক তখন বুঝতে


194

পাস-বাই-রেফারেন্স বৈশিষ্ট্যগুলি বুঝতে আমার কিছুটা সমস্যা হচ্ছে data.table। কিছু ক্রিয়াকলাপ রেফারেন্সটিকে 'ভাঙ্গা' বলে মনে হচ্ছে এবং আমি ঠিক কী ঘটছে তা বুঝতে চাই।

data.tableঅন্যটি থেকে তৈরি করার data.tableমাধ্যমে (এর মাধ্যমে <-নতুন টেবিলটি আপডেট করে :=, মূল টেবিলটিও পরিবর্তন করা হবে per

?data.table::copy এবং স্ট্যাকওভারফ্লো: ডেটা-টেবিল-প্যাকেজ-ইন-অপারেটর-অনুসারে-রেফারেন্স

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

library(data.table)

DT <- data.table(a=c(1,2), b=c(11,12))
print(DT)
#      a  b
# [1,] 1 11
# [2,] 2 12

newDT <- DT        # reference, not copy
newDT[1, a := 100] # modify new DT

print(DT)          # DT is modified too.
#        a  b
# [1,] 100 11
# [2,]   2 12

যাইহোক, যদি আমি অ্যাসাইনমেন্ট এবং উপরের লাইনগুলির :=মধ্যে একটি ভিত্তি ভিত্তিক পরিবর্তন সন্নিবেশ করি তবে এখন আর পরিবর্তিত হবে না:<-:=DT

DT = data.table(a=c(1,2), b=c(11,12))
newDT <- DT        
newDT$b[2] <- 200  # new operation
newDT[1, a := 100]

print(DT)
#      a  b
# [1,] 1 11
# [2,] 2 12

সুতরাং মনে হয় newDT$b[2] <- 200রেখাটি কোনওভাবে রেফারেন্সটিকে 'ব্রেক' করে। আমি অনুমান করতে পারি যে এটি কোনওরকম একটি অনুলিপি চেয়েছিল, তবে আমি পুরোপুরি বুঝতে চাই যে কীভাবে আর এই ক্রিয়াকলাপগুলি চিকিত্সা করছেন, যাতে আমি আমার কোডে সম্ভাব্য বাগগুলি প্রবর্তন না করি তা নিশ্চিত করে।

যদি কেউ আমার কাছে এটি ব্যাখ্যা করতে পারে তবে আমি খুব প্রশংসা করব।


1
আমি এই "বৈশিষ্ট্য" সবেমাত্র আবিষ্কার করেছি এবং এটি ভয়াবহ। এটি ইন্টারনেটসকে আর-তে বেসিক অ্যাসাইনমেন্টের <-পরিবর্তে ব্যবহার করার পক্ষে ব্যাপকভাবে পরামর্শ দেওয়া হয় =(যেমন গুগল: google.github.io/styleguide/Rguide.xML#assignment )। তবে এর অর্থ হ'ল ডেটা.ট্যাবল ম্যানিপুলেশন ডেটা ফ্রেম ম্যানিপুলেশনের মতোই কাজ করবে না এবং তাই ডেটা ফ্রেমে ড্রপ-ইন প্রতিস্থাপন থেকে অনেক দূরে।
সিএমও

উত্তর:


141

হ্যাঁ, এটি আর এর মধ্যে সাবসাইনমেন্ট <-(বা =বা ->) ব্যবহার করে যা পুরো বস্তুর অনুলিপি তৈরি করে । আপনি এটি ব্যবহার করে tracemem(DT)এবং .Internal(inspect(DT))নীচের মতো করেও ট্রেস করতে পারেন । data.tableবৈশিষ্ট্য :=এবং set()যাই হোক না কেন রেফারেন্স দ্বারা বরাদ্দ আপত্তি তারা পাঠানো হয়। সুতরাং যদি সেই বস্তুটি আগে অনুলিপি করা হয়েছিল (সাবসাইনিং <-বা স্পষ্ট করে copy(DT)) তবে এটি সেই অনুলিপি যা রেফারেন্সের মাধ্যমে সংশোধিত হয়।

DT <- data.table(a = c(1, 2), b = c(11, 12)) 
newDT <- DT 

.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB:  # ..snip..

.Internal(inspect(newDT))   # precisely the same object at this point
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB:  # ..snip..

tracemem(newDT)
# [1] "<0x0000000003b7e2a0"

newDT$b[2] <- 200
# tracemem[0000000003B7E2A0 -> 00000000040ED948]: 
# tracemem[00000000040ED948 -> 00000000040ED830]: .Call copy $<-.data.table $<- 

.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),TR,ATT] (len=2, tl=100)
#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB:  # ..snip..

.Internal(inspect(newDT))
# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
#   @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
#   @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,200
# ATTRIB:  # ..snip..

এমনকি aভেক্টরকে কীভাবে অনুলিপি করা হয়েছিল তা লক্ষ্য করুন (ভিন্ন ভিন্ন হেক্স মান ভেক্টরের নতুন অনুলিপি নির্দেশ করে), যদিও aপরিবর্তন করা হয়নি। এমনকি পুরো bউপাদানটি অনুলিপি করা হয়েছিল কেবল পরিবর্তিত হওয়া উপাদানগুলির পরিবর্তনের চেয়ে। বড় ডেটার জন্য এড়ানো গুরুত্বপূর্ণ, এবং কেন :=এবং এর set()সাথে পরিচয় করিয়ে দেওয়া হয়েছিল data.table

এখন, আমাদের অনুলিপি সহ newDTআমরা এটিকে রেফারেন্সের মাধ্যমে সংশোধন করতে পারি:

newDT
#      a   b
# [1,] 1  11
# [2,] 2 200

newDT[2, b := 400]
#      a   b        # See FAQ 2.21 for why this prints newDT
# [1,] 1  11
# [2,] 2 400

.Internal(inspect(newDT))
# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
#   @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
#   @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,400
# ATTRIB:  # ..snip ..

লক্ষ্য করুন যে সমস্ত 3 টি হেক্স মান (কলাম পয়েন্টের ভেক্টর এবং 2 টি কলামের প্রতিটি) অপরিবর্তিত রয়েছে। সুতরাং এটিকে কোনও অনুলিপি সহ সত্যই সংশোধন করা হয়েছিল।

বা, আমরা DTরেফারেন্স দ্বারা মূলটি পরিবর্তন করতে পারি :

DT[2, b := 600]
#      a   b
# [1,] 1  11
# [2,] 2 600

.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,600
#   ATTRIB:  # ..snip..

এই হেক্সের মানগুলি DTউপরে বর্ণিত মূল মানগুলির সমান । example(copy)ব্যবহার করে tracememতুলনা করে আরও উদাহরণের জন্য টাইপ করুন data.frame

বিটিডব্লিউ, আপনি যদি tracemem(DT)তবে DT[2,b:=600]একটি কপি প্রতিবেদন দেখতে পাবেন। এটি printপদ্ধতিটি যে প্রথম 10 টি সারির অনুলিপি করে। যখন invisible()কোনও ফাংশন বা স্ক্রিপ্টের সাথে আবৃত হয় বা কল করা হয়, তখন printপদ্ধতিটি কল হয় না।

এই সমস্ত কার্যকারিতা ভিতরেও প্রযোজ্য; যেমন, :=এবং set()এমনকি ফাংশনগুলির মধ্যেও লেখার অনুলিপি করবেন না। আপনার যদি কোনও স্থানীয় অনুলিপি পরিবর্তন করতে x=copy(x)হয় তবে ফাংশনের শুরুতে কল করুন । তবে, মনে রাখবেন data.tableবড় ডেটা (পাশাপাশি ছোট ডেটার জন্য দ্রুত প্রোগ্রামিং সুবিধা) for আমরা ইচ্ছাকৃতভাবে বড় বস্তু (কখনও) অনুলিপি করতে চাই না। ফলস্বরূপ আমাদের থাম্বের স্বাভাবিক 3 * কার্যকরী মেমরি ফ্যাক্টর রুলের অনুমতি দেওয়ার দরকার নেই। আমরা কেবলমাত্র একটি কলামের চেয়ে বৃহত্তর ওয়ার্কিং মেমরির প্রয়োজন (যেমন 3 এর চেয়ে 1 / এনসিএল এর একটি মেমরি ফ্যাক্টর) প্রয়োজন।


1
এই আচরণটি কখন কাম্য?
কলিন

মজার বিষয় হল, পুরো অবজেক্টটি অনুলিপি করার আচরণটি কোনও ডেটা.ফ্রেম অবজেক্টের জন্য ঘটে না। অনুলিপি করা ডেটা.ফ্রেমে, কেবলমাত্র ভেক্টর যা ->অ্যাসাইনমেন্টের মাধ্যমে সরাসরি পরিবর্তিত হয়েছিল মেমরির অবস্থান পরিবর্তন করে। অপরিবর্তিত ভেক্টরগুলি মূল ডেটা.ফ্রেমের ভেক্টরগুলির মেমরির অবস্থান বজায় রাখে। আচরণকে data.tableএখানে বর্ণিত গুলি 1.12.2 হিসাবে বর্তমান আচরণ।
lmo

105

মাত্র একটি দ্রুত যোগফল।

<-সঙ্গে data.tableঠিক বেস মত; অর্থাত্ কোনও সাবসিইন না করা পর্যন্ত কোনও অনুলিপি নেওয়া হয় না <-(যেমন কলামের নাম পরিবর্তন করা বা যেমন কোনও উপাদান পরিবর্তন করা DT[i,j]<-v)। তারপরে এটি পুরো অবজেক্টের একটি অনুলিপি বেসের মতো লাগে। এটি অনুলিপি হিসাবে পরিচিত। কপি অন-সাবসাইন হিসাবে আরও পরিচিত হতে হবে, আমি মনে করি! আপনি বিশেষ :=অপারেটর বা set*সরবরাহিত ফাংশনগুলি ব্যবহার করার সময় এটি অনুলিপি করে না data.table। আপনার কাছে যদি বড় ডেটা থাকে তবে আপনি সম্ভবত এটির পরিবর্তে সেগুলি ব্যবহার করতে চান। :=এবং ফাংশনগুলির সাথেও set*অনুলিপি করবে না data.table

এই উদাহরণ ডেটা দেওয়া:

DT <- data.table(a=c(1,2), b=c(11,12))

নিম্নলিখিতটি কেবল নামের DT2সাথে আবদ্ধ একই ডেটা অবজেক্টটির সাথে কেবলমাত্র "বাঁধাই" করে DT:

DT2 <- DT

এটি কখনই অনুলিপি করে না এবং কখনও বেসেও অনুলিপি করে না। এটি কেবল ডেটা অবজেক্টটিকে চিহ্নিত করে যাতে আর জানে যে দুটি পৃথক নাম ( DT2এবং DT) একই বস্তুর দিকে নির্দেশ করে। আর তাই আর এর পরে যদি সাবসাইন করা হয় তবে অবজেক্টটি অনুলিপি করতে হবে ।

যে জন্য উপযুক্ত data.table, অত্যধিক। এটা করার :=জন্য নয়। সুতরাং নিম্নলিখিতটি একটি ইচ্ছাকৃত ত্রুটি হিসাবে :=কেবলমাত্র বস্তুর নাম বাঁধাই নয়:

DT2 := DT    # not what := is for, not defined, gives a nice error

:=রেফারেন্স দ্বারা subassigning জন্য হয়। তবে আপনি এটি বেসের মতো ব্যবহার করেন না:

DT[3,"foo"] := newvalue    # not like this

আপনি এটি এর মতো ব্যবহার করুন:

DT[3,foo:=newvalue]    # like this

DTরেফারেন্স দ্বারা এটি পরিবর্তন হয়েছে। বলুন যে আপনি newডেটা অবজেক্টের উল্লেখ করে একটি নতুন কলাম যুক্ত করেছেন, এটি করার দরকার নেই:

DT <- DT[,new:=1L]

কারণ আরএইচএস ইতিমধ্যে DTরেফারেন্স দ্বারা পরিবর্তিত হয়েছে। অতিরিক্তটি DT <-কী :=করে তা ভুল বোঝা tand আপনি সেখানে এটি লিখতে পারেন, তবে এটি অতিরিক্ত অতিরিক্ত।

DT:=ফাংশন ছাড়াও রেফারেন্স দ্বারা পরিবর্তিত হয় :

f <- function(X){
    X[,new2:=2L]
    return("something else")
}
f(DT)   # will change DT

DT2 <- DT
f(DT)   # will change both DT and DT2 (they're the same data object)

data.tableবড় ডেটাসেটের জন্য, মনে রাখবেন। আপনার যদি 20 গিগাবাইট data.tableমেমরি থাকে তবে আপনার এটি করার একটি উপায় প্রয়োজন। এটি একটি খুব ইচ্ছাকৃত ডিজাইনের সিদ্ধান্ত data.table

কপিগুলি অবশ্যই তৈরি করা যায়। আপনাকে কেবল ডেটা বলার দরকার আছে able টেবিলটি নিশ্চিত যে copy()ফাংশনটি ব্যবহার করে আপনি নিজের 20 জিবি ডেটাসেটটি অনুলিপি করতে চান :

DT3 <- copy(DT)   # rather than DT3 <- DT
DT3[,new3:=3L]     # now, this just changes DT3 because it's a copy, not DT too.

অনুলিপিগুলি এড়ানোর জন্য, বেস টাইপ অ্যাসাইনমেন্ট বা আপডেট ব্যবহার করবেন না:

DT$new4 <- 1L                 # will make a copy so use :=
attr(DT,"sorted") <- "a"      # will make a copy use setattr() 

আপনি যদি নিশ্চিত হতে চান যে আপনি রেফারেন্স ব্যবহারের মাধ্যমে আপডেট করছেন .Internal(inspect(x))এবং সংস্থাগুলির মেমরি ঠিকানা মানগুলি দেখুন (ম্যাথিউ ডাওলের উত্তর দেখুন)।

লিখন :=মধ্যে jআপনি রেফারেন্স দ্বারা subassign পারবেন মত একদল । আপনি গ্রুপ অনুসারে একটি নতুন কলাম যুক্ত করতে পারেন। সুতরাং সেই কারণেই :=ভিতরে করা হয়েছে [...]:

DT[, newcol:=mean(x), by=group]
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.