जब वर्ण नाम वेक्टर वर्ण में संग्रहीत हों, तो data.table का चयन करें / असाइन करें


91

यदि आप data.tableवर्ण नाम वेक्टर में संग्रहीत हैं, तो आप चर का संदर्भ कैसे लेते हैं ? उदाहरण के लिए, यह एक के लिए काम करता है data.frame:

df <- data.frame(col1 = 1:3)
colname <- "col1"
df[colname] <- 4:6
df
#   col1
# 1    4
# 2    5
# 3    6

मैं डेटा के लिए एक ही ऑपरेशन कैसे कर सकता हूं :=। काम करने की स्पष्ट बात dt[ , list(colname)]नहीं है (न ही मुझे इसकी उम्मीद थी)।

जवाबों:


132

प्रोग्राम को वेरिएबल सेलेक्ट करने के दो तरीके :

  1. with = FALSE:

    DT = data.table(col1 = 1:3)
    colname = "col1"
    DT[, colname, with = FALSE] 
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    
  2. 'डॉट डॉट' ( ..) उपसर्ग:

    DT[, ..colname]    
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    

'डॉट डॉट' ( ..) अंकन के आगे के विवरण के लिए , 1.10.2 में नई विशेषताएँ देखें (यह वर्तमान में सहायता पाठ में वर्णित नहीं है)।

चर (ओं) को असाइन करने के लिए, :=कोष्ठक में LHS लपेटें :

DT[, (colname) := 4:6]    
#    col1
# 1:    4
# 2:    5
# 3:    6

उत्तरार्द्ध को कॉलम प्लैंक के रूप में जाना जाता है , क्योंकि आप संदर्भ द्वारा पूरे कॉलम वेक्टर को बदलते हैं। यदि एक उपसमुच्चय iमौजूद था, तो यह संदर्भ द्वारा उप-समूह होगा। चारों ओर (colname)परेंस CRAN 2014 के संस्करण v1.9.4 में पेश किया गया एक शॉर्टहैंड है। यहाँ समाचार आइटम है :

के with = FALSEसाथ उपयोग करना :=अब सभी मामलों में पदावनत कर दिया गया है, यह देखते हुए कि :=कोष्ठक के साथ एलएचएस को लपेटना कुछ समय के लिए पसंद किया गया है।

colVar = "col1"
DT[, colVar := 1, with = FALSE]                 # deprecated, still works silently
DT[, (colVar) := 1]                             # please change to this
DT[, c("col1", "col2") := 1]                    # no change
DT[, 2:4 := 1]                                  # no change
DT[, c("col1","col2") := list(sum(a), mean(b)]  # no change
DT[, `:=`(...), by = ...]                       # no change

विवरण अनुभाग भी देखें ?`:=`:

DT[i, (colnamevector) := value]
# [...] The parens are enough to stop the LHS being a symbol

और टिप्पणी में आगे के सवाल का जवाब देने के लिए, यहां एक तरीका है (हमेशा की तरह कई तरीके हैं):

DT[, colname := cumsum(get(colname)), with = FALSE]
#    col1
# 1:    4
# 2:    9
# 3:   15 

या, आप इसे आसान को पढ़ने के लिए मिल सकती है, लिखने और बस को डिबग evalएक paste, एक गतिशील एसक्यूएल बयान का निर्माण एक सर्वर को भेजने के लिए करने के लिए इसी तरह की:

expr = paste0("DT[,",colname,":=cumsum(",colname,")]")
expr
# [1] "DT[,col1:=cumsum(col1)]"

eval(parse(text=expr))
#    col1
# 1:    4
# 2:   13
# 3:   28

यदि आप ऐसा करते हैं, तो आप एक सहायक कार्य को परिभाषित कर सकते हैं EVAL:

EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2))

EVAL("DT[,",colname,":=cumsum(",colname,")]")
#    col1
# 1:    4
# 2:   17
# 3:   45

अब जब data.table1.8.2 स्वचालित रूप jसे दक्षता के लिए अनुकूलन करता है, तो evalविधि का उपयोग करना बेहतर हो सकता है । get()में j, कुछ अनुकूलन से बचाता है, उदाहरण के लिए।

या, वहाँ है set()। एक कम ओवरहेड, कार्यात्मक रूप :=, जो यहां ठीक होगा। देखते हैं ?set

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66

1
उत्तर मैथ्यू के लिए धन्यवाद। के साथ = FALSE निश्चित रूप से मेरी समस्या का हिस्सा है। हकीकत में, मैं कॉलम को कम्सम के कॉलम से बदलना चाहता हूं। क्या मैं किसी तरह असाइनमेंट के दाईं ओर चर द्वारा कॉलम नाम का संदर्भ दे सकता हूं?
फ्रैंक

अकस्मात, मैंने सिर्फ एक अलग नाम के साथ बाहरी रूप से कमसुम को जकड़ लिया जो डीटी के अंदर मौजूद नहीं है और जो ठीक काम करता है।
फ्रैंक

1
लेकिन यह पूरी अतिरिक्त रेखा होगी! बहुत सुरुचिपूर्ण नहीं है :) लेकिन ठीक है कभी-कभी यह उपयोगी होता है। उन मामलों में, जिनके साथ चर नाम शुरू करना .या ..किसी भी संभावित मास्किंग से बचने के लिए सबसे अच्छा है अगर DTकभी भी उस प्रतीक को भविष्य में एक स्तंभ नाम के रूप में शामिल किया गया है (और उस सम्मेलन से चिपके रहें जो स्तंभ नाम से शुरू नहीं होता है .)। इस तरह के मुद्दों को जोड़ने के लिए इसे और अधिक मजबूत बनाने के लिए कुछ फीचर अनुरोध हैं, जैसे कि जोड़ना .()और ..()
मैट डोले

मैंने उत्तर दिया इससे पहले कि मैंने देखा कि आपने अपना उत्तर संपादित किया। मेरा पहला विचार eval (parse ()) था, लेकिन किसी कारण से मुझे इसे काम करने में परेशानी हो रही थी, जब इसने मुझे बाहरी रूप से ऐसा करने के लिए मनाया। यह उन चीजों के साथ बहुत अच्छा जवाब है जिनके बारे में मैंने नहीं सोचा था। सामान्य रूप से data.table के लिए धन्यवाद, यह एक शानदार पैकेज है।
फ्रैंक

2
ध्यान दें कि आप fn$EVAL समाधान की पठनीयता में सुधार करने के लिए gsubfn पैकेज से अर्ध-पर्ल प्रकार स्ट्रिंग प्रक्षेप का उपयोग कर सकते हैं library(gsubfn); fn$EVAL( "DT[,$colname:=cumsum($colname)]" ):।
जी। ग्रोथेंडिक

8

* यह वास्तव में एक जवाब नहीं है, लेकिन मेरे पास टिप्पणियों को पोस्ट करने के लिए पर्याप्त सड़क क्रेडिट नहीं है: /

वैसे भी, जो कोई भी वास्तव में एक चर में संग्रहीत नाम के साथ डेटा तालिका में एक नया स्तंभ बनाना चाह रहा है, उसके लिए मुझे निम्नलिखित काम करना होगा। मेरे पास प्रदर्शन का कोई सुराग नहीं है। सुधार के लिए कोई सुझाव? क्या यह मान लेना सुरक्षित है कि एक नए कॉलम को हमेशा V1 नाम दिया जाएगा?

colname <- as.name("users")
# Google Analytics query is run with chosen metric and resulting data is assigned to DT
DT2 <- DT[, sum(eval(colname, .SD)), by = country]
setnames(DT2, "V1", as.character(colname))

सूचना मैं इसे सम राशि में ठीक से संदर्भित कर सकता हूं (), लेकिन इसे उसी चरण में निर्दिष्ट करने के लिए प्राप्त नहीं कर सकता। BTW, कारण मुझे यह करने की आवश्यकता है कॉलनाम एक चमकदार ऐप में उपयोगकर्ता इनपुट पर आधारित होगा।


सिर्फ काम करने के लिए +1: मैं मानता हूं कि ऐसा करने के लिए "तरीका" नहीं होना चाहिए, लेकिन इस विषय पर हर एसओ पोस्ट पर सिर्फ 45 मिनट का समय बिताना, यह एकमात्र समाधान है जिसे मैं वास्तव में प्राप्त करने में सक्षम हूं काम - इसे इंगित करने के लिए समय निकालने के लिए धन्यवाद!
न्यूरोप्सिक

मैं खुशी से मदद कर सकता है! दुर्भाग्य से, मुझे डेटाटेबल्स का उपयोग करके सीधे अधिक सुरुचिपूर्ण समाधान कभी नहीं मिला, हालांकि यह 3 लाइनर भयानक नहीं है। मेरे परिदृश्य में, मैंने महसूस किया कि एक सरल विकल्प का उपयोग करना होगा कि मैं अपने डेटा को "विस्तृत" के बजाय "लंबा" बनाने के लिए केवल tidyr का उपयोग कर सकता हूं, क्योंकि उपयोगकर्ता इनपुट के आधार पर, मैं हमेशा सेट से चयन करने के बजाय एकल कॉलम पर फ़िल्टर कर सकता हूं स्तंभों की।
efh0888

2
यह मान V1लेना सुरक्षित नहीं है कि नया नाम है। उदाहरण के लिए, यदि आप सीएसवी के साथ पढ़ते हैं freadऔर एक अनाम कॉलम है, तो उसका V1नाम read.csvहोगा (और देगा X)। तो यह संभव है कि आपकी तालिका पहले से ही एक है V1। हो सकता है कि बस नाम मिल जाएnames(DT)[length(names(DT))]
dracodoc

2

कई स्तंभों के लिए और एक फ़ंक्शन स्तंभ मानों पर लागू होता है।

किसी फ़ंक्शन से मानों को अपडेट करते समय, आरएचएस को एक सूची ऑब्जेक्ट होना चाहिए, इसलिए एक लूप का उपयोग करने के .SDसाथ lapplyचाल करना होगा।

नीचे दिया गया उदाहरण पूर्णांक स्तंभों को संख्यात्मक स्तंभों में परिवर्तित करता है

a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5])
sapply(a1, class)  # show classes of columns
#         a           b          c1 
# "integer"   "integer" "character" 

# column name character vector
nm <- c("a", "b")

# Convert columns a and b to numeric type
a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ]

sapply(a1, class)
#         a           b          c1 
# "numeric"   "numeric" "character" 

2

चर या फ़ंक्शन के माध्यम से data.table से कई कॉलम पुनः प्राप्त करें:

library(data.table)

x <- data.table(this=1:2,that=1:2,whatever=1:2)

# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]

# === indirect via  variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)

x[, ..mycols]
x[, .SD, .SDcols=mycols]

# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]

जो सभी उपज

   that whatever
1:    1        1
2:    2        2

मुझे .SDcolsरास्ता सबसे सुरुचिपूर्ण लगता है।


1

आप यह कोशिश कर सकते हैं

colname <- as.name ("COL_NAME")

DT2 <- DT [, सूची (COL_SUM = sum (eval (colname, .SD))), = c (समूह)] द्वारा


1
हमेशा केवल कोड पोस्ट करने के बजाय अपने कोड के साथ स्पष्टीकरण जोड़ने की सिफारिश की जाती है।
MBorg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.