diff --git a/vignettes/hi/datatable-benchmarking.Rmd b/vignettes/hi/datatable-benchmarking.Rmd
new file mode 100644
index 0000000000..dd7c81a110
--- /dev/null
+++ b/vignettes/hi/datatable-benchmarking.Rmd
@@ -0,0 +1,206 @@
+---
+title: "data.table की बेंचमार्किंग"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format:
+ options:
+ toc: true
+ number_sections: true
+vignette: >
+ %\VignetteIndexEntry{Benchmarking data.table}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+```
+
+
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+इस दस्तावेज़ का उद्देश्य `data.table` के प्रदर्शन को मापने के लिए मार्गदर्शन देना है। इसमें बेंचमार्किंग के सर्वोत्तम तरीकों और उन सामान्य गलतियों की जानकारी एक ही स्थान पर दी गई है, जिनसे बचना चाहिए।
+
+# fread: कैश साफ़ करना
+
+आदर्श रूप से, प्रत्येक `fread` कॉल को एक नए सेशन में चलाना चाहिए। R को चलाने से पहले नीचे दिए गए कमांड निष्पादित करने चाहिए। इससे RAM में मौजूद ऑपरेटिंग सिस्टम का फ़ाइल कैश और हार्ड डिस्क का कैश साफ़ करने में मदद मिलती है।
+
+```sh
+free -g
+sudo sh -c 'echo 3 >/proc/sys/vm/drop_caches'
+sudo lshw -class disk
+sudo hdparm -t /dev/sda
+```
+
+जब `fread` के प्रदर्शन की तुलना R के बाहर उपलब्ध अन्य समाधानों से की जाती है, तो यह ध्यान रखना ज़रूरी है कि R में कैरेक्टर कॉलम के मानों को _R के ग्लोबल स्ट्रिंग कैश_ में जोड़ना पड़ता है।
+
+डेटा पढ़ते समय इस प्रक्रिया में अतिरिक्त समय लगता है। हालाँकि, इसके बाद किए जाने वाले ऑपरेशन को इसका लाभ मिलता है, क्योंकि कैरेक्टर स्ट्रिंग पहले से कैश में उपलब्ध होती हैं।
+
+इसलिए, केवल किसी एक कार्य (जैसे `fread`) में लगने वाले समय को मापना पर्याप्त नहीं होता। बेहतर होगा कि संपूर्ण प्रक्रिया की बेंचमार्किंग भी की जाए, जिसमें डेटा पढ़ना, उसमें परिवर्तन करना और अंतिम आउटपुट तैयार करना शामिल हो।
+
+# subset: संयुक्त क्वेरी में इंडेक्स ऑप्टिमाइज़ेशन की सीमा
+
+संयुक्त फ़िल्टर क्वेरी के लिए इंडेक्स ऑप्टिमाइज़ेशन का उपयोग उस स्थिति में नहीं किया जाएगा, जब फ़िल्टर में दिए गए तत्वों का कार्तीय गुणनफल (cross product) 1e4 यानी 10,000 तत्वों से अधिक हो जाए।
+
+```r
+DT = data.table(V1=1:10, V2=1:10, V3=1:10, V4=1:10)
+setindex(DT)
+v = c(1L, rep(11L, 9))
+length(v)^4 # cross product of elements in filter
+#[1] 10000 # <= 10000
+DT[V1 %in% v & V2 %in% v & V3 %in% v & V4 %in% v, verbose=TRUE]
+#Optimized subsetting with index 'V1__V2__V3__V4'
+#on= matches existing index, using index
+#Starting bmerge ...done in 0.000sec
+#...
+v = c(1L, rep(11L, 10))
+length(v)^4 # cross product of elements in filter
+#[1] 14641 # > 10000
+DT[V1 %in% v & V2 %in% v & V3 %in% v & V4 %in% v, verbose=TRUE]
+#Subsetting optimization disabled because the cross-product of RHS values exceeds 1e4, causing memory problems.
+#...
+```
+
+# subset: इंडेक्स को ध्यान में रखकर बेंचमार्किंग करना
+
+सुविधा के लिए, `data.table` उन कॉलम पर अपने आप इंडेक्स बना देता है, जिनका उपयोग डेटा को फ़िल्टर करने के लिए किया जाता है।
+
+किसी विशेष कॉलम पर पहली बार सबसेट बनाने में इंडेक्स तैयार करने के कारण थोड़ा अतिरिक्त समय लग सकता है। लेकिन बाद में उसी कॉलम पर क्वेरी चलाने में लगने वाला समय काफी कम हो जाता है।
+
+प्रदर्शन को सही तरीके से मापने के लिए इंडेक्स बनाने में लगे समय और पहले से मौजूद इंडेक्स का उपयोग करके क्वेरी चलाने में लगे समय को अलग-अलग मापना चाहिए।
+
+इस तरह प्राप्त परिणामों से यह तय करना आसान हो जाता है कि आपके उपयोग के लिए कौन-सी रणनीति सबसे उपयुक्त होगी।
+
+इंडेक्स के उपयोग को नियंत्रित करने के लिए नीचे दिए गए विकल्पों का उपयोग किया जा सकता है:
+
+```r
+options(datatable.auto.index=TRUE)
+options(datatable.use.index=TRUE)
+```
+
+- `use.index=FALSE` सेट करने पर क्वेरी उपलब्ध इंडेक्स का उपयोग नहीं करेगी, भले ही इंडेक्स पहले से मौजूद हों। हालाँकि, ऑप्टिमाइज़ेशन के लिए पहले से मौजूद keys का उपयोग फिर भी किया जा सकता है।
+- `auto.index=FALSE` सेट करने पर बिना इंडेक्स वाले डेटा पर सबसेट बनाते समय अपने आप नया इंडेक्स नहीं बनाया जाएगा। लेकिन यदि इंडेक्स इस विकल्प को सेट करने से पहले बनाए गए थे, या `setindex` का उपयोग करके स्पष्ट रूप से बनाए गए थे, तो उनका उपयोग ऑप्टिमाइज़ेशन के लिए फिर भी किया जाएगा।
+
+दो अन्य विकल्प भी उपलब्ध हैं, जो इंडेक्स के उपयोग सहित संपूर्ण ऑप्टिमाइज़ेशन प्रक्रिया को नियंत्रित करते हैं:
+
+```r
+options(datatable.optimize=2L)
+options(datatable.optimize=3L)
+```
+
+`options(datatable.optimize=2L)` का उपयोग करने पर सबसेट का ऑप्टिमाइज़ेशन पूरी तरह बंद हो जाएगा, जबकि `options(datatable.optimize=3L)` इसे दोबारा चालू कर देगा।
+
+ये विकल्प केवल इंडेक्स ही नहीं, बल्कि कई अन्य प्रकार के ऑप्टिमाइज़ेशन को भी प्रभावित करते हैं। इसलिए, यदि आपका उद्देश्य केवल इंडेक्स के उपयोग को नियंत्रित करना है, तो इन विकल्पों का उपयोग नहीं करना चाहिए।
+
+अधिक जानकारी के लिए `?datatable.optimize` देखें।
+
+# _by reference_ ऑपरेशन
+
+`set*` फ़ंक्शन की बेंचमार्किंग करते समय केवल पहली बार चलने वाले ऑपरेशन का समय मापना उचित होता है।
+
+ये फ़ंक्शन अपने इनपुट डेटा को _by reference_ अपडेट करते हैं। इसलिए, जब इन्हें दोबारा चलाया जाता है, तो ये पहले से प्रोसेस किए गए `data.table` पर काम करते हैं। इससे बेंचमार्क के परिणाम प्रभावित हो सकते हैं और प्रदर्शन वास्तविकता से बेहतर दिखाई दे सकता है।
+
+अपने `data.table` को _by reference_ ऑपरेशन के कारण होने वाले बदलावों से सुरक्षित रखने के लिए `copy` या `data.table:::shallow` फ़ंक्शन का उपयोग किया जा सकता है।
+
+हालाँकि, ध्यान रखना चाहिए कि `copy` का उपयोग महँगा साबित हो सकता है, क्योंकि इसके लिए पूरे ऑब्जेक्ट की प्रतिलिपि बनानी पड़ती है।
+
+आमतौर पर हम जिस वास्तविक ऑपरेशन की बेंचमार्किंग कर रहे हैं, उसके समय में डेटा की प्रतिलिपि बनाने में लगने वाला समय शामिल नहीं करना चाहते।
+
+# छोटे और स्वतंत्र ऑपरेशन की बेंचमार्किंग करने का प्रयास करें
+
+यदि आप अपने बेंचमार्क के परिणाम प्रकाशित करना चाहते हैं, तो पूरी प्रक्रिया को छोटे और स्वतंत्र ऑपरेशन में बाँटकर प्रत्येक का समय अलग-अलग मापना अधिक उपयोगी होगा।
+
+इससे पाठक समझ सकेंगे कि डेटा को स्रोत से पढ़ने, साफ़ करने, वास्तविक ट्रांसफ़ॉर्मेशन करने और परिणामों को एक्सपोर्ट करने में कितना समय लगा।
+
+बेशक, यदि आपके बेंचमार्क का उद्देश्य संपूर्ण _end-to-end workflow_ का प्रदर्शन दिखाना है, तो कुल समय प्रस्तुत करना पूरी तरह उचित है।
+
+फिर भी, प्रत्येक चरण का समय अलग-अलग मापने से यह समझने में मदद मिलती है कि पूरी प्रक्रिया में सबसे अधिक समय कहाँ लग रहा है और कौन-से चरण मुख्य बाधा बन रहे हैं।
+
+कुछ परिस्थितियों में प्रत्येक छोटे ऑपरेशन की अलग बेंचमार्किंग करना उपयोगी नहीं भी हो सकता।
+
+उदाहरण के लिए, पहले _CSV फ़ाइल पढ़ना_ और उसके बाद डेटा की _ग्रुपिंग करना_।
+
+R में कैरेक्टर डेटा इम्पोर्ट करते समय _R के ग्लोबल स्ट्रिंग कैश_ को भरना पड़ता है। इससे डेटा इम्पोर्ट करने की प्रक्रिया में अतिरिक्त समय लगता है।
+
+दूसरी ओर, यही _ग्लोबल स्ट्रिंग कैश_ बाद में _ग्रुपिंग_ जैसे ऑपरेशन को तेज़ कर सकता है।
+
+ऐसी परिस्थितियों में, जब R के प्रदर्शन की तुलना अन्य प्रोग्रामिंग भाषाओं से की जा रही हो, तो पूरी प्रक्रिया में लगने वाले कुल समय को भी शामिल करना उपयोगी हो सकता है।
+
+# डेटा की क्लास बदलने से बचें
+
+जब तक आपका वास्तविक उद्देश्य डेटा की क्लास बदलने में लगने वाले समय को मापना न हो, तब तक बेंचमार्किंग में शामिल प्रत्येक टूल के लिए इनपुट ऑब्जेक्ट पहले से उसकी अपेक्षित क्लास में तैयार होने चाहिए।
+
+इससे क्लास कन्वर्ज़न में लगने वाला अतिरिक्त समय बेंचमार्क के परिणामों को प्रभावित नहीं करेगा।
+
+# `microbenchmark(..., times=100)` का उपयोग करने से बचें
+
+किसी बेंचमार्क को बहुत अधिक बार दोहराने से आमतौर पर डेटा प्रोसेसिंग टूल के प्रदर्शन की सबसे स्पष्ट तस्वीर नहीं मिलती।
+
+छोटी और स्वतंत्र गणनाओं के लिए ऐसा करना उचित हो सकता है। लेकिन यह उन वास्तविक परिस्थितियों का सही प्रतिनिधित्व नहीं करता, जिनमें डेटा प्रोसेसिंग टूल का सामान्यतः उपयोग किया जाता है।
+
+असल में, डेटा प्रोसेसिंग के दौरान कई ट्रांसफ़ॉर्मेशन एक क्रम में किए जाते हैं और प्रत्येक ट्रांसफ़ॉर्मेशन अक्सर केवल एक बार चलाया जाता है।
+
+Matt ने एक बार कहा था:
+
+> मुझे उन बेंचमार्क पर भरोसा करने में काफी संकोच होता है, जिनमें मापा गया समय 1 सेकंड से कम होता है। मैं किसी एक रन के लिए 10 सेकंड या उससे अधिक समय को प्राथमिकता देता हूँ, जिसे डेटा का आकार बढ़ाकर प्राप्त किया जा सकता है। किसी बेंचमार्क को 500 बार दोहराना मेरे लिए चिंता का संकेत है। बड़े डेटा पर 3 से 5 रन ही पर्याप्त होने चाहिए। इतने छोटे समय के पैमाने पर फ़ंक्शन कॉल का अतिरिक्त समय और गार्बेज कलेक्शन (GC) में लगने वाला समय निष्कर्षों को प्रभावित कर सकता है।
+
+यह बात काफी हद तक सही है।
+
+समय की माप जितनी छोटी होगी, उसके मुकाबले मापन में होने वाले अनावश्यक उतार-चढ़ाव (noise) का प्रभाव उतना ही अधिक होगा।
+
+यह noise कई कारणों से उत्पन्न हो सकता है, जैसे method dispatch, पैकेज या क्लास की initialization प्रक्रिया और अन्य अतिरिक्त ऑपरेशन।
+
+इसलिए, बेंचमार्किंग का मुख्य ध्यान वास्तविक उपयोग की परिस्थितियों पर होना चाहिए।
+
+# मल्टीथ्रेडेड प्रोसेसिंग
+
+बेंचमार्किंग के समय को प्रभावित करने वाले प्रमुख कारकों में से एक यह है कि आपके R सेशन के लिए कितने थ्रेड उपलब्ध हैं।
+
+`data.table` के हाल के संस्करणों में कुछ फ़ंक्शन को समानांतर रूप से चलाने की सुविधा दी गई है।
+
+आप `setDTthreads` का उपयोग करके नियंत्रित कर सकते हैं कि कितने थ्रेड इस्तेमाल किए जाएँ।
+
+```r
+setDTthreads(0) # use all available cores (default)
+getDTthreads() # check how many cores are currently used
+```
+
+# लूप के अंदर `:=` के बजाय `set` को प्राथमिकता दें
+
+जब तक आप _sub-assign by reference_ करते समय इंडेक्स का उपयोग नहीं कर रहे हैं, तब तक `set` फ़ंक्शन का उपयोग करना बेहतर होता है।
+
+इसका कारण यह है कि `set` फ़ंक्शन में `[.data.table` मेथड कॉल का अतिरिक्त ओवरहेड नहीं होता।
+
+```r
+DT = data.table(a=3:1, b=letters[1:3])
+setindex(DT, a)
+
+# for (...) { # imagine loop here
+
+ DT[a==2L, b := "z"] # sub-assign by reference, uses index
+ DT[, d := "z"] # not sub-assign by reference, not uses index and adds overhead of `[.data.table`
+ set(DT, j="d", value="z") # no `[.data.table` overhead, but no index yet, till #1196
+
+# }
+```
+
+# लूप के अंदर `data.table()` के बजाय `setDT` को प्राथमिकता दें
+
+वर्तमान में `data.table()` का उपयोग करने पर कुछ अतिरिक्त ओवरहेड होता है।
+
+इसलिए, लूप के अंदर किसी मान्य लिस्ट (valid list) पर `as.data.table()` या `setDT()` का उपयोग करना अधिक उपयुक्त माना जाता है।
\ No newline at end of file
diff --git a/vignettes/hi/datatable-faq.Rmd b/vignettes/hi/datatable-faq.Rmd
new file mode 100644
index 0000000000..76a2b0df21
--- /dev/null
+++ b/vignettes/hi/datatable-faq.Rmd
@@ -0,0 +1,662 @@
+---
+title: "data.table के बारे में अक्सर पूछे जाने वाले प्रश्न"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format:
+ options:
+ toc: true
+ number_sections: true
+vignette: >
+ %\VignetteIndexEntry{Frequently Asked Questions about data.table}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+पहला भाग, शुरुआती उपयोगकर्ताओं के FAQs, शुरू से अंत तक क्रम में पढ़ने के लिए बनाया गया है। इसे केवल FAQ शैली में इसलिए लिखा गया है ताकि इसे समझना और पढ़ना आसान हो। ये वास्तव में सबसे अधिक बार पूछे जाने वाले प्रश्नों की सूची नहीं है। उसके लिए Stack Overflow पर पूछे गए प्रश्नों को देखना अधिक अच्छा संकेतक है।
+
+यह FAQ आवश्यक पठन है और इसे मुख्य दस्तावेज़ीकरण का हिस्सा माना जाता है। कृपया इसे पढ़ने से पहले Stack Overflow पर प्रश्न न पूछें और GitHub पर issue न खोलें। जब कोई व्यक्ति इसे पढ़े बिना प्रश्न पूछता है, तो आमतौर पर यह स्पष्ट हो जाता है। इसलिए यदि आप इसे पढ़े बिना प्रश्न पूछते हैं, तो बेहतर होगा कि अपना असली नाम इस्तेमाल न करें।
+
+यह दस्तावेज़ Nov 2016 में जारी v1.9.8 में हुए बदलावों को ध्यान में रखते हुए जल्दी से संशोधित किया गया था। गलतियों को सुधारने या दस्तावेज़ को बेहतर बनाने के लिए कृपया pull requests भेजें। यदि किसी को पता हो कि CRAN पर दिखाए जाने पर table of contents इतना संकरा और दबा हुआ क्यों दिखाई देता है, तो कृपया हमें बताएं। यह दस्तावेज़ पहले PDF था और हाल ही में इसे HTML में बदला गया है।
+
+
+# शुरुआती उपयोगकर्ताओं के FAQs
+
+## `DT[ , 5]` और `DT[2, 5]`, `data.frame` की तरह vectors लौटाने के बजाय 1-column data.table क्यों लौटाते हैं? {#j-num}
+
+यह व्यवहार consistency बनाए रखने के लिए है, ताकि जब आप data.table को ऐसे functions में उपयोग करें जो अलग-अलग प्रकार के inputs स्वीकार करते हैं, तो आप भरोसा कर सकें कि `DT[...]` एक data.table ही लौटाएगा। आपको data.frame की तरह `drop=FALSE` जोड़ना याद रखने की आवश्यकता नहीं होती। data.table पहली बार 2006 में जारी हुआ था और data.frame से यह अंतर शुरुआत से ही इसकी एक विशेषता रहा है।
+
+आपने शायद सुना होगा कि columns को उनके नाम के बजाय number से refer करना सामान्यतः अच्छी practice नहीं मानी जाती। यदि आपका कोई सहकर्मी बाद में आपका code पढ़ता है, तो उसे यह ढूँढना पड़ सकता है कि column number 5 वास्तव में कौन-सा column है। यदि आप या वह आपके R program में पहले कहीं columns का क्रम बदल देते हैं, और आप उन सभी जगहों को बदलना भूल जाते हैं जहाँ column number 5 को refer किया गया है, तो बिना किसी warning या error के गलत परिणाम मिल सकते हैं। यह R या data.table की गलती नहीं होगी; यह आपकी गलती होगी। यह वास्तव में बहुत खराब practice है। कृपया ऐसा न करें। यही सिद्धांत professional SQL developers भी अपनाते हैं: कभी `select *` न करें; भविष्य के बदलावों के प्रति code को कम से कम कुछ हद तक robust रखने के लिए columns को स्पष्ट रूप से नाम से select करें।
+
+मान लें कि column 5 का नाम `"region"` है और आपको वास्तव में उस column को data.table के बजाय vector के रूप में निकालना है। तब column name का उपयोग करके `DT$region` या `DT[["region"]]` लिखना अधिक robust है; यानी base R की तरह। data.table पर base R के `$` और `[[` का उपयोग प्रोत्साहित किया जाता है। हालांकि assignment के लिए `<-` के साथ इनका उपयोग न करें (उसके लिए `:=` का उपयोग करें), लेकिन नाम से किसी एक column को select करने के लिए इनका उपयोग उचित है। एक महत्वपूर्ण अंतर यह है कि DT$col और DT[['col']] reference लौटा सकते हैं, जबकि DT[, col] हमेशा copy लौटाता है। इसके महत्वपूर्ण परिणाम हो सकते हैं और इन्हें `vignette("datatable-reference-semantics", package="data.table")` में समझाया गया है।
+
+कुछ स्थितियों में column को number से refer करना ही एकमात्र तरीका जैसा लग सकता है, जैसे columns की sequence के मामले में। ऐसी स्थितियों में data.frame की तरह आप `DT[, 5:10]` और `DT[,c(1,4,10)]` लिख सकते हैं। फिर भी, भविष्य में आपके data में columns की संख्या या क्रम बदल सकता है, इसलिए named range जैसे `DT[,columnRed:columnViolet]` का उपयोग करना या प्रत्येक column का नाम स्पष्ट रूप से देना, जैसे `DT[,c("columnRed","columnOrange","columnYellow")]`, अधिक robust है। शुरुआत में इसमें थोड़ा अधिक काम लगता है, लेकिन भविष्य में आप स्वयं इसके लिए आभारी हो सकते हैं और आपके सहकर्मी भी। कम से कम यदि कुछ गलत हो जाए, तो आप कह सकेंगे कि आपने robust code लिखने की पूरी कोशिश की थी।
+
+हालाँकि, हम वास्तव में चाहते हैं कि आप `DT[,.(columnRed,columnOrange,columnYellow)]` का उपयोग करें; यानी `DT[...]` के अंदर column names को सीधे variables की तरह उपयोग करें। data.frame की तरह हर column के आगे `DT$` लगाने की आवश्यकता नहीं है। `.()` केवल `list()` का alias है और यदि आप चाहें तो इसके बजाय `list()` का उपयोग कर सकते हैं। आप वहीं पर column names से बना कोई भी R expression रख सकते हैं, किसी भी R package का उपयोग कर सकते हैं, और अलग-अलग lengths तथा types के परिणाम लौटा सकते हैं। पहले हम आपको यह तरीका अपनाने के लिए इतना प्रोत्साहित करना चाहते थे कि हमने जानबूझकर `DT[,5]` को काम ही नहीं करने दिया। Nov 2016 में जारी v1.9.8 से पहले `DT[,5]` केवल `5` लौटाता था। विचार यह था कि हम एक सरल नियम सिखा सकते हैं: `DT[...]` के अंदर के सभी हिस्से हमेशा DT के frame के भीतर evaluate होते हैं, जहाँ column names variables की तरह दिखाई देते हैं। और `5` का evaluation `5` ही होता है, इसलिए यह व्यवहार उस एक नियम के अनुरूप था। यदि आप वास्तव में column को name या number से select करना चाहते थे, तो आपको जानबूझकर एक अतिरिक्त कदम `DT[,5,with=FALSE]` लेना पड़ता था। Nov 2016 के बाद से आपको `with=FALSE` का उपयोग करने की आवश्यकता नहीं है, और समय के साथ यह देखा जाएगा कि इस मामले में data.frame के साथ अधिक consistency नए और पुराने दोनों उपयोगकर्ताओं के लिए सहायक होती है या बाधा। जो नए उपयोगकर्ता इस FAQ को, यहाँ तक कि इस पहले entry को भी नहीं पढ़ते, वे उम्मीद है data.table का उपयोग करते समय उतनी जल्दी नहीं अटकेंगे जितना पहले तब अटकते थे जब वे इसे data.frame की तरह काम करने की उम्मीद करते थे। उम्मीद है वे `DT[i, j, by]` के अंदर column expressions रखने के हमारे उद्देश्य और recommendation को समझने से वंचित नहीं रहेंगे। यदि वे data.table को data.frame की तरह उपयोग करेंगे, तो उन्हें इसके लाभ नहीं मिलेंगे। यदि आप किसी ऐसे व्यक्ति को जानते हैं, तो उसे मित्रतापूर्वक यह दस्तावेज़ पढ़ने के लिए कहें, जैसे आप पढ़ रहे हैं।
+
+याद रखें: आप `DT[...]` के अंदर column names को variables की तरह उपयोग करके _कोई भी_ R expression रख सकते हैं; उदाहरण के लिए `DT[, colA*colB/2]` आज़माएँ। यह vector लौटाता है क्योंकि आपने column names को variables की तरह उपयोग किया। data.table लौटाने के लिए इसे `.()` में wrap करें; यानी `DT[,.(colA*colB/2)]`। इसे नाम दें: `DT[,.(myResult = colA*colB/2)]`। इस query से दो चीज़ें कैसे लौटानी हैं, यह हम आपके अनुमान पर छोड़ते हैं। किसी anonymous body के अंदर कई काम करना भी काफी सामान्य है: `DT[, { x<-colA+10; x*x/2 }]` या किसी दूसरे package का function call करना: `DT[ , fitdistr(columnA, "normal")]`।
+
+## `DT[,"region"]` vector के बजाय 1-column data.table क्यों लौटाता है?
+
+[ऊपर दिया गया उत्तर](#j-num) देखें। इसके बजाय `DT$region` आज़माएँ। या `DT[["region"]]`।
+
+
+## `DT[, region]`, "region" column के लिए vector क्यों लौटाता है? मुझे 1-column data.table चाहिए।
+
+इसके बजाय `DT[ , .(region)]` आज़माएँ। `.()` `list()` का alias है और यह सुनिश्चित करता है कि data.table लौटाया जाए।
+
+आगे पढ़ते रहें और इसके बाद वाला FAQ भी देखें। किसी एक हिस्से में अटकने से पहले पूरे दस्तावेज़ को एक बार जल्दी से देख लेना उपयोगी है।
+
+## `DT[ , x, y, z]` क्यों काम नहीं करता? मुझे `x`,`y` और `z` ये 3 columns चाहिए थे।
+
+`j` expression दूसरा argument है। `DT[ , c("x","y","z")]` या `DT[ , .(x,y,z)]` आज़माएँ।
+
+## मैंने variable `mycol="x"` assign किया, लेकिन `DT[, mycol]` error देता है। मैं इसे `mycol` variable में मौजूद column name को lookup करने के लिए कैसे कहूँ?
+
+Error यह है कि `"mycol"` नाम का column नहीं मिल सकता, और यह error सही है। `data.table` की scoping `data.frame` से अलग है क्योंकि आप `DT[...]` के अंदर column names को सीधे variables की तरह उपयोग कर सकते हैं और हर column name के आगे `DT$` लगाने की आवश्यकता नहीं होती; ऊपर FAQ 1.1 देखें।
+
+`mycol` का उपयोग करके `DT` से column `x` select करने के कुछ विकल्प हैं:
+
+```r
+DT[, ..mycol] # .. prefix conveys to look for the mycol one level up in calling scope
+DT[, mycol, with=FALSE] # revert to data.frame behavior
+DT[[mycol]] # treat DT as a list and use [[ from base R
+```
+
+`..` prefix के बारे में अधिक जानकारी के लिए `?data.table` देखें।
+
+`with` argument का नाम `base` function `with()` से आया है। जब `with=TRUE` (default) होता है, तो `data.table` लगभग `with()` की तरह काम करता है; यानी `DT[, mycol]`, `with(DT, mycol)` की तरह व्यवहार करता है। जब `with=FALSE` होता है, तो `j` के सभी variables पर standard `data.frame` evaluation rules लागू होते हैं और आप column names को सीधे उपयोग नहीं कर सकते।
+
+## `DT[...]` के अंदर column names को variables की तरह उपयोग करने में क्या लाभ है?
+
+`j` केवल column names तक सीमित नहीं है। आप सीधे `j` में column names से बना कोई भी R _expression_ लिख सकते हैं, जैसे `DT[ , mean(x*y/z)]`। यही बात `i` पर भी लागू होती है, जैसे `DT[x>1000, sum(y*z)]`।
+
+यह `j` expression उन rows पर चलाता है जहाँ `i` expression true होता है। आपको data लौटाना भी आवश्यक नहीं है; उदाहरण के लिए `DT[x>1000, plot(y, z)]`। आप केवल `by = ` जोड़कर `j` को group के अनुसार चला सकते हैं; जैसे `DT[x>1000, sum(y*z), by = w]`। यह column `w` के प्रत्येक group के लिए `j` चलाता है, लेकिन केवल उन rows पर जहाँ `x>1000` है। Query के तीनों हिस्सों (i=where, j=select और by=group by) को square brackets के भीतर रखने से data.table किसी भी हिस्से को evaluate करने से पहले पूरी query को एक साथ देख सकता है। इसलिए वह combined query को performance के लिए optimize कर सकता है। यह इसलिए संभव है क्योंकि R language में uniquely lazy evaluation है (Python और Julia में नहीं)। data.table `DT[...]` के अंदर expressions को evaluate होने से पहले देखता है और evaluation से पहले उन्हें optimize करता है। उदाहरण के लिए, यदि data.table देखता है कि आप 100 columns में से केवल 2 columns का उपयोग कर रहे हैं, तो वह उन 98 columns को subset करने का प्रयास नहीं करेगा जिनकी आपके j expression को आवश्यकता नहीं है।
+
+## ठीक है, अब मुझे समझ आने लगा है कि data.table किस बारे में है, लेकिन आपने R के `data.frame` को ही बेहतर क्यों नहीं बनाया? नया package क्यों आवश्यक था?
+
+जैसा कि [ऊपर बताया गया](#j-num), `[.data.table` में `j`, `[.data.frame` में `j` से मूल रूप से अलग है। यदि base R में `DF[ , 1]` जैसी सरल चीज़ को भी vector के बजाय data.frame लौटाने के लिए बदल दिया जाता, तो CRAN के हजारों packages और users के existing code टूट जाते। जैसे ही हमने data.frame से inherit करने वाली नई class बनाने का निर्णय लिया, हमारे पास कुछ चीज़ें बदलने का अवसर था और हमने ऐसा किया। हम चाहते हैं कि data.table थोड़ा अलग हो और अधिक जटिल syntax को संभव बनाने के लिए इस तरह काम करे। अन्य अंतर भी हैं (नीचे [यहाँ](#SmallerDiffs) देखें)।
+
+इसके अलावा, data.table `data.frame` से _inherit_ करता है। यह स्वयं भी एक `data.frame` है। data.table को किसी भी ऐसे package को दिया जा सकता है जो केवल `data.frame` स्वीकार करता है और वह package data.table पर `[.data.frame` syntax का उपयोग कर सकता है। यह कैसे किया जाता है, इसके लिए [यह उत्तर](https://stackoverflow.com/a/10529888/403310) देखें।
+
+जहाँ संभव हुआ है, हमने R में सुधारों के proposals भी दिए हैं। इनमें से एक को R 2.12.0 में नई feature के रूप में स्वीकार किया गया था:
+
+> `unique()` और `match()` अब उन character vectors पर तेज़ हैं जिनके सभी elements global CHARSXP cache में हैं और जिनकी encoding unmarked (ASCII) है। unique.c में hash code बनाने के तरीके में सुधार सुझाने के लिए Matt Dowle को धन्यवाद।
+
+दूसरा proposal duplicate.c में `memcpy` का उपयोग करने का था, जो C के for loop से काफी तेज़ है। इससे R द्वारा data को internally copy करने के तरीके में सुधार होता (कुछ measures पर लगभग 13 गुना तक)। r-devel पर thread [यहाँ](https://stat.ethz.ch/pipermail/r-devel/2010-April/057249.html) है।
+
+तीसरा, अधिक महत्वपूर्ण proposal स्वीकार किया गया: R 3.3.0 से R data.table के radix sort code का उपयोग करता है:
+
+> data.table (forder) का radix sort algorithm और implementation पिछले radix (counting) sort को replace करता है और order() के लिए नया method जोड़ता है। Matt Dowle और Arun Srinivasan द्वारा contribute किया गया नया algorithm logical, integer (बड़े values सहित), real और character vectors को support करता है। यह अन्य सभी methods से बेहतर प्रदर्शन करता है, लेकिन कुछ सावधानियाँ हैं (देखें ?sort)।
+
+यह हमारे लिए बहुत बड़ी घटना थी और हमने खूब जश्न मनाया। (असल में नहीं।)
+
+## Defaults ऐसे क्यों हैं? यह इसी तरह क्यों काम करता है?
+
+सरल उत्तर यह है कि मुख्य author ने शुरुआत में इसे अपने उपयोग के लिए design किया था। वह इसे इसी तरह चाहते थे। उन्हें code लिखने का यह तरीका अधिक स्वाभाविक और तेज़ लगता है, और code execute भी अधिक तेज़ होता है।
+
+## क्या यह काम `base` के `with()` और `subset()` से पहले ही नहीं किया जा चुका है?
+
+अब तक चर्चा की गई कुछ features के लिए उत्तर हाँ है। यह package base functionality पर ही आगे निर्माण करता है। यह समान प्रकार के काम करता है, लेकिन सही तरीके से उपयोग करने पर कम code की आवश्यकता होती है और यह कई गुना तेज़ execute हो सकता है।
+
+## `X[Y]`, `Y` के सभी columns भी क्यों लौटाता है? क्या इसे `X` का subset नहीं लौटाना चाहिए?
+
+यह v1.5.3 (Feb 2011) में बदला गया था। तब से `X[Y]` में `Y` के non-join columns भी शामिल होते हैं। हम इस feature को _join inherited scope_ कहते हैं क्योंकि `j` expression के लिए केवल `X` के columns ही नहीं, बल्कि `Y` के columns भी उपलब्ध होते हैं। इसका downside यह है कि `X[Y]` कम efficient होता है क्योंकि `Y` के non-join columns के प्रत्येक item को `X` में match होने वाली (संभवतः बहुत बड़ी संख्या में) rows के अनुरूप duplicate करना पड़ता है। इसलिए हम `X[Y]` के बजाय `X[Y, j]` का उपयोग करने की दृढ़ता से सलाह देते हैं। [अगला FAQ](#MergeDiff) देखें।
+
+## `X[Y]` और `merge(X, Y)` में क्या अंतर है? {#MergeDiff}
+
+`X[Y]` एक join है, जिसमें `Y` (या यदि उसकी key है तो `Y` की key) को index की तरह उपयोग करके `X` की rows lookup की जाती हैं।
+
+`Y[X]` एक join है, जिसमें `X` (या यदि उसकी key है तो `X` की key) को index की तरह उपयोग करके `Y` की rows lookup की जाती हैं।
+
+`merge(X,Y)`[^1] दोनों दिशाओं का काम एक साथ करता है। `X[Y]` और `Y[X]` में rows की संख्या सामान्यतः अलग होती है, जबकि `merge(X, Y)` और `merge(Y, X)` द्वारा लौटाई गई rows की संख्या समान होती है।
+
+_लेकिन_ इससे मुख्य बात छूट जाती है। अधिकांश tasks में join या merge के बाद data पर कुछ और करना होता है। यदि बाद में केवल कुछ columns ही उपयोग करने हैं, तो सभी columns को merge क्यों किया जाए? आप कह सकते हैं कि `merge(X[ , ColsNeeded1], Y[ , ColsNeeded2])` उपयोग करें, लेकिन इसके लिए programmer को पहले तय करना होगा कि कौन-से columns आवश्यक हैं। data.table में `X[Y, j]` यह सब एक ही step में कर देता है। जब आप `X[Y, sum(foo*bar)]` लिखते हैं, तो data.table `j` expression को automatically inspect करता है कि वह किन columns का उपयोग करता है। वह केवल उन्हीं columns को subset करेगा; बाकी को ignore कर दिया जाएगा। Memory केवल उन columns के लिए बनाई जाती है जिन्हें `j` उपयोग करता है, और `Y` के columns प्रत्येक group के context में standard R recycling rules का लाभ लेते हैं। मान लें `foo`, `X` में है और `bar`, `Y` में है (और `Y` में 20 अन्य columns भी हैं)। क्या `X[Y, sum(foo*bar)]` लिखना और चलाना उस `merge` से तेज़ और सरल नहीं है जिसमें सब कुछ बेवजह merge करके बाद में `subset` किया जाए?
+
+[^1]: यहाँ हमारा मतलब data.table के `merge` _method_ या `data.frame` के `merge` method, दोनों में से किसी से भी है, क्योंकि इस संदर्भ में दोनों methods एक ही तरीके से काम करते हैं। Method dispatch के बारे में अधिक जानकारी के लिए `?merge.data.table` और [नीचे](#r-dispatch) देखें।
+
+## `X[Y, sum(foo*bar)]` के बारे में और कुछ?
+
+यह व्यवहार v1.9.4 (Sep 2014) में बदल गया। अब यह पहले `X[Y]` join करता है और फिर सभी rows पर `sum(foo*bar)` चलाता है; यानी `X[Y][ , sum(foo*bar)]`। पहले यह `Y` की प्रत्येक row से match होने वाले `X` के प्रत्येक _group_ के लिए `j` चलाता था। यह अभी भी किया जा सकता है क्योंकि यह बहुत उपयोगी है, लेकिन अब आपको स्पष्ट रूप से `by = .EACHI` specify करना होगा; यानी `X[Y, sum(foo*bar), by = .EACHI]`। हम इसे _हर `i` के अनुसार grouping_ कहते हैं।
+
+उदाहरण के लिए, (_join inherited scope_ का भी उपयोग करके इसे थोड़ा और जटिल बनाते हुए):
+
+```{r}
+X = data.table(grp = c("a", "a", "b",
+ "b", "b", "c", "c"), foo = 1:7)
+setkey(X, grp)
+Y = data.table(c("b", "c"), bar = c(4, 2))
+X
+Y
+X[Y, sum(foo*bar)]
+X[Y, sum(foo*bar), by = .EACHI]
+```
+
+## यह अच्छा है। लेकिन जब users पुराने behaviour पर निर्भर थे, तब आप इसे बदल कैसे पाए?
+
+बदलाव की request users से ही आई थी। विचार यह था कि यदि query grouping कर रही है, तो code readability के लिए explicit `by=` मौजूद होना चाहिए। पुराने behaviour को वापस पाने के लिए एक option दिया गया: `options(datatable.old.bywithoutby)`, जिसका default `FALSE` था। इससे users v1.9.4 में अन्य नई features / bug fixes को test करने के लिए upgrade कर सकते थे, और बाद में जब तैयार हों तब by-without-by queries में `by=.EACHI` जोड़कर migration कर सकते थे। हमने बदलाव से पहले के 47 tests बनाए रखे और उन्हें नए tests के रूप में वापस जोड़ा, जिन्हें `options(datatable.old.bywithoutby=TRUE)` के तहत test किया गया। हमने बदलाव और पुराने behaviour पर वापस जाने के तरीके के बारे में startup message जोड़ा। 1 वर्ष बाद इस option को use करने पर warning के साथ deprecated किया गया। 2 वर्षों बाद पुराने behaviour पर वापस जाने का option हटा दिया गया।
+
+v1.9.4 release के समय CRAN या Bioconductor पर 66 packages data.table पर depend करते थे या उसे import करते थे (अब यह संख्या 300 से अधिक है), और उनमें से केवल एक package इस बदलाव से प्रभावित हुआ। इसका कारण यह हो सकता है कि कई packages में comprehensive tests नहीं थे, या downstream packages में `i` की हर row के अनुसार grouping बहुत अधिक उपयोग नहीं होती थी। Release से पहले हम हमेशा नई version को सभी dependent packages के साथ test करते हैं और किसी बदलाव के लिए उन maintainers के साथ coordinate करते हैं। इस मामले में release काफी straightforward था।
+
+इस बदलाव का एक और मजबूत कारण यह था कि पहले वह काम efficiently करने का कोई तरीका नहीं था जो अब `X[Y, sum(foo*bar)]` करता है। आपको `X[Y][ , sum(foo*bar)]` लिखना पड़ता था। यह suboptimal था क्योंकि `X[Y]` सभी columns को join करता था और बिना यह जाने कि केवल `foo` और `bar` आवश्यक हैं, उन सभी को दूसरी compound query को pass कर देता था। इस efficiency problem को हल करने के लिए extra programming effort की आवश्यकता थी: `X[Y, list(foo, bar)][ , sum(foo*bar)]`। `by = .EACHI` के बदलाव ने efficiency के लिए दोनों queries को एक single `DT[...]` query के अंदर express करने की अनुमति देकर इसे सरल बना दिया।
+
+# सामान्य Syntax
+
+## मैं बहुत लंबा `j` expression लिखने से कैसे बच सकता हूँ? आपने कहा है कि मुझे column _names_ का उपयोग करना चाहिए, लेकिन मेरे पास बहुत सारे columns हैं।
+
+Grouping करते समय `j` expression column names को variables की तरह उपयोग कर सकता है, जैसा कि आप जानते हैं, लेकिन यह reserved symbol `.SD` का भी उपयोग कर सकता है, जो प्रत्येक group के लिए **S**ubset of the **D**ata.table को refer करता है (grouping columns को छोड़कर)। इसलिए सभी columns का sum निकालने के लिए बस `DT[ , lapply(.SD, sum), by = grp]` पर्याप्त है। यह शुरू में थोड़ा tricky लग सकता है, लेकिन लिखने और चलाने दोनों में तेज़ है। ध्यान दें कि आपको anonymous function बनाने की आवश्यकता नहीं है। `.SD` object internally efficiently implement किया गया है और function को argument pass करने की तुलना में अधिक efficient है। लेकिन यदि `.SD` symbol `j` में दिखाई देता है, तो data.table को प्रत्येक group के लिए `.SD` को पूरी तरह populate करना पड़ता है, भले ही `j` उसका पूरा उपयोग न करे।
+
+इसलिए उदाहरण के लिए `DT[ , sum(.SD[["sales"]]), by = grp]` न लिखें। यह काम करता है लेकिन inefficient और inelegant है। Intended तरीका `DT[ , sum(sales), by = grp]` है, और यह सैकड़ों गुना तेज़ हो सकता है। यदि आप प्रत्येक group के लिए `.SD` के _सभी_ data का उपयोग करते हैं (जैसे `DT[ , lapply(.SD, sum), by = grp]`), तो यह `.SD` का बहुत अच्छा उपयोग है। यदि आप कई लेकिन सभी columns का उपयोग नहीं कर रहे हैं, तो `.SD` को `.SDcols` के साथ combine कर सकते हैं; `?data.table` देखें।
+
+## `mult` का default अब `"all"` क्यों है?
+
+v1.5.3 में default को `"all"` में बदला गया। जब `i` (या यदि उसकी key है तो `i` की key) में `x` की key से कम columns होते थे, तो `mult` पहले से automatically `"all"` पर set हो जाता था। Default बदलने से यह users के लिए अधिक स्पष्ट और सरल हो गया, क्योंकि यह विषय अक्सर सामने आता था।
+
+v1.3 तक की versions में `"all"` धीमा था। Internally, `"all"` को पहले `"first"` का उपयोग करके join करने, फिर शुरुआत से दोबारा `"last"` का उपयोग करके join करने, और फिर दोनों के बीच diff निकालकर `i` की प्रत्येक row के लिए `x` में matches की span निर्धारित करने के रूप में implement किया गया था। हालांकि, हम अधिकतर single rows से join करते हैं, जहाँ `"first"`, `"last"` और `"all"` एक ही result देते हैं। अधिकांश situations के लिए maximum performance को प्राथमिकता दी गई, इसलिए default `"first"` चुना गया। Non-unique key (आमतौर पर grouping variable वाला single column) के साथ काम करते समय `DT["A"]` उस group की पहली row लौटाता था, इसलिए group की सभी rows पाने के लिए `DT["A", mult = "all"]` आवश्यक था।
+
+v1.4 में C की binary search को deepest level पर branch करने के लिए बदला गया ताकि first और last दोनों पाए जा सकें। यह branch संभवतः RAM के उन्हीं final pages के भीतर होती है, इसलिए `mult` को `"all"` default बनाने में अब speed disadvantage नहीं होना चाहिए। हमने users को पहले से चेतावनी दी थी कि default बदल सकता है, और v1.5.3 में यह बदलाव किया गया।
+
+data.table की भविष्य की version key और _unique key_ के बीच distinction की अनुमति दे सकती है। Internally, जब `x` की सभी key columns पर join किया गया हो और `x` की key unique key हो, तब `mult = "all"`, `mult = "first"` की तरह अधिक perform कर सकता है। Unique key बनाए रखने के लिए data.table को insert और update पर checks की आवश्यकता होगी। Unique key specify करने का एक लाभ यह होगा कि performance के अतिरिक्त data.table यह भी सुनिश्चित करेगा कि duplicates insert न हो सकें।
+
+## मैं `j` में `c()` का उपयोग कर रहा हूँ और अजीब results मिल रहे हैं।
+
+यह confusion का एक सामान्य कारण है। `data.frame` में आप उदाहरण के लिए यह करने के आदी हैं:
+
+```{r}
+DF = data.frame(x = 1:3, y = 4:6, z = 7:9)
+DF
+DF[ , c("y", "z")]
+```
+
+जो दो columns लौटाता है। data.table में आप जानते हैं कि column names को सीधे उपयोग किया जा सकता है, इसलिए आप यह आज़मा सकते हैं:
+
+```{r}
+DT = data.table(DF)
+DT[ , c(y, z)]
+```
+
+लेकिन यह एक vector लौटाता है। याद रखें कि `j` expression `DT` के environment के भीतर evaluate होता है और `c()` एक vector लौटाता है। यदि 2 या अधिक columns आवश्यक हैं, तो इसके बजाय `list()` या `.()` का उपयोग करें:
+
+```{r}
+DT[ , .(y, z)]
+```
+
+`c()` data.table में भी उपयोगी हो सकता है, लेकिन इसका behaviour `[.data.frame` से अलग है।
+
+## मैंने कई columns वाली एक complex table बनाई है। मैं उसे नई table के template के रूप में उपयोग करना चाहता हूँ; यानी एक नई table जिसमें कोई rows न हों लेकिन column names और types मेरी table से copy हो जाएँ। क्या यह आसानी से किया जा सकता है?
+
+हाँ। यदि आपकी complex table का नाम `DT` है, तो `NEWDT = DT[0]` आज़माएँ।
+
+## क्या null data.table, `DT[0]` के समान है?
+
+नहीं। "null data.table" से हमारा मतलब `data.table(NULL)` या `as.data.table(NULL)` के result से है; यानी:
+
+```{r}
+data.table(NULL)
+data.frame(NULL)
+as.data.table(NULL)
+as.data.frame(NULL)
+is.null(data.table(NULL))
+is.null(data.frame(NULL))
+```
+
+Null data.table|`frame`, `NULL` है जिसके साथ कुछ attributes जुड़े हुए हैं, जिसका अर्थ है कि वह अब `NULL` नहीं रहा। R में केवल pure `NULL` ही `NULL` होता है, जैसा `is.null()` से test किया जाता है। "null data.table" कहते समय हम lower case null का उपयोग करते हैं ताकि उसे upper case `NULL` से अलग पहचाना जा सके। Null data.table को test करने के लिए `length(DT) == 0` या `ncol(DT) == 0` का उपयोग करें (`length` थोड़ा तेज़ है क्योंकि यह primitive function है)।
+
+एक _empty_ data.table (`DT[0]`) में एक या अधिक columns होते हैं, जिनमें से सभी empty होते हैं। उन empty columns के names और types फिर भी बने रहते हैं।
+
+```{r}
+DT = data.table(a = 1:3, b = c(4, 5, 6), d = c(7L,8L,9L))
+DT[0]
+sapply(DT[0], class)
+```
+
+## `DT()` alias को क्यों हटाया गया? {#DTremove1}
+`DT` को मूल रूप से `j `expressions की list के wrapper के रूप में introduce किया गया था। चूँकि `DT` data.table का alias था, इसलिए उन cases में silent recycling संभालने का यह सुविधाजनक तरीका था जहाँ `j` list का प्रत्येक item अलग length में evaluate होता था। हालांकि यह alias grouping धीमी होने के कारणों में से एक था।
+
+v1.3 से `j` argument में इसके बजाय `list()` या `.()` pass किया जाना चाहिए। ये बहुत तेज़ हैं, विशेषकर तब जब groups की संख्या अधिक हो। Internally यह एक non-trivial बदलाव था। Vector recycling अब internally की जाती है, साथ ही grouping के लिए कई अन्य speed enhancements भी लागू किए गए हैं।
+
+## लेकिन मेरा code `j = DT(...)` का उपयोग करता है और काम करता है। पिछले FAQ में कहा गया कि `DT()` हटा दिया गया है। {#DTremove2}
+
+तब आप 1.5.3 से पहले की version उपयोग कर रहे हैं। 1.5.3 से पहले `[.data.table` `j` में `DT()` के उपयोग को detect करता था और automatically उसे `list()` call से replace कर देता था। यह existing users के transition में मदद करने के लिए किया गया था।
+
+## `j` expressions के scoping rules क्या हैं?
+
+Subset को एक ऐसे environment की तरह सोचें जहाँ सभी column names variables हैं। जब `X[Y, sum(foo)]` जैसी query के `j` में variable `foo` उपयोग होता है, तो `foo` को निम्न क्रम में खोजा जाता है:
+
+ 1. `X` के subset का scope; यानी `X` के column names।
+ 2. `Y` की प्रत्येक row का scope; यानी `Y` के column names (_join inherited scope_)
+ 3. Calling frame का scope; उदाहरण के लिए वह line जो data.table query से पहले दिखाई देती है।
+ 4. Reader के लिए exercise: क्या इसके बाद यह calling frames में ऊपर की ओर जाता है, या सीधे `globalenv()` पर जाता है?
+ 5. Global environment
+
+यह _lexical scoping_ है, जैसा [R FAQ 3.3.1](https://cran.r-project.org/doc/FAQ/R-FAQ.html#Lexical-scoping) में समझाया गया है। हालांकि जिस environment में function बनाया गया था वह relevant नहीं है, क्योंकि यहाँ _कोई function नहीं है_। `j` को कोई anonymous _function_ pass नहीं किया जाता। इसके बजाय एक anonymous _body_ pass की जाती है; उदाहरण के लिए:
+
+```{r}
+DT = data.table(x = rep(c("a", "b"), c(2, 3)), y = 1:5)
+DT
+DT[ , {z = sum(y); z + 3}, by = x]
+```
+
+कुछ programming languages इसे _lambda_ कहती हैं।
+
+## क्या मैं groups के बीच चलते समय `j` expression को trace कर सकता हूँ? {#j-trace}
+
+कुछ ऐसा आज़माएँ:
+
+```{r}
+DT[ , {
+ cat("Objects:", paste(objects(), collapse = ","), "\n")
+ cat("Trace: x=", as.character(x), " y=", y, "\n")
+ sum(y)},
+ by = x]
+```
+
+## प्रत्येक group के अंदर group variables की length 1 क्यों होती है?
+
+[ऊपर](#j-trace), `x` एक grouping variable है और (v1.6.1 से) इसकी `length` 1 होती है, यदि इसे inspect किया जाए या `j` में उपयोग किया जाए। यह efficiency और convenience के लिए है। इसलिए नीचे दिए गए दोनों statements में कोई अंतर नहीं है:
+
+```{r}
+DT[ , .(g = 1, h = 2, i = 3, j = 4, repeatgroupname = x, sum(y)), by = x]
+DT[ , .(g = 1, h = 2, i = 3, j = 4, repeatgroupname = x[1], sum(y)), by = x]
+```
+
+यदि आपको current group का size चाहिए, तो किसी column पर `length()` call करने के बजाय `.N` का उपयोग करें।
+
+## केवल पहली 10 rows print होती हैं, मैं अधिक rows कैसे print करूँ?
+
+यहाँ दो चीज़ें हो रही हैं। पहली, यदि data.table में rows की संख्या बड़ी है (default रूप से `> 100`), तो console में default रूप से data.table का summary print किया जाता है। दूसरी, बड़े data.table का summary उसकी ऊपर और नीचे की `n` rows (`= 5` default) लेकर और केवल उन्हीं को print करके बनाया जाता है। दोनों parameters—summary कब trigger हो और table के कितने हिस्से को summary में उपयोग किया जाए—R के `options` mechanism या सीधे `print` function call करके configure किए जा सकते हैं।
+
+उदाहरण के लिए, यदि आप चाहते हैं कि data.table का summary केवल तब हो जब उसमें 50 से अधिक rows हों, तो `options(datatable.print.nrows = 50)` उपयोग कर सकते हैं। Default summary को पूरी तरह disable करने के लिए `options(datatable.print.nrows = Inf)` उपयोग कर सकते हैं। आप `print` को सीधे भी call कर सकते हैं, जैसे `print(your.data.table, nrows = Inf)`।
+
+यदि आप data.table summary में केवल ऊपर और नीचे की कुल 10 rows से अधिक दिखाना चाहते हैं (मान लें 20), तो उदाहरण के लिए `options(datatable.print.topn = 20)` set करें। फिर से, आप `print` को सीधे call कर सकते हैं, जैसे `print(your.data.table, topn = 20)`।
+
+## `X[Y]` join में क्या होगा यदि `X` में `"Y"` नाम का column हो?
+
+जब `i` केवल एक single name जैसे `Y` हो, तो उसे calling frame में evaluate किया जाता है। अन्य सभी cases में, जैसे `.()` calls या अन्य expressions, `i` को `X` के scope में evaluate किया जाता है। इससे `X[J(unique(colA)), mult = "first"]` जैसे _self-joins_ आसानी से किए जा सकते हैं।
+
+## `X[Z[Y]]` fail हो रहा है क्योंकि `X` में `"Y"` नाम का column है। मैं चाहता हूँ कि यह calling scope की table `Y` का उपयोग करे।
+
+`Z[Y]` हिस्सा single name नहीं है, इसलिए इसे `X` के frame में evaluate किया जाता है और समस्या होती है। `tmp = Z[Y]; X[tmp]` आज़माएँ। यह तब भी robust है यदि `X` में `"tmp"` नाम का column हो, क्योंकि `tmp` single name है। यदि आपको इस प्रकार के conflicts अक्सर मिलते हैं, तो एक सरल समाधान यह हो सकता है कि सभी tables के names uppercase और सभी column names lowercase रखें, या कोई similar scheme अपनाएँ।
+
+## क्या आप और समझा सकते हैं कि data.table, `base` के `A[B]` syntax से क्यों inspired है?
+
+एक example matrix `A` के साथ `A[B]` syntax पर विचार करें:
+```{r}
+A = matrix(1:12, nrow = 4)
+A
+```
+
+Cells `(1, 2) = 5` और `(3, 3) = 11` पाने के लिए कई users (हमारा मानना है) पहले यह कोशिश कर सकते हैं:
+```{r}
+A[c(1, 3), c(2, 3)]
+```
+
+लेकिन यह उन rows और columns का union लौटाता है। Cells को reference करने के लिए 2-column matrix आवश्यक है। `?Extract` कहता है:
+
+> जब arrays को `[` से index किया जाता है, तो single argument `i` एक ऐसा matrix हो सकता है जिसमें उतने ही columns हों जितनी `x` की dimensions हैं; result तब एक vector होता है जिसमें `i` की प्रत्येक row में दिए गए indices के sets के corresponding elements होते हैं।
+
+फिर से कोशिश करते हैं।
+
+```{r}
+B = cbind(c(1, 3), c(2, 3))
+B
+A[B]
+```
+
+Matrix एक 2-dimensional structure है जिसमें row names और column names होते हैं। क्या names के साथ भी हम यही कर सकते हैं?
+
+```{r}
+rownames(A) = letters[1:4]
+colnames(A) = LETTERS[1:3]
+A
+B = cbind(c("a", "c"), c("B", "C"))
+A[B]
+```
+
+हाँ, कर सकते हैं। क्या यही `data.frame` के साथ कर सकते हैं?
+
+```{r}
+A = data.frame(A = 1:4, B = letters[11:14], C = pi*1:4)
+rownames(A) = letters[1:4]
+A
+B
+A[B]
+```
+
+लेकिन ध्यान दें कि result को `character.` में coerce कर दिया गया। Syntax को काम करने के लिए R ने पहले `A` को `matrix` में coerce किया, लेकिन result ideal नहीं है। अब `B` को `data.frame` बनाकर देखते हैं।
+
+```{r}
+B = data.frame(c("a", "c"), c("B", "C"))
+cat(try(A[B], silent = TRUE))
+```
+
+इसलिए base R में हम एक `data.frame` को दूसरे `data.frame` से subset नहीं कर सकते। क्या होगा यदि हम ऐसे row names और column names चाहते हों जो `character` नहीं बल्कि `integer` या `float` हों? क्या होगा यदि हमें mixed types की 2 से अधिक dimensions चाहिए हों? यहीं data.table आता है।
+
+इसके अलावा matrices, विशेषकर sparse matrices, अक्सर 3-column tuple के रूप में store किए जाते हैं: `(i, j, value)`। इसे key-value pair की तरह सोचा जा सकता है जहाँ `i` और `j` मिलकर 2-column key बनाते हैं। यदि हमारे पास एक से अधिक values हों, संभवतः अलग-अलग types की, तो यह `(i, j, val1, val2, val3, ...)` जैसा दिखाई दे सकता है। यह बहुत हद तक `data.frame` जैसा दिखता है। इसलिए data.table `data.frame` को इस तरह extend करता है कि `data.frame` `X` को `data.frame` `Y` से subset किया जा सके, जिससे `X[Y]` syntax मिलता है।
+
+## तो नया package बनाने के बजाय base को ऐसा करने के लिए बदला जा सकता है?
+`data.frame` का उपयोग _हर जगह_ होता है, इसलिए इसमें _कोई भी_ बदलाव करना बहुत कठिन है।
+data.table `data.frame` से _inherit_ करता है। यह स्वयं भी एक `data.frame` है। data.table को ऐसे किसी भी package को दिया जा सकता है जो _केवल_ `data.frame` स्वीकार करता है। जब वह package data.table पर `[.data.frame` syntax का उपयोग करता है, तो यह काम करता है। ऐसा इसलिए होता है क्योंकि `[.data.table` यह देखता है कि उसे कहाँ से call किया गया था। यदि उसे ऐसे package से call किया गया हो, तो `[.data.table`, `[.data.frame` की ओर divert कर देता है।
+
+## मैंने सुना है कि data.table syntax SQL के समान है।
+हाँ:
+
+ - `i` $\Leftrightarrow$ where
+ - `j` $\Leftrightarrow$ select
+ - `:=` $\Leftrightarrow$ update
+ - `by` $\Leftrightarrow$ group by
+ - `i` $\Leftrightarrow$ order by (in compound syntax)
+ - `i` $\Leftrightarrow$ having (in compound syntax)
+ - `nomatch = NA` $\Leftrightarrow$ outer join
+ - `nomatch = NULL` $\Leftrightarrow$ inner join
+ - `mult = "first"|"last"` $\Leftrightarrow$ N/A because SQL is inherently unordered
+ - `roll = TRUE` $\Leftrightarrow$ N/A because SQL is inherently unordered
+
+General form है:
+
+```r
+DT[where, select|update, group by][order by][...] ... [...]
+```
+
+R में column vectors का एक मुख्य लाभ यह है कि वे SQL[^2] के विपरीत _ordered_ होते हैं। हम `data.table` queries में `diff()` जैसे ordered functions का उपयोग कर सकते हैं और केवल SQL में defined functions तक सीमित रहने के बजाय किसी भी package का _कोई भी_ R function उपयोग कर सकते हैं। एक disadvantage यह है कि R objects को memory में fit होना आवश्यक है, लेकिन `ff`, `bigmemory`, `mmap` और `indexing` जैसे कई R packages के साथ यह स्थिति बदल रही है।
+
+[^2]: यह जानकर आश्चर्य हो सकता है कि SQL में `select top 10 * from ...` समय के साथ reliably वही rows नहीं लौटाता। Row order की guarantee के लिए आपको `order by` clause शामिल करना या clustered index का उपयोग करना आवश्यक है; यानी SQL inherently unordered है।
+
+## `data.frame` और data.table के बीच छोटे syntax differences क्या हैं? {#SmallerDiffs}
+
+ - `DT[3]` तीसरी _row_ को refer करता है, लेकिन `DF[3]` तीसरे _column_ को।
+ - `DT[3, ] == DT[3]`, लेकिन `DF[ , 3] == DF[3]` (data.frame में यह कुछ confusing है, जबकि data.table consistent है)।
+ - इसी कारण हम कहते हैं कि `DT` में comma _optional_ है, लेकिन `DF` में optional नहीं है।
+ - `DT[[3]] == DF[, 3] == DF[[3]]`
+ - `DT[i, ]`, जहाँ `i` एक single integer है, single row लौटाता है, ठीक `DF[i, ]` की तरह; लेकिन matrix के single-row subset से अलग, जो vector लौटाता है।
+ - `DT[ , j]` जहाँ `j` single integer है, one-column data.table लौटाता है; जबकि `DF[, j]` default रूप से vector लौटाता है।
+ - `DT[ , "colA"][[1]] == DF[ , "colA"]`।
+ - `DT[ , colA] == DF[ , "colA"]` (वर्तमान में data.table v1.9.8 में ऐसा है लेकिन यह बदलने वाला है; release notes देखें)।
+ - `DT[ , list(colA)] == DF[ , "colA", drop = FALSE]`
+ - `DT[NA]` `NA` की 1 row लौटाता है, लेकिन `DF[NA]` पूरे `DF` की copy लौटाता है जिसमें हर जगह `NA` होते हैं। R में symbol `NA` का type `logical` है और इसलिए `[.data.frame` द्वारा recycle किया जाता है। User का intention शायद `DF[NA_integer_]` था। `[.data.table` convenience के लिए automatically इसी probable intention की ओर divert करता है।
+ - `DT[c(TRUE, NA, FALSE)]` `NA` को `FALSE` मानता है, लेकिन `DF[c(TRUE, NA, FALSE)]` प्रत्येक `NA` के लिए `NA` rows लौटाता है।
+ - `DT[ColA == ColB]`, `DF[!is.na(ColA) & !is.na(ColB) & ColA == ColB, ]` से सरल है।
+ - `data.frame(list(1:2, "k", 1:4))` 3 columns बनाता है; data.table एक `list` column बनाता है।
+ - Convenience के लिए `check.names` का default `data.frame` में `TRUE` लेकिन data.table में `FALSE` है।
+ - `data.table` ने हमेशा `stringsAsFactors=FALSE` default रखा है। R 4.0.0 (Apr 2020) में `data.frame` का default `TRUE` से बदलकर `FALSE` कर दिया गया और इस मामले में अब कोई अंतर नहीं है; [stringsAsFactors, Kurt Hornik, Feb 2020](https://developer.r-project.org/Blog/public/2020/02/16/stringsasfactors/) देखें।
+ - `list` columns में atomic vectors को print करते समय `data.frame` में `", "` से collapse किया जाता है, लेकिन data.table में `","` से, और 6th item के बाद trailing comma दिया जाता है ताकि बड़े embedded objects गलती से print न हो जाएँ।
+ - data.frames के विपरीत data.table बिना columns वाली rows store नहीं कर सकता, क्योंकि rows को columns के children माना जाता है: `nrow(DF[, 0])` rows की संख्या लौटाता है, जबकि `nrow(DT[, 0])` हमेशा 0 लौटाता है; लेकिन issue [#2422](https://github.com/Rdatatable/data.table/issues/2422) देखें।
+
+`[.data.frame` में हम बहुत बार `drop = FALSE` set करते हैं। जब हम इसे भूल जाते हैं, तो ऐसे edge cases में bugs हो सकते हैं जहाँ single columns select किए जाते हैं और अचानक single-column `data.frame` के बजाय vector लौट आता है। `[.data.table` में हमने इसे consistent बनाने का अवसर लिया और `drop` को हटा दिया।
+
+जब data.table किसी ऐसे package को दिया जाता है जो data.table-aware नहीं है, तो वह package इन differences से प्रभावित नहीं होता; यह बस काम करता है।
+
+## मैं `j` का उपयोग केवल उसके side effect के लिए कर रहा हूँ, लेकिन फिर भी data return हो रहा है। इसे कैसे रोकूँ?
+
+इस case में `j` को `invisible()` से wrap किया जा सकता है; जैसे `DT[ , invisible(hist(colB)), by = colA]`[^3]
+
+[^3]: उदाहरण के लिए `hist()` graphics device पर plot करने के अलावा breakpoints भी लौटाता है।
+
+## v1.5 से `[.data.table` में `drop` argument क्यों है?
+
+ताकि data.table `...` का उपयोग किए बिना `data.frame` से inherit कर सके। यदि हम `...` का उपयोग करते, तो invalid argument names पकड़े नहीं जाते।
+
+`drop` argument का `[.data.table` द्वारा कभी उपयोग नहीं किया जाता। यह उन packages के लिए placeholder है जो data.table-aware नहीं हैं और data.table पर सीधे `[.data.frame` syntax का उपयोग करते हैं।
+
+## Rolling joins बहुत अच्छे और बहुत तेज़ हैं! क्या इन्हें program करना कठिन था?
+`i` row पर या उससे पहले लागू होने वाली prevailing row वैसे भी binary search द्वारा test की जाने वाली final row होती है। इसलिए `roll = TRUE` मूलतः binary search के C code में उसी row को return करने का switch है।
+
+## `DT[i, col := value]` पूरे `DT` को क्यों लौटाता है? मुझे या तो कोई visible value नहीं चाहिए थी (`<-` के अनुरूप), या ऐसा message/return value जो बताए कि कितनी rows update हुईं। यह स्पष्ट नहीं होता कि data वास्तव में by reference update हो चुका है।
+
+आपकी अपेक्षाओं के अनुरूप इसे v1.8.3 में बदल दिया गया। कृपया upgrade करें।
+
+पूरा `DT` लौटाया जाता है (अब invisibly) ताकि compound syntax काम कर सके; जैसे `DT[i, done := TRUE][ , sum(done)]`। Updated rows की संख्या तब return होती है जब `verbose` `TRUE` हो, चाहे per-query basis पर या globally `options(datatable.verbose = TRUE)` का उपयोग करके।
+
+## ठीक है, धन्यवाद। `DT[i, col := value]` के result को invisibly return करना इतना कठिन क्यों था?
+R internally `[` के लिए visibility को force करके on कर देता है। FunTab के eval column का value (देखें [src/main/names.c](https://github.com/wch/r-source/blob/trunk/src/main/names.c)) `[` के लिए `0` है, जिसका अर्थ है "force `R_Visible` on" (देखें [R-Internals section 1.6](https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Autoprinting))। इसलिए जब हमने `invisible()` का उपयोग किया या सीधे `R_Visible` को `0` set किया, तो [src/main/eval.c](https://github.com/wch/r-source/blob/trunk/src/main/eval.c) में `eval` उसे फिर से on कर देता था।
+
+इस problem को solve करने की key यह थी कि `:=` के बाद print method को run होने से रोकने की कोशिश बंद की जाए। इसके बजाय अब (v1.8.3 से) `:=` के अंदर हम एक global flag set करते हैं जिसका उपयोग print method यह तय करने के लिए करता है कि वास्तव में print करना है या नहीं।
+
+## `:=` का उपयोग करने के बाद result को console पर print करने के लिए कभी-कभी मुझे `DT` दो बार क्यों टाइप करना पड़ता है?
+
+यह [#869](https://github.com/Rdatatable/data.table/issues/869) को काम कराने का दुर्भाग्यपूर्ण downside है। यदि किसी function के अंदर `:=` उपयोग किया गया हो और function के अंत से पहले कोई `DT[]` न हो, तो अगली बार prompt पर `DT` टाइप करने पर कुछ print नहीं होगा। `DT` को दोबारा टाइप करने पर print होगा। इससे बचने के लिए अपने function में अंतिम `:=` के बाद `DT[]` शामिल करें। यदि यह संभव न हो (जैसे वह function ऐसा हो जिसे आप बदल नहीं सकते), तो prompt पर `print(DT)` और `DT[]` का उपयोग निश्चित रूप से print करेगा। पहले की तरह, `:=` query के अंत में अतिरिक्त `[]` जोड़ना update करने और फिर print करने के लिए recommended idiom है; जैसे `DT[,foo:=3L][]`।
+
+## मैंने देखा है कि `base::cbind.data.frame` (और `base::rbind.data.frame`) data.table द्वारा बदले हुए लगते हैं। यह कैसे संभव है? और क्यों?
+
+यह R >= 4.0.0 में rbind और cbind S3 method dispatch ठीक होने से पहले का temporary, last-resort solution था। मूल समस्या यह थी कि `data.table`, `data.frame` से inherit करता है, _और_ `base::cbind` तथा `base::rbind` (विशेष रूप से) `?cbind` में documented तरीके से internally अपना S3 dispatch करते हैं। `data.table` workaround में प्रत्येक function की शुरुआत में सीधे `base` के भीतर एक `for` loop जोड़ना शामिल था। यह modification dynamically किया जाता था; यानी `base` से `cbind.data.frame` की definition fetch की जाती, शुरुआत में `for` loop जोड़ा जाता, और फिर वापस `base` में assign कर दिया जाता। इस solution को R की अलग-अलग versions में `base::cbind.data.frame` की अलग definitions और अज्ञात future changes के प्रति robust बनाया गया था। यह अच्छी तरह काम करता था। Competing requirements ये थे:
+
+ - `cbind(DT, DF)` को काम करना चाहिए। `cbind.data.table` define करना काम नहीं करता था क्योंकि `base::cbind` अपना S3 dispatch करता है और (R 4.0.0 से पहले) यह आवश्यक था कि उसे दिए गए प्रत्येक object का _पहला_ `cbind` method _identical_ हो। `cbind(DT, DF)` में यह true नहीं है क्योंकि `DT` का पहला method `cbind.data.table` है जबकि `DF` का पहला method `cbind.data.frame` है। तब `base::cbind` अपने internal `bind` code पर fall through करता था, जो `DT` को regular `list` की तरह treat करता प्रतीत होता था और बहुत अजीब तथा unusable `matrix` output लौटाता था। [नीचे](#cbinderror) देखें। हम users को सिर्फ यह नहीं कह सकते कि `cbind(DT, DF)` call न करें, क्योंकि `ggplot2` जैसे packages ऐसा call करते हैं ([test 167.2](https://github.com/Rdatatable/data.table/blob/master/inst/tests/tests.Rraw#L444-L447))।
+
+ - इससे स्वाभाविक रूप से `cbind.data.frame` को mask करने का प्रयास किया गया। चूँकि data.table एक `data.frame` है, `cbind` को `DT` और `DF` दोनों के लिए वही method मिलना चाहिए था। लेकिन यह भी काम नहीं किया क्योंकि `base::cbind` methods को पहले `base` में खोजता प्रतीत होता है; यानी `base::cbind.data.frame` mask नहीं किया जा सकता।
+
+ - अंत में हमने `cbind` को ही mask करने की कोशिश की (v1.6.5 और v1.6.6)। इससे `cbind(DT, DF)` काम करने लगा, लेकिन package `IRanges` के साथ compatibility issues पैदा हो गए, क्योंकि `IRanges` भी `cbind` को mask करता है। यदि `search()` path पर `IRanges`, data.table से नीचे था तो यह काम करता था; लेकिन यदि `IRanges` ऊपर था, तो data.table का `cbind` कभी call नहीं होता था और फिर वही अजीब दिखने वाला `matrix` output आता था (देखें [नीचे](#cbinderror))।
+
+Sep 2019 में यह issue ठीक करने के लिए R core team का बहुत धन्यवाद। data.table v1.12.6+ अब R >= 4.0.0 में workaround लागू नहीं करता।
+
+## मैंने method dispatch के बारे में पढ़ा है (जैसे `merge` कभी `merge.data.table` पर dispatch कर सकता है और कभी नहीं), लेकिन R को कैसे पता चलता है कि dispatch कैसे करना है? क्या dots significant या special हैं? R आखिर कैसे जानता है कि कौन-सा function कब dispatch करना है? {#r-dispatch}
+
+यह प्रश्न काफी बार आता है, लेकिन वास्तव में इसका उत्तर आश्चर्यजनक रूप से सरल है। `merge` जैसा function _generic_ होता है यदि उसमें `UseMethod` call शामिल हो। जब आप लोगों को यह चर्चा करते देखते हैं कि कोई function _generic_ है या नहीं, तो वे बस function को बिना `()` के टाइप करते हैं, उसके अंदर का program code देखते हैं, और यदि उसमें `UseMethod` call दिखता है तो वह _generic_ है। `UseMethod` क्या करता है? यह सचमुच function name को first argument की class के साथ period (`.`) से जोड़ता है और फिर वही arguments pass करते हुए उस function को call करता है। बस इतना ही। उदाहरण के लिए `merge(X, Y)` में `UseMethod` call होता है, जिसका अर्थ है कि यह फिर _dispatch_ करता है (यानी call करता है) `paste("merge", class(X), sep = ".")`। जिन functions के name में dots होते हैं वे methods हो भी सकते हैं और नहीं भी। Dot वास्तव में अपने आप में महत्वपूर्ण नहीं है; यह केवल separator है जिसका उपयोग `UseMethod` करता है। इस background को समझने के बाद यह स्पष्ट होना चाहिए कि, उदाहरण के लिए, R users को यह तुरंत क्यों स्पष्ट होता है कि `as.data.table.data.frame`, generic function `as.data.table` के लिए `data.frame` method है। इसी तरह यह भी स्पष्ट हो सकता है कि केवल नाम देखकर यह जानना आसान नहीं है कि `ls.fit`, `ls` generic function का fit method नहीं है। यह तभी पता चलता है जब आप `ls` (न कि `ls()`) टाइप करते हैं और देखते हैं कि वह केवल `UseMethod` की single call नहीं है।
+
+अब आप पूछ सकते हैं: R में यह कहाँ documented है? उत्तर: यह काफी स्पष्ट है, लेकिन आपको पहले यह जानना होगा कि `?UseMethod` देखना है, और उस help file में लिखा है:
+
+> जब `UseMethod('fun')` call करने वाला function class attribute `c('first', 'second')` वाले object पर apply किया जाता है, तो system `fun.first` नाम का function खोजता है और यदि वह मिलता है तो object पर उसे apply करता है। यदि ऐसा function नहीं मिलता, तो `fun.second` नाम का function try किया जाता है। यदि कोई class name suitable function नहीं देता, तो यदि `fun.default` मौजूद है तो उसका उपयोग किया जाता है, अन्यथा error आता है।
+
+सौभाग्य से "How does R method dispatch work" के लिए internet search (इस लेखन के समय) top links में `?UseMethod` help page देता है। यह मानना होगा कि अन्य links जल्दी ही S3 बनाम S4, internal generics आदि की intricacies में उतर जाते हैं।
+
+लेकिन basic S3 dispatch जैसी features—function name और class name को जोड़ देना—एक कारण हैं कि कुछ R users R को बहुत पसंद करते हैं। यह बहुत सरल है। किसी complicated registration या signature की आवश्यकता नहीं होती। सीखने के लिए बहुत अधिक नहीं है। data.table के लिए `merge` method बनाने हेतु सचमुच केवल `merge.data.table` नाम का function बनाना आवश्यक था।
+
+## कुछ `data.table` queries में `T` और `F`, `TRUE` और `FALSE` से अलग व्यवहार क्यों करते हैं?
+
+`data.table` में `TRUE` और `FALSE` के abbreviations के रूप में `T` और `F` का उपयोग unexpected behaviour दे सकता है। ऐसा इसलिए है क्योंकि `T` और `F` global variables हैं जिन्हें redefine किया जा सकता है, इसलिए उन्हें logical constants के बजाय variable names की तरह treat किया जा सकता है। `TRUE` और `FALSE` के साथ यह समस्या नहीं होती। `T` और `F` से बचना सामान्य R usage के लिए भी सलाह है, लेकिन `data.table` में यह कभी-कभी आश्चर्यजनक तरीके से दिखाई दे सकता है, उदाहरण के लिए:
+
+```r
+DT <- data.table(x=rep(c("a", "b", "c"), each = 3), y=c(1, 3, 6), v=1:9)
+
+# Using TRUE/FALSE works as expected in cases like the ones below:
+
+DT[, .SD, .SDcols=c(TRUE, TRUE, FALSE)]
+# A) This selects the first two columns (x and y) and excludes the third one (v). Output:
+#> x y
+#> 1: a 1
+#> 2: a 3
+#> 3: a 6
+#> 4: b 1
+#> 5: b 3
+#> 6: b 6
+#> 7: c 1
+#> 8: c 3
+#> 9: c 6
+
+DT[, .SD, .SDcols=c(T, T, F), with=FALSE]
+# B) This forces data.table to treat T/F as logical constants.
+# Same output as DT[, .SD, .SDcols=c(TRUE, TRUE, FALSE)]
+
+# But, using T/F may lead to unexpected behavior in cases like:
+
+DT[, .SD, .SDcols=c(T, T, F)]
+# data.table treats T and F as variable names here, not logical constants. Output:
+#> Detected that j uses these columns:
+#> [1] TRUE TRUE FALSE
+```
+
+सामान्य सलाह के रूप में, `lintr::T_and_F_symbol_linter()` `T` और `F` के उपयोग को detect करता है और ऐसी समस्याओं से बचने के लिए उन्हें `TRUE` और `FALSE` से replace करने का सुझाव देता है।
+
+# Compute time से संबंधित प्रश्न
+
+## मेरे पास 20 columns और बहुत बड़ी संख्या में rows हैं। एक column का expression इतना तेज़ क्यों है?
+
+इसके कई कारण हैं:
+
+ - केवल उसी column पर grouping होती है; अन्य 19 ignore किए जाते हैं क्योंकि data.table `j` expression को inspect करके समझ लेता है कि वह बाकी columns का उपयोग नहीं करता।
+ - केवल सबसे बड़े group के लिए एक memory allocation किया जाता है, फिर वही memory अन्य groups के लिए reuse होती है। Collect करने के लिए बहुत कम garbage बनता है।
+ - R एक in-memory column store है; यानी columns RAM में contiguous होते हैं। RAM से L2 cache में page fetches को न्यूनतम किया जाता है।
+
+## मेरे बड़े table पर `key` नहीं है, फिर भी grouping बहुत तेज़ है। क्यों?
+
+data.table radix sorting का उपयोग करता है। यह अन्य sort algorithms से काफी तेज़ है। अधिक जानकारी के लिए [हमारी presentations](https://github.com/Rdatatable/data.table/wiki/Presentations) देखें, विशेष रूप से useR!2015 Denmark वाली।
+
+यह एक कारण है कि `setkey()` भी तेज़ है।
+
+जब कोई `key` set नहीं होती, या हम key के क्रम से अलग क्रम में group करते हैं, तो हम इसे _ad hoc_ `by` कहते हैं।
+
+## Key के columns से grouping, _ad hoc_ `by` से तेज़ क्यों होती है?
+
+क्योंकि प्रत्येक group RAM में contiguous होता है, जिससे page fetches कम होते हैं और memory को C में loop करने के बजाय bulk में (`memcpy` in C) copy किया जा सकता है।
+
+## data.table में primary और secondary indexes क्या हैं?
+
+Manual: [`?setkey`](https://r-datatable.com/reference/setkey.html)
+S.O.: [What is the purpose of setting a key in data.table?](https://stackoverflow.com/questions/20039335/what-is-the-purpose-of-setting-a-key-in-data-table/20057411#20057411)
+
+`setkey(DT, col1, col2)` rows को पहले column `col1` के अनुसार order करता है, फिर `col1` के प्रत्येक group के भीतर `col2` के अनुसार। यह _primary index_ है। Row order RAM में _by reference_ बदलता है। बाद में उन key columns पर joins और groups efficiency के लिए इसी sort order का लाभ लेते हैं। (कल्पना करें कि printed telephone directory में phone number ढूँढना कितना कठिन होता यदि वह surname और फिर forename के अनुसार sorted न हो। `setkey` वास्तव में बस यही करता है: आपके द्वारा specify किए गए columns के अनुसार rows को sort करता है।) Index कोई अतिरिक्त RAM उपयोग नहीं करता। यह केवल RAM में row order बदलता है और key columns को mark करता है। यह SQL के _clustered index_ के समान है।
+
+हालांकि, केवल एक primary key हो सकती है क्योंकि data को एक समय में RAM में physically केवल एक तरीके से sort किया जा सकता है। Primary index वही चुनें जिसका उपयोग आप सबसे अधिक करते हैं (जैसे `[id,date]`)। कभी-कभी primary key के लिए कोई स्पष्ट choice नहीं होती, या आपको अलग-अलग order में कई columns join और group करने होते हैं। तब secondary index उपयोगी होता है। यह memory उपयोग करता है (`4*nrow` bytes, index में columns की संख्या चाहे जितनी हो) ताकि आपके द्वारा specify किए गए columns के अनुसार rows का order store किया जा सके, लेकिन RAM में rows को वास्तव में reorder नहीं करता। बाद के joins और groups secondary key के order का लाभ लेते हैं, लेकिन उन्हें उस index के माध्यम से _hop_ करना पड़ता है, इसलिए वे primary indexes जितने efficient नहीं होते। फिर भी यह full vector scan से बहुत तेज़ है। Secondary indexes की संख्या पर कोई limit नहीं है क्योंकि प्रत्येक केवल अलग ordering vector है। सामान्यतः आपको secondary indexes स्वयं बनाने की आवश्यकता नहीं होती। data.table को सामान्य तरीके से उपयोग करने पर वे automatically बनते और उपयोग होते हैं; उदाहरण के लिए `DT[someCol == someVal, ]` और `DT[someCol %in% someVals, ]` secondary index बनाएँगे, attach करेंगे और फिर उपयोग करेंगे। यह data.table में vector scan से तेज़ है, इसलिए automatic indexing default रूप से on है क्योंकि इसकी कोई up-front penalty नहीं है। Automatic indexing को off करने का option भी है; उदाहरण के लिए यदि किसी तरह बहुत सारे indexes बन रहे हों और अपेक्षाकृत छोटी अतिरिक्त memory भी बहुत अधिक हो जाए।
+
+हम _index_ और _key_ शब्दों का एक-दूसरे के स्थान पर उपयोग करते हैं।
+
+# Error messages
+## "Could not find function `DT`"
+ऊपर [यहाँ](#DTremove1) और [यहाँ](#DTremove2) देखें।
+
+## "unused argument(s) (`MySum = sum(v)`)"
+
+यह error `DT[ , MySum = sum(v)]` से generate होता है। Intended syntax `DT[ , .(MySum = sum(v))]` था, या `DT[ , j = .(MySum = sum(v))]`।
+
+## "`translateCharUTF8` must be called on a `CHARSXP`"
+यह error (और समान errors, जैसे "`getCharCE` must be called on a `CHARSXP`") character data या locale से संबंधित होना आवश्यक नहीं है। इसके बजाय यह पहले हुए memory corruption का symptom हो सकता है। अब तक ऐसे cases reproducible रहे हैं और जल्दी fix किए गए हैं। कृपया इसे हमारे [issues tracker](https://github.com/Rdatatable/data.table/issues) पर report करें।
+
+## `cbind(DT, DF)` एक अजीब format लौटाता है, जैसे `Integer,5` {#cbinderror}
+
+यह v1.6.5 से पहले होता है, और `rbind(DT, DF)` के लिए भी। कृपया v1.6.7 या बाद की version पर upgrade करें।
+
+## "cannot change value of locked binding for `.SD`"
+
+`.SD` design के अनुसार locked है। `?data.table` देखें। यदि आप `.SD` का उपयोग या return करने से पहले उसे manipulate करना चाहते हैं और `:=` का उपयोग करके `DT` को modify नहीं करना चाहते, तो पहले copy लें (देखें `?copy`), जैसे:
+
+```{r}
+DT = data.table(a = rep(1:3, 1:3), b = 1:6, c = 7:12)
+DT
+DT[ , { mySD = copy(.SD)
+ mySD[1, b := 99L]
+ mySD},
+ by = a]
+```
+
+## "cannot change value of locked binding for `.N`"
+
+कृपया v1.8.1 या बाद की version पर upgrade करें। इस version से, यदि `.N` को `j` द्वारा return किया जाता है, तो इसे `N` नाम दिया जाता है ताकि subsequent grouping में special variable `.N` और `".N"` नाम के column के बीच ambiguity न हो।
+
+पुराने behaviour को reproduce करने के लिए `.N` को जबरन `.N` नाम दिया जा सकता है, इस तरह:
+```{r}
+DT = data.table(a = c(1,1,2,2,2), b = c(1,2,2,2,1))
+DT
+DT[ , list(.N = .N), list(a, b)] # show intermediate result for exposition
+cat(try(
+ DT[ , list(.N = .N), by = list(a, b)][ , unique(.N), by = a] # compound query more typical
+, silent = TRUE))
+```
+
+यदि आप पहले से v1.8.1 या बाद की version चला रहे हैं, तो error message अब "cannot change value of locked binding" error से अधिक helpful है, जैसा ऊपर देखा जा सकता है, क्योंकि यह vignette v1.8.1 या बाद की version का उपयोग करके बनाई गई थी।
+
+अब अधिक natural syntax काम करता है:
+```{r}
+if (packageVersion("data.table") >= "1.8.1") {
+ DT[ , .N, by = list(a, b)][ , unique(N), by = a]
+ }
+if (packageVersion("data.table") >= "1.9.3") {
+ DT[ , .N, by = .(a, b)][ , unique(N), by = a] # same
+}
+```
+
+# Warning messages
+## "The following object(s) are masked from `package:base`: `cbind`, `rbind`"
+
+यह warning केवल v1.6.5 और v1.6.6 में package load करते समय मौजूद थी। उद्देश्य `cbind(DT, DF)` को काम करने देना था, लेकिन बाद में पता चला कि इससे package `IRanges` के साथ पूर्ण compatibility टूट गई। कृपया v1.6.7 या बाद की version पर upgrade करें।
+
+## "Coerced numeric RHS to integer to match the column's type"
+
+उम्मीद है यह अपने आप में स्पष्ट है। पूरा message है:
+
+Coerced numeric RHS to integer to match the column's type; may have truncated precision. Either change the column to numeric first by creating a new numeric vector length 5 (nrows of entire table) yourself and assigning that (i.e. 'replace' column), or coerce RHS to integer yourself (e.g. 1L or as.integer) to make your intent clear (and for speed). Or, set the column type correctly up front when you create the table and stick to it, please.
+
+
+इसे generate करने के लिए यह आज़माएँ:
+
+```{r}
+DT = data.table(a = 1:5, b = 1:5)
+suppressWarnings(
+DT[2, b := 6] # works (slower) with warning
+)
+class(6) # numeric not integer
+DT[2, b := 7L] # works (faster) without warning
+class(7L) # L makes it an integer
+DT[ , b := rnorm(5)] # 'replace' integer column with a numeric column
+```
+
+## RDS या RData file से data.table पढ़ना
+
+`*.RDS` और `*.RData` ऐसे file types हैं जो in-memory R objects को disk पर efficiently store कर सकते हैं। हालांकि `data.table` को binary file में store करने पर उसका column over-allocation खो जाता है (यह भी देखें `?truelength`)। यह बड़ी समस्या नहीं है—अगले _by reference_ operation पर आपका `data.table` memory में copy होगा और warning देगा।
+इसलिए `readRDS()` या `load()` calls से load किए गए प्रत्येक `data.table` पर `setDT()` call करने की recommendation है ताकि उसके internal attributes restore हो जाएँ। यदि आपको केवल नए columns के लिए space pre-allocate करना हो, तो `setalloccol()` का भी उपयोग किया जा सकता है।
+
+अधिक जानकारी के लिए `?setDT` और `?truelength` देखें।
+
+# Package के बारे में सामान्य प्रश्न
+
+## CRAN archive में v1.3 missing दिखाई देता है?
+यह सही है। v1.3 केवल R-Forge पर उपलब्ध था। Internally कई बड़े changes हुए थे और development में उन्हें test करने में कुछ समय लगा।
+
+## क्या data.table S-plus के साथ compatible है?
+
+अभी नहीं।
+
+ - Package के कुछ core parts C में लिखे गए हैं और internal R functions तथा R structures का उपयोग करते हैं।
+ - Package lexical scoping का उपयोग करता है, जो R और **S-plus** के बीच differences में से एक है, जैसा [R FAQ 3.3.1](https://cran.r-project.org/doc/FAQ/R-FAQ.html#Lexical-scoping) में समझाया गया है।
+
+## क्या यह Linux, Mac और Windows के लिए उपलब्ध है?
+हाँ, सभी platforms पर 32-bit और 64-bit दोनों के लिए। CRAN का धन्यवाद। कोई special या OS-specific libraries उपयोग नहीं की जातीं।
+
+## मुझे यह बहुत अच्छा लगता है। मैं क्या कर सकता हूँ?
+कृपया suggestions, bug reports और enhancement requests हमारे [issues tracker](https://github.com/Rdatatable/data.table/issues) पर दर्ज करें। इससे package बेहतर बनाने में मदद मिलती है।
+
+कृपया [GitHub](https://github.com/Rdatatable/data.table) पर package को star भी करें। इससे developers को प्रोत्साहन मिलता है और अन्य R users को package खोजने में मदद मिलती है।
+
+आप code और/या documentation को स्वयं बदलने के लिए pull requests submit कर सकते हैं; हमारी [Contribution Guidelines](https://github.com/Rdatatable/data.table/blob/master/.github/CONTRIBUTING.md) देखें।
+
+## मुझे यह अच्छा नहीं लगता। मैं अपने अनुभव के बारे में दूसरों को कैसे चेतावनी दूँ?
+
+जिन सभी articles के बारे में हमें पता है (चाहे positive हों या negative), उन्हें हम [Articles](https://github.com/Rdatatable/data.table/wiki/Articles) page पर जोड़ते हैं। GitHub पर project के wiki के सभी pages open-access हैं और modify करने पर restrictions नहीं हैं। आप स्वयं article लिख सकते हैं, किसी और द्वारा लिखे गए negative article का link जोड़ सकते हैं, या अपनी criticisms collect करने के लिए हमारे wiki में नया page जोड़ सकते हैं। कृपया इसे constructive रखें ताकि हमें सुधार करने का अवसर मिले।
+
+## मेरा एक प्रश्न है। मुझे पता है कि r-help posting guide maintainer से संपर्क करने को कहती है (r-help से नहीं), लेकिन क्या कोई बड़ा group है जिससे मैं पूछ सकता हूँ?
+Project के homepage पर [support guide](https://github.com/Rdatatable/data.table/wiki/Support) देखें, जिसमें up-to-date links दिए गए हैं।
+
+## datatable-help archives कहाँ हैं?
+[Homepage](https://github.com/Rdatatable/data.table/wiki) पर कई formats में archives के links हैं।
+
+## मैं Issues page पर post नहीं करना चाहता; क्या मैं केवल एक या दो लोगों को privately mail कर सकता हूँ?
+हाँ। हालांकि Issues page या Stack Overflow से आपको अधिक तेज़ उत्तर मिलने की संभावना है। इसके अलावा, उन जगहों पर public रूप से प्रश्न पूछने से general knowledge base बनाने में मदद मिलती है।
+
+## मैंने एक package बनाया है जो data.table का उपयोग करता है। मैं कैसे सुनिश्चित करूँ कि मेरा package data.table-aware हो ताकि `data.frame` से inheritance ठीक से काम करे?
+
+कृपया [यह उत्तर](https://stackoverflow.com/a/10529888/403310) देखें।
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
diff --git a/vignettes/hi/datatable-fread-and-fwrite.Rmd b/vignettes/hi/datatable-fread-and-fwrite.Rmd
new file mode 100644
index 0000000000..173a971a30
--- /dev/null
+++ b/vignettes/hi/datatable-fread-and-fwrite.Rmd
@@ -0,0 +1,302 @@
+---
+title: "तेज़ Read और तेज़ Write"
+date: "`{r} Sys.Date()`"
+output: litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Fast Read and Fast Write}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+require(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+`data.table` R package के `fread()` और `fwrite()` functions केवल बड़ी files पर speed के लिए optimized नहीं हैं, बल्कि छोटे datasets के साथ काम करने के लिए भी शक्तिशाली और सुविधाजनक features देते हैं। यह विगनेट efficient data import और export के लिए उनकी usability, flexibility और performance को highlight करता है।
+
+***
+
+## 1. fread()
+
+### **1.1 Command line tools का सीधे उपयोग**
+`data.table` का `fread()` function shell commands से pipe किए गए data को पढ़ सकता है, जिससे data के R में आने से पहले ही उसे filter या preprocess किया जा सकता है।
+
+```{r}
+# Create a sample file with some unwanted lines
+writeLines(
+'HEADER: Some metadata
+HEADER: More metadata
+1 2.0 3.0
+2 4.5 6.7
+HEADER: Yet more
+3 8.9 0.1
+4 1.2 3.4',
+"example_data.txt")
+
+library(data.table)
+fread("grep -v HEADER example_data.txt")
+```
+
+`-v` option, `grep` को उन सभी lines को लौटाने के लिए कहता है जिनमें string 'HEADER' नहीं है।
+
+> "वर्षों से बहुत से उच्च-गुणवत्ता वाले engineers ने command tool grep को देखा और सुधार किया है, इसलिए बहुत संभावना है कि यह जितना तेज़ हो सकता है उतना ही तेज़ है, साथ ही सही, सुविधाजनक, online अच्छी तरह documented, सीखने में आसान और specific tasks के solutions खोजने में सरल है। यदि आपको अधिक complex string filtering करनी हो (जैसे lines की शुरुआत या अंत में strings match करना), तो grep syntax बहुत शक्तिशाली है। इसका syntax सीखना अन्य languages और environments में भी उपयोगी transferable skill है।"
+>
+> — Matt Dowle
+
+अधिक detail के लिए यह [example](https://stackoverflow.com/questions/36256706/fread-together-with-grepl/36270543#36270543) देखें।
+
+Windows पर `grep` जैसे command-line tools Rtools, Cygwin, या Windows Subsystem for Linux (WSL) जैसे environments के माध्यम से उपलब्ध होते हैं। Linux और macOS पर ये tools सामान्यतः operating system में ही शामिल होते हैं।
+
+#### 1.1.1 Text string से सीधे पढ़ना
+
+`fread()` R में `text` argument का उपयोग करके character string से सीधे data पढ़ सकता है। यह reproducible examples बनाने, code snippets test करने, या R session के भीतर programmatically generated data के साथ काम करने के लिए विशेष रूप से उपयोगी है। String की प्रत्येक line को newline character `\n` से अलग होना चाहिए।
+
+```{r}
+my_data_string = "colA,colB,colC\n1,apple,TRUE\n2,banana,FALSE\n3,orange,TRUE"
+dt_from_text = fread(text = my_data_string)
+print(dt_from_text)
+```
+
+#### 1.1.2 URLs से पढ़ना
+
+`fread()` किसी web URL को character string के रूप में उसके `file` argument को देकर सीधे data पढ़ सकता है।
+इससे internet से data download और read एक ही step में किया जा सकता है।
+
+ ```{r}
+ # dt = fread("https://people.sc.fsu.edu/~jburkardt/data/csv/airtravel.csv")
+ # print(dt)
+ ```
+
+#### 1.1.3 Compressed files का automatic decompression
+
+कई cases में `fread()` सामान्य compression extensions वाली files को explicit connection object या shell commands के बिना सीधे automatically detect और decompress कर सकता है। यह file extension check करके काम करता है।
+
+**आमतौर पर supported extensions में शामिल हैं:**
+- `.gz` / `.bz2` (gzip / bzip2): Supported और सीधे काम करते हैं।
+- `.zip` / `.tar` (ZIP / tar archives, single file): Supported—यदि archive में केवल एक file है तो `fread()` पहली file पढ़ेगा।
+
+**ध्यान दें**: यदि archive में multiple files हों, तो `fread()` error के साथ fail होगा।
+
+### 1.2 Automatic separator और skip detection
+
+`fread` delimiter और header detection को automate करता है, इसलिए अधिकांश cases में इन्हें manually specify करने की आवश्यकता नहीं होती। आप केवल filename देते हैं—`fread` structure को intelligently detect करता है:
+
+**Separator Detection**
+
+`fread` common separators (`,`,`\t`, `|`, space, `:`, `;`) test करता है और sampled rows में सबसे consistent number of fields देने वाले separator को चुनता है। Non-standard delimiters के लिए `sep=` parameter से इसे override किया जा सकता है।
+
+**Header Detection**
+
+किसी भी `skip` या `nrows` settings (यदि दी गई हों) apply करने के बाद consistent number of fields वाली पहली row examine की जाती है:
+
+यदि इस line के सभी fields character के रूप में interpret किए जा सकते हैं और values data row जैसी strongly दिखाई नहीं देतीं (उदाहरण के लिए पूरी तरह numeric-looking strings वाली row को फिर भी data माना जा सकता है), तो सामान्यतः उसे header (column names) के रूप में उपयोग किया जाता है।
+
+**ध्यान दें:** `keepLeadingZeros = TRUE` set करने से यह logic प्रभावित हो सकती है। क्योंकि इससे leading zeros वाले numeric columns को `character` माना जाता है, `fread` किसी data row को गलती से header row समझ सकता है। ऐसे cases में explicitly `header = FALSE` set करने की recommendation है।
+
+अन्यथा (जैसे line में detected numeric types हों, या character strings numbers जैसी strongly दिखती हों और data हो सकती हों), उसे data row माना जाता है और default column names (`V1`, `V2`, …) assign किए जाते हैं।
+
+आप `header = TRUE` या `header = FALSE` से `fread` को explicitly बता सकते हैं कि header मौजूद है या नहीं।
+
+**Skip Detection**
+
+Default रूप से (`skip="auto"`), `fread` data header से पहले blank lines और comment lines (जैसे `#` से शुरू होने वाली) automatically skip करेगा।
+Manually अलग number of lines skip करने के लिए उपयोग करें
+
+* पहले `n` lines skip करने के लिए `skip=n`।
+* किसी substring वाली line खोजने के लिए `skip="string"` (आमतौर पर column names से, जैसे `skip="Date"`)। Reading पहली matching line से शुरू होती है। Metadata skip करने या multi-table files में sub-tables select करने के लिए यह उपयोगी है। यह feature `gdata` package के `read.xls` function से inspired है।
+
+### 1.3 High-Quality Automatic Column Type Detection
+
+कई real-world datasets में ऐसे columns होते हैं जो शुरुआत में blank, zero-filled, या numeric दिखाई देते हैं लेकिन बाद में characters रखते हैं। ऐसी inconsistencies को संभालने के लिए `fread()` robust column-type detection strategy उपयोग करता है।
+
+v1.10.5 से `fread()` file के कई equally spaced points—start, middle और end सहित—से contiguous rows के blocks पढ़कर sample करता है। Sample की कुल rows file size और structure के आधार पर dynamically चुनी जाती हैं और आमतौर पर लगभग 10,000 होती हैं, लेकिन कम या थोड़ी अधिक भी हो सकती हैं। यह wide sampling data में बाद में होने वाले type changes (जैसे `001` से `0A0`, या blanks का populated होना) detect करने में मदद करती है।
+
+**mmap के साथ Efficient File Access**
+
+इस sampling को efficiently implement करने के लिए `fread()` operating system की memory-mapped file access (`mmap`) उपयोग करता है, जिससे sequential scanning के बिना file में arbitrary positions पर jump किया जा सकता है। यह lazy, on-demand strategy बहुत बड़ी files पर भी sampling को लगभग instantaneous बनाती है।
+
+यदि jump quoted field के भीतर land करता है जिसमें newlines हों, तो `fread()` बाद की lines test करता है जब तक expected number of fields वाली 5 consecutive rows न मिल जाएँ, जिससे complex files में भी correct parsing सुनिश्चित होती है।
+
+**Accurate और Optimized Type Detection**
+
+प्रत्येक column का type नीचे दी ordered list से आवश्यक minimum type के आधार पर infer किया जाता है:
+
+`logical` < `integer` < `integer64` < `double` < `character`
+
+इससे सुनिश्चित होता है:
+
+- सही type के साथ memory की एक ही बार up-front allocation
+- File को दोबारा पढ़ने या manually `colClasses` set करने से बचाव
+- बेहतर speed और memory efficiency
+
+**Out-of-Sample Type Exceptions**
+
+यदि type change sampled rows के बाहर होता है, तो `fread()` उसे automatically detect करता है और सही type assignment सुनिश्चित करने के लिए user intervention के बिना file दोबारा पढ़ता है। उदाहरण के लिए integer के रूप में sampled column में बाद में `00A` हो सकता है—जिससे character के रूप में automatic reread trigger होगा।
+
+जब `verbose=TRUE` enabled हो, तो सभी detection logic और rereads का detail दिया जाता है।
+
+### 1.4 End-of-File पर Early Error Detection
+
+क्योंकि बड़ा sample file के बिल्कुल end को explicitly शामिल करता है, इसलिए inconsistent number of columns, malformed footer, या matching closing quote के बिना opening quote जैसे critical issues लगभग तुरंत detect और report किए जा सकते हैं।
+यह early error detection पूरी file process करने या excessive memory allocate करने की unnecessary overhead से बचाता है, केवल final step पर failure मिलने की स्थिति से पहले ही।
+विशेष रूप से large datasets के साथ काम करते समय यह faster feedback और अधिक efficient resource usage सुनिश्चित करता है।
+
+### 1.5 `integer64` Support
+
+Default रूप से `fread`, 231 से बड़े integers detect करके full precision बनाए रखने के लिए उन्हें `bit64::integer64` के रूप में पढ़ता है। इस behaviour को तीन तरीकों से override किया जा सकता है:
+
+- Per-column: Individual columns का type specify करने के लिए `colClasses` argument उपयोग करें।
+
+- Per-call: सभी detected `integer64` columns कैसे read हों, यह set करने के लिए `fread()` में `integer64` argument उपयोग करें।
+
+- Globally: सभी fread calls के default behaviour को बदलने के लिए अपने R session या `.Rprofile` file में option `datatable.integer64` set करें।
+
+`integer64` argument (और corresponding option) निम्न values स्वीकार करता है:
+
+- `"integer64"` (default): Large integers को full precision के साथ `bit64::integer64` के रूप में पढ़ता है।
+
+- `"double"` या `"numeric"`: Large integers को double-precision numbers के रूप में पढ़ता है, जिससे precision silently lose हो सकती है (base R के `utils::read.csv` जैसा)।
+
+- `"character"`: Large integers को character strings के रूप में पढ़ता है।
+
+Global default check या set करने के लिए उपयोग करें:
+
+```{r}
+# fread's default behavior is to treat large integers as "integer64"; however, this global setting can be changed:
+options(datatable.integer64 = "double") # Example: set globally to "double"
+getOption("datatable.integer64")
+```
+
+### 1.6 Name या Position से Columns Drop या Select करना
+
+Memory बचाने और performance सुधारने के लिए `fread()` के `select` या `drop` arguments का उपयोग करके केवल आवश्यक columns पढ़ें।
+
+- यदि केवल कुछ columns चाहिए, तो `select` उपयोग करें।
+- यदि केवल कुछ columns exclude करने हैं, तो `drop` उपयोग करें—इससे उन सभी columns की list नहीं देनी पड़ेगी जिन्हें रखना है।
+
+मुख्य बातें:
+- `select`: रखने वाले column names/positions का vector (बाकी discard होते हैं)।
+- `drop`: discard करने वाले column names/positions का vector (बाकी रहते हैं)।
+- `select` और `drop` को एक साथ उपयोग न करें—दोनों mutually exclusive हैं।
+- यदि specified column file में missing हो तो `fread()` warning देगा।
+
+Details के लिए R में `?fread` चलाकर manual page देखें।
+
+### 1.7 Automatic Quote Escape Detection (No-Escape सहित)
+
+`fread` automatically detect करता है कि quotes कैसे escaped हैं—including doubled ("") या backslash-escaped (\") quotes—बिना user input की आवश्यकता के। यह data के बड़े sample (point 3 देखें) से निर्धारित होता है और पूरी file के विरुद्ध validate किया जाता है।
+
+Supported Scenarios:
+- Quoted fields के अंदर unescaped quotes
+उदाहरण: `"This "quote" is invalid, but fread works anyway"` — जब तक column count consistent रहता है, supported है:
+
+```{r}
+data.table::fread(text='x,y\n"This "quote" is invalid, but fread works anyway",1')
+```
+
+- Quotes से शुरू होने वाले unquoted fields
+उदाहरण: `Invalid"Field,10,20` — correctly पहचाना जाता है कि यह quoted field नहीं है।
+
+```{r}
+data.table::fread(text='x,y\nNot"Valid,1')
+```
+
+Requirements और Limitations:
+- Escaping rules और column counts पूरी file में consistent होने चाहिए।
+
+- `fill=TRUE` होने पर supported नहीं—उस case में file को RFC4180-compliant quoting/escaping follow करना होगा।
+
+Version-Specific Robustness:
+v1.10.6 से `fread` पूरी file में full-column-count consistency (default `fill=FALSE`) का उपयोग करके ambiguities अधिक reliably resolve करता है। Ambiguity के कारण parsing fail होने पर warnings जारी होती हैं।
+
+## 2. fwrite()
+
+`fwrite()`, `fread()` का fast file-writer companion है।
+यह speed, sensible defaults और ease of use के लिए design किया गया है, और `fread` में मिलने वाली कई सुविधाओं को mirror करता है।
+
+### 2.1 Intelligent और Minimalist Quoting (`quote="auto"`)
+
+जब data strings के रूप में लिखा जाता है (चाहे inherently, जैसे character columns, या choice से, जैसे `dateTimeAs="ISO"`), तो `quote="auto"` (default) intelligently fields quote करता है:
+
+**Contextual Quoting**: Fields केवल आवश्यकता होने पर quote होते हैं। ऐसा तब होता है जब उनमें delimiter `(sep)`, double quote `(")`, newline `(\n)`, carriage return `(\r)` हो, या field empty string `("")` हो। Empty string को quote करने से file पढ़ते समय उसे NA value से अलग पहचाना जा सकता है।
+
+**Direct Numeric Output में Bypass**: यदि specific columns अपने underlying numeric types में लिखे जाते हैं (जैसे `POSIXct` के लिए `dateTimeAs="epoch"`, या user ने Date को पहले integer में convert कर दिया हो), तो उन numeric fields पर quoting logic naturally bypass हो जाती है, जिससे efficiency बढ़ती है।
+
+```{r}
+dt_quoting_scenario = data.table(
+ text_field = c("Contains,a,comma", "Contains \"a quote\"", "Clean_text", "", NA),
+ numeric_field = 1:5
+)
+temp_quote_adv = tempfile(fileext = ".csv")
+
+fwrite(dt_quoting_scenario, temp_quote_adv)
+# Note the output: the empty string is quoted (""), but the NA is not.
+cat(readLines(temp_quote_adv), sep = "\n")
+```
+
+### 2.2 Fine-Grained Date/Time Serialization (`dateTimeAs` argument)
+
+POSIXct/Date types के लिए precise control देता है:
+
+- `dateTimeAs="ISO"` (POSIXct के लिए Default): ISO 8601 format (जैसे YYYY-MM-DDTHH:MM:SS.ffffffZ), जो unambiguous interchange के लिए sub-second precision सुरक्षित रखता है।
+
+- `dateTimeAs="epoch"`: POSIXct को epoch से seconds (numeric) के रूप में लिखता है।
+
+```{r}
+dt_timestamps = data.table(
+ ts = as.POSIXct("2023-10-26 14:35:45.123456", tz = "GMT"),
+ dt = as.Date("2023-11-15")
+)
+temp_dt_iso = tempfile(fileext = ".csv")
+fwrite(dt_timestamps, temp_dt_iso, dateTimeAs = "ISO")
+cat(readLines(temp_dt_iso), sep = "\n")
+unlink(temp_dt_iso)
+```
+
+### 2.3 `bit64::integer64` को संभालना
+
+**Large Integers के लिए Full Precision**: `fwrite`, `bit64::integer64` columns को full precision वाली strings में convert करके लिखता है। इससे data loss या silent double conversion से बचाव होता है जो कम specialized writers में हो सकता है। यह IDs या measurements के लिए critical है जिन्हें R के standard `32-bit` integer range या `53-bit` double precision से अधिक चाहिए।
+
+**Direct Handling**: Specialized numerics की यह direct और careful handling unnecessary intermediate conversions के बिना data integrity और efficient I/O सुनिश्चित करती है।
+
+```{r}
+if (requireNamespace("bit64", quietly = TRUE)) {
+ dt_i64 = data.table(uid = bit64::as.integer64("1234567890123456789"), val = 100)
+ temp_i64_out = tempfile(fileext = ".csv")
+ fwrite(dt_i64, temp_i64_out)
+ cat(readLines(temp_i64_out), sep = "\n")
+ unlink(temp_i64_out)
+}
+```
+
+### 2.4 Column Order और Subset Control
+
+File में लिखे जाने वाले columns का order और subset control करने के लिए `fwrite()` call करने से पहले `data.table` subset करें। `fwrite()` में `col.names` argument logical (TRUE/FALSE) है जो यह control करता है कि header row लिखी जाए या नहीं; यह नहीं कि कौन-से columns लिखे जाएँ।
+
+```{r}
+dt = data.table(A = 1:3, B = 4:6, C = 7:9)
+
+# Write only columns C and A, in that order
+fwrite(dt[, .(C, A)], "out.csv")
+cat(readLines("out.csv"), sep = "\n")
+file.remove("out.csv")
+```
+
+## 3. Performance पर एक Note
+
+यद्यपि यह विगनेट features और usability पर केंद्रित है, `fread` और `fwrite` का primary motivation speed है।
+
+Detailed और up-to-date performance comparisons में रुचि रखने वाले users के लिए हम ये external blog posts recommend करते हैं, जो rigorous analysis के लिए `atime` package उपयोग करते हैं:
+
+- **[data.table asymptotic timings](https://tdhock.github.io/blog/2023/dt-atime-figures/)**: `fread` और `fwrite` performance की तुलना `readr` और `arrow` जैसे अन्य popular R packages से करता है।
+- **[Benchmarking data.table with polars, duckdb, and pandas](https://tdhock.github.io/blog/2024/pandas-dt/)**: `data.table` I/O और grouping performance की तुलना leading Python libraries से करता है।
+
+ये benchmarks लगातार दिखाते हैं कि `fread` और `fwrite`, R ecosystem में performance के लिए अत्यंत competitive हैं और अक्सर state-of-the-art स्तर पर होते हैं।
+
+***
diff --git a/vignettes/hi/datatable-importing.Rmd b/vignettes/hi/datatable-importing.Rmd
new file mode 100644
index 0000000000..e3deec90a7
--- /dev/null
+++ b/vignettes/hi/datatable-importing.Rmd
@@ -0,0 +1,290 @@
+---
+title: "data.table को Import करना"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Importing data.table}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo = FALSE, message = FALSE}
+litedown::reactor(comment = "# ")
+.old.th = data.table::setDTthreads(1)
+```
+
+
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+यह दस्तावेज़ अन्य R packages में `data.table` को dependency के रूप में उपयोग करने पर केंद्रित है। यदि आप किसी non-R application से `data.table` के C code का उपयोग करना चाहते हैं, या उसके C functions को सीधे call करना चाहते हैं, तो इस विगनेट के [अंतिम section](#non-r-api) पर जाएँ।
+
+`data.table` को import करना अन्य R packages को import करने से अलग नहीं है। यह विगनेट इस विषय से जुड़े सबसे सामान्य प्रश्नों का उत्तर देने के लिए है; यहाँ दिए गए lessons अन्य R packages पर भी लागू किए जा सकते हैं।
+
+## `data.table` को import क्यों करें
+
+`data.table` की सबसे बड़ी features में से एक उसका concise syntax है, जो exploratory analysis को तेज़ और लिखने/समझने में आसान बनाता है; यही सुविधा package authors को `data.table` उपयोग करने के लिए प्रेरित कर सकती है। दूसरा, और शायद अधिक महत्वपूर्ण कारण, high performance है। जब आप अपने package के heavy computing tasks `data.table` को सौंपते हैं, तो आमतौर पर बिना उन numerical optimization tricks को स्वयं फिर से विकसित किए top performance मिलती है।
+
+## `data.table` को import करना आसान है
+
+`data.table` को dependency के रूप में उपयोग करना बहुत आसान है क्योंकि `data.table` की अपनी कोई dependency नहीं है। यह operating system और R दोनों dependencies पर लागू होता है। इसका अर्थ है कि यदि आपकी machine पर R installed है, तो `data.table` install करने के लिए आवश्यक सब कुछ पहले से मौजूद है। इसका यह भी अर्थ है कि आपके package में `data.table` dependency जोड़ने से अन्य recursive dependencies की chain install नहीं करनी पड़ेगी, इसलिए offline installation के लिए भी यह बहुत सुविधाजनक है।
+
+## `DESCRIPTION` file {#DESCRIPTION}
+
+Package में dependency define करने का पहला स्थान `DESCRIPTION` file है। सामान्यतः आपको `Imports:` field के अंतर्गत `data.table` जोड़ना होगा। ऐसा करने पर आपके package के compile/install होने से पहले `data.table` का install होना आवश्यक होगा। जैसा ऊपर बताया गया, कोई अन्य package install नहीं होगा क्योंकि `data.table` की अपनी dependencies नहीं हैं। आप dependency का minimum required version भी specify कर सकते हैं; उदाहरण के लिए यदि आपका package `fwrite` function उपयोग करता है, जिसे `data.table` version 1.9.8 में introduce किया गया था, तो आपको `Imports: data.table (>= 1.9.8)` लिखना चाहिए। इससे सुनिश्चित होगा कि users आपके package को install कर सकें उससे पहले installed `data.table` version 1.9.8 या बाद का हो। `Imports:` field के अलावा आप `Depends: data.table` भी उपयोग कर सकते हैं, लेकिन हम इस approach को strongly discourage करते हैं (और भविष्य में disallow भी कर सकते हैं), क्योंकि इससे `data.table` user के workspace में load हो जाता है; यानी user ने माँगा न हो तब भी उनकी scripts में `data.table` functionality enable हो जाती है। User पर `data.table` थोपे बिना package के भीतर इसका उपयोग करने का उचित तरीका `Imports:` है। वास्तव में हमें उम्मीद है कि R में `Depends:` field अंततः deprecated हो, क्योंकि यही बात सभी packages पर लागू होती है।
+
+## `NAMESPACE` file {#NAMESPACE}
+
+अगला कदम यह define करना है कि आपका package `data.table` की कौन-सी content उपयोग करता है। यह `NAMESPACE` file में किया जाना चाहिए। सामान्यतः package authors `import(data.table)` उपयोग करना चाहेंगे, जो `data.table` से सभी exported functions (अर्थात `data.table` की अपनी `NAMESPACE` file में listed functions) import करेगा।
+
+आप केवल `data.table` functions का subset भी उपयोग करना चाह सकते हैं; उदाहरण के लिए कुछ packages केवल `data.table` के high-performance CSV reader और writer का उपयोग कर सकते हैं, जिसके लिए `NAMESPACE` file में `importFrom(data.table, fread, fwrite)` जोड़ सकते हैं। किसी package के सभी functions को _कुछ specific functions को छोड़कर_ import करना भी संभव है, जैसे `import(data.table, except=c(fread, fwrite))`।
+
+[“undefined globals” वाले section](#globals) में `data.table` की non-standard evaluation से संबंधित note भी अवश्य पढ़ें।
+
+## उपयोग
+
+उदाहरण के लिए हम `a.pkg` package में दो functions define करेंगे जो `data.table` उपयोग करते हैं। एक function, `gen`, सरल `data.table` generate करेगा; दूसरा, `aggr`, उसका simple aggregation करेगा।
+
+```r
+gen = function (n = 100L) {
+ dt = as.data.table(list(id = seq_len(n)))
+ dt[, grp := ((id - 1) %% 26) + 1
+ ][, grp := letters[grp]
+ ][]
+}
+aggr = function (x) {
+ stopifnot(
+ is.data.table(x),
+ "grp" %in% names(x)
+ )
+ x[, .N, by = grp]
+}
+```
+
+## Testing
+
+अपने package में tests अवश्य शामिल करें। `data.table` की प्रत्येक major release से पहले हम reverse dependencies check करते हैं। इसका अर्थ है कि यदि `data.table` में कोई change आपके code को तोड़ता है, तो हम breaking change पकड़कर नई version release करने से पहले आपको सूचित कर सकते हैं। यह निश्चित रूप से इस assumption पर आधारित है कि आप अपना package CRAN या Bioconductor पर publish करेंगे। सबसे basic test आपके package directory में plaintext R script `tests/test.R` हो सकती है:
+
+```r
+library(a.pkg)
+dt = gen()
+stopifnot(nrow(dt) == 100)
+dt2 = aggr(dt)
+stopifnot(nrow(dt2) < 100)
+```
+
+Package test करते समय आप `R CMD check --no-stop-on-test-error` उपयोग करना चाह सकते हैं, जो error आने के बाद भी जारी रहेगा और आपके सभी tests चलाएगा (पहली failed script line पर रुकने के बजाय)।
+
+## `testthat` का उपयोग करके Testing
+
+Tests के लिए `testthat` package का उपयोग बहुत सामान्य है। `data.table` import करने वाले package को test करना अन्य packages को test करने से अलग नहीं है। Example test script `tests/testthat/test-pkg.R`:
+
+```r
+context("pkg tests")
+
+test_that("generate dt", { expect_true(nrow(gen()) == 100) })
+test_that("aggregate dt", { expect_true(nrow(aggr(gen())) < 100) })
+```
+
+यदि `data.table` Suggests में है (लेकिन Imports में नहीं), तो `testthat::test_package` या `testthat::test_check` से testing करते समय "object not found" errors से बचने के लिए R/* files में से किसी एक में `.datatable.aware=TRUE` declare करना होगा।
+
+## "Undefined global functions or variables" से निपटना {#globals}
+
+R की deferred evaluation का `data.table` द्वारा उपयोग (विशेष रूप से `:=` के left-hand side पर) `R CMD check` द्वारा अच्छी तरह पहचाना नहीं जाता। इससे package check के दौरान नीचे जैसे `NOTE`s आते हैं:
+
+```
+* checking R code for possible problems ... NOTE
+aggr: no visible binding for global variable 'grp'
+gen: no visible binding for global variable 'grp'
+gen: no visible binding for global variable 'id'
+Undefined global functions or variables:
+grp id
+```
+
+इससे निपटने का सबसे आसान तरीका है उन variables को अपने package के भीतर पहले से define करके `NULL` set करना, और चाहें तो comment जोड़ना (जैसा नीचे `gen` के refined version में किया गया है)। जहाँ संभव हो, symbols के बजाय character vector भी उपयोग कर सकते हैं (जैसे नीचे `aggr` में):
+
+```r
+gen = function (n = 100L) {
+ id = grp = NULL # due to NSE notes in R CMD check
+ dt = as.data.table(list(id = seq_len(n)))
+ dt[, grp := ((id - 1) %% 26) + 1
+ ][, grp := letters[grp]
+ ][]
+}
+aggr = function (x) {
+ stopifnot(
+ is.data.table(x),
+ "grp" %in% names(x)
+ )
+ x[, .N, by = "grp"]
+}
+```
+
+`data.table` के special symbols (जैसे `.SD` और `.N`) और assignment operator (`:=`) का case थोड़ा अलग है (`?.N` में अधिक जानकारी और ऐसे symbols की पूरी list देखें)। भविष्य में यदि हम इनके exported value को बदलें तो किसी issue से बचने के लिए आप जिन values का उपयोग करते हैं उन्हें `data.table` namespace से import करना चाहिए। उदाहरण के लिए यदि `.N`, `.I`, और `:=` उपयोग करना है, तो minimal `NAMESPACE` इस प्रकार होगा:
+
+```r
+importFrom(data.table, .N, .I, ':=')
+```
+
+इससे बहुत सरल है केवल `import(data.table)` उपयोग करना, जो `data.table` से export किए गए किसी भी object को आपके package code में उपयोग करने की व्यापक अनुमति देगा।
+
+यदि आपको `id` और `grp` का package namespace में globally variables के रूप में registered होना ठीक लगता है, तो `?globalVariables` उपयोग कर सकते हैं। ध्यान रखें कि इन notes का code या उसकी functionality पर कोई प्रभाव नहीं पड़ता; यदि package publish नहीं करना है, तो इन्हें ignore करना चुन सकते हैं।
+
+## Options देते और उपयोग करते समय आवश्यक सावधानी
+
+R packages में customization options देने की सामान्य practice है `options(name=val)` से set करना और `getOption("name", default)` से fetch करना। Function arguments अक्सर `getOption()` call specify करते हैं ताकि user को (`?fun` या `args(fun)` से) पता हो कि किसी parameter का default कौन-सा option control करता है; उदाहरण `fun(..., verbose=getOption("datatable.verbose", FALSE))`। सभी `data.table` options `datatable.` से शुरू होते हैं ताकि अन्य packages के options से conflict न हो। User केवल `options(datatable.verbose=TRUE)` call करके verbosity चालू कर सकता है। यह सभी data.table function calls को प्रभावित करता है जब तक explicit `verbose=FALSE` न दिया गया हो; जैसे `fun(..., verbose=FALSE)`।
+
+R में option mechanism _global_ है। इसका अर्थ है कि यदि user अपने उपयोग के लिए कोई `data.table` option set करता है, तो वही setting `data.table` उपयोग करने वाले किसी भी package के अंदर के code को भी प्रभावित करती है। `datatable.verbose` जैसे option के लिए यही desired behaviour है क्योंकि उद्देश्य है जहाँ से भी `data.table` operations originate हों उन्हें trace और log करना; verbosity चालू करने से results नहीं बदलते। R का एक और unique तथा production के लिए उत्कृष्ट option `options(warn=2)` है, जो सभी warnings को errors में बदल देता है। यहाँ भी उद्देश्य है कि किसी भी package की warning छूट न जाए। 6 `datatable.print.*` options और 3 optimization options हैं जो operation results को प्रभावित नहीं करते। लेकिन एक `data.table` option ऐसा था जो result बदलता था और concern बना: `datatable.nomatch`। यह option default join को outer से inner बदलता है। [Aside: default join outer है क्योंकि outer अधिक सुरक्षित है; missing data silently drop नहीं करता; और names तथा indices द्वारा matching के base R तरीके से consistent है।] कुछ users inner join को default पसंद करते थे और हमने उनके लिए यह option दिया था। लेकिन user द्वारा यह option set करने से `data.table` उपयोग करने वाले packages के भीतर joins का behaviour अनजाने में बदल सकता था। इसलिए v1.12.4 (Oct 2019) में `datatable.nomatch` option उपयोग होने पर message print किया गया, और v1.14.2 से इसे warning के साथ ignore किया जाता है। इस concern वाला यही एकमात्र `data.table` option था।
+
+## Troubleshooting
+
+यदि data.table उपयोग करने वाला package बनाते समय कोई समस्या आए, तो पहले पुष्टि करें कि problem clean R session में R console का उपयोग करके reproducible है: `R CMD check package.name`।
+
+Developers को मिलने वाली कई common issues helper tools से संबंधित होती हैं, जो package development tasks automate करने के लिए बने हैं; जैसे R code files के metadata से `NAMESPACE` file generate करने के लिए `roxygen` का उपयोग। अन्य issues package build और check helpers से संबंधित होते हैं। दुर्भाग्य से इन helpers के कभी-कभी unintended/hidden side effects होते हैं, जो समस्या के source को अस्पष्ट कर सकते हैं। इसलिए R console से दोबारा जाँचें (command line पर R चलाएँ) और सुनिश्चित करें कि import, `DESCRIPTION` तथा `NAMESPACE` files में ऊपर दिए [instructions](#DESCRIPTION) [के अनुसार](#NAMESPACE) defined है।
+
+यदि plain R console build और check से problem reproduce नहीं होती, तो helper tools के साथ `data.table` interaction से जुड़े पुराने issues से सहायता मिल सकती है: [devtools#192](https://github.com/r-lib/devtools/issues/192) या [devtools#1472](https://github.com/r-lib/devtools/issues/1472)।
+
+## License
+
+Version 1.10.5 से `data.table` Mozilla Public License (MPL) के अंतर्गत licensed है। GPL से change के कारण पूरे [यहाँ](https://github.com/Rdatatable/data.table/pull/2456) पढ़े जा सकते हैं और MPL के बारे में Wikipedia पर [यहाँ](https://en.wikipedia.org/wiki/Mozilla_Public_License) तथा [यहाँ](https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses) अधिक पढ़ सकते हैं।
+
+## `data.table` को वैकल्पिक रूप से import करना: Suggests
+
+यदि आप `data.table` को conditionally उपयोग करना चाहते हैं, अर्थात केवल जब वह installed हो, तो `DESCRIPTION` file में `Imports: data.table` के बजाय `Suggests: data.table` उपयोग करें। Default रूप से इससे package install करते समय `data.table` install करना आवश्यक नहीं होगा। इसके लिए package code में `data.table` का conditional उपयोग भी करना होगा, जो `?requireNamespace` function से किया जाना चाहिए। नीचे का example `data.table` के fast CSV writer `?fwrite` का conditional use दिखाता है। यदि `data.table` package installed नहीं है, तो उसके बजाय बहुत धीमा base R `?write.table` function उपयोग होता है।
+
+```r
+my.write = function (x) {
+ if(requireNamespace("data.table", quietly=TRUE)) {
+ data.table::fwrite(x, "data.csv")
+ } else {
+ write.table(x, "data.csv")
+ }
+}
+```
+
+इसका थोड़ा विस्तारित version यह भी सुनिश्चित करेगा कि installed `data.table` version इतना नया हो कि उसमें `fwrite` function उपलब्ध हो:
+
+```r
+my.write = function (x) {
+ if(requireNamespace("data.table", quietly=TRUE) &&
+ utils::packageVersion("data.table") >= "1.9.8") {
+ data.table::fwrite(x, "data.csv")
+ } else {
+ write.table(x, "data.csv")
+ }
+}
+```
+
+जब किसी package को suggested dependency के रूप में उपयोग करते हैं, तो उसे `NAMESPACE` file में `import` नहीं करना चाहिए। केवल `DESCRIPTION` file में उल्लेख करें।
+Package code (R/* files) में `data.table` functions उपयोग करते समय `data.table::` prefix देना होगा क्योंकि उनमें से कोई import नहीं किया गया है।
+Package tests (जैसे tests/testthat/test* files) में `data.table` उपयोग करते समय R/* files में से किसी एक में `.datatable.aware=TRUE` declare करना होगा।
+
+## `Imports` में `data.table`, लेकिन कुछ भी imported नहीं
+
+कुछ users ([उदाहरण](https://github.com/Rdatatable/data.table/issues/2341)) अपनी `NAMESPACE` file में `importFrom` या `import` उपयोग न करना और इसके बजाय internal code में हर जगह `data.table::` qualification उपयोग करना पसंद कर सकते हैं (और निश्चित रूप से `DESCRIPTION` में `data.table` को `Imports:` के अंतर्गत रखते हुए)।
+
+इस case में un-exported function `[.data.table`, safeguard के रूप में `[.data.frame` को call करने लगेगा क्योंकि `data.table` को यह पता नहीं होता कि parent package जानता है कि वह `data.table` की query API syntax के विरुद्ध calls करने का प्रयास कर रहा है (जिससे unexpected behaviour हो सकता है क्योंकि `[.data.frame` और `[.data.table` calls की structure fundamentally अलग है, उदाहरण के लिए बाद वाले में बहुत अधिक arguments हैं)।
+
+यदि package development के लिए यही आपका preferred approach है, तो अपने R source code में कहीं भी `.datatable.aware = TRUE` define करें (export करने की आवश्यकता नहीं)। इससे `data.table` को पता चलता है कि package developer के रूप में आपने जानबूझकर code को `data.table` functionality पर निर्भर बनाया है, भले `NAMESPACE` file देखकर यह स्पष्ट न हो।
+
+`data.table` runtime पर तय करता है कि calling function `data.table` उपयोग करने के बारे में aware है या नहीं, internal `cedta` function (**C**alling **E**nvironment is **D**ata **T**able **A**ware) से। यह आपके package के `?getNamespaceImports` check करने के अलावा इस variable का अस्तित्व भी जाँचता है (अन्य चीज़ों के साथ)।
+
+## Dependencies पर अतिरिक्त जानकारी
+
+Packages dependency define करने की अधिक canonical documentation के लिए official manual देखें: [Writing R Extensions](https://cran.r-project.org/doc/manuals/r-release/R-exts.html)।
+
+## data.table C routines को Import करना
+
+कुछ internally used C routines अब [datatableAPI.h](https://github.com/Rdatatable/data.table/blob/master/inst/include/datatableAPI.h) में exported हैं, इसलिए अन्य R packages के C code में उपयोग किए जा सकते हैं। Details के लिए [`?cdt`](https://rdatatable.gitlab.io/data.table/reference/cdt.html) और usage के लिए [Writing R Extensions](https://cran.r-project.org/doc/manuals/r-release/R-exts.html) का _Linking to native routines in other packages_ section देखें।
+
+## data.table पर अपनी Depends dependency को Imports में कैसे बदलें
+
+अपने package में `data.table` की `Depends` dependency को `Imports` dependency में बदलने के लिए ये steps follow करें:
+
+### Step 0. सुनिश्चित करें कि आपका package शुरुआत में R CMD check pass कर रहा है
+
+### Step 1. DESCRIPTION file update करके data.table को Depends के बजाय Imports में रखें
+
+**पहले:**
+```dcf
+Depends:
+ R (>= 3.5.0),
+ data.table
+Imports:
+```
+
+**बाद में:**
+```dcf
+Depends:
+ R (>= 3.5.0)
+Imports:
+ data.table
+```
+
+### Step 2.1: `R CMD check` चलाएँ
+
+किसी missing import या symbol की पहचान के लिए `R CMD check` चलाएँ। यह step मदद करता है:
+
+- `data.table` के उन functions या symbols को automatically detect करने में जिन्हें explicitly import नहीं किया गया है।
+- `.N`, `.SD`, और `:=` जैसे missing special symbols को flag करने में।
+- `NAMESPACE` file में क्या जोड़ना है इस पर immediate feedback देने में।
+
+ध्यान दें: ऐसे सभी usages `R CMD check` से नहीं पकड़े जाते। विशेष रूप से `R CMD check` formulas में कुछ symbols/functions skip करता है और `parse(text = "data.table(a = 1)")` जैसी parsed expressions को पूरी तरह miss कर देगा। इन edge cases को detect करने के लिए packages को अच्छी test coverage चाहिए।
+
+### Step 2.2: NAMESPACE file modify करें
+
+`R CMD check` results के आधार पर सुनिश्चित करें कि `data.table` के सभी उपयोग किए गए functions, special symbols, S3 generics और S4 classes imported हैं।
+
+इसका अर्थ है symbols, functions और S3 generics के लिए आवश्यकतानुसार `importFrom(data.table, ...)` directives और S4 classes के लिए `importClassesFrom(data.table, ...)` directives जोड़ना। इसे सही ढंग से करने की पूरी details के लिए ‘Writing R Extensions’ देखें।
+
+#### Blanket import
+
+वैकल्पिक रूप से आप `data.table` के सभी functions एक साथ import कर सकते हैं, हालाँकि सामान्यतः इसकी recommendation नहीं की जाती:
+
+```r
+import(data.table)
+```
+
+**Blanket Imports से बचने का कारण:**
+1. **Documentation**: NAMESPACE file यह document करने का अच्छा तरीका हो सकती है कि आप किन packages पर कैसे depend करते हैं।
+2. **Conflicts से बचाव**: Blanket imports आपको subtle breakage के लिए खुला छोड़ते हैं। उदाहरण के लिए यदि आप `import(pkgA)` और `import(pkgB)` करते हैं, लेकिन बाद में pkgB भी वही function export करने लगे जो pkgA करता है, तो namespace conflicts के कारण आपका package break हो जाएगा; `R CMD check` और CRAN इसकी अनुमति नहीं देते।
+
+### Step 3: Package की R/ directory के बाहर वाली R code files update करें
+
+जब आप package को `Depends` से `Imports` में बदलते हैं, तो package load होने पर dependency automatically attach नहीं होगी। यह examples, tests, vignettes और demos के लिए महत्वपूर्ण हो सकता है, जहाँ `Imports` packages को explicitly attach करना आवश्यक है।
+
+**पहले (`Depends` के साथ):**
+```r
+# data.table functions are directly available
+library(MyPkgDependsDataTable)
+dt <- data.table(x = 1:10, y = letters[1:10])
+setDT(dt)
+result <- merge(dt, other_dt, by = "x")
+```
+
+**बाद में (`Imports` के साथ):**
+```r
+# Explicitly load data.table in user scripts or vignettes
+library(data.table)
+library(MyPkgDependsDataTable)
+dt <- data.table(x = 1:10, y = letters[1:10])
+setDT(dt)
+result <- merge(dt, other_dt, by = "x")
+```
+
+### `Imports` उपयोग करने के लाभ
+- **User-Friendliness**: `Depends` users के `search()` path को बदलता है, संभवतः उनकी इच्छा के बिना।
+- **Namespace Management**: केवल वे functions उपलब्ध रहते हैं जिन्हें आपका package explicitly import करता है, जिससे function name clashes का risk कम होता है।
+- **Cleaner Package Loading**: Package dependencies search path से attach नहीं होतीं, जिससे loading process अधिक साफ़ और संभावित रूप से तेज़ होती है।
+- **Easier Maintenance**: Upstream dependencies की APIs evolve होने पर maintenance आसान होता है। `Depends` पर बहुत अधिक निर्भरता समय के साथ conflicts और compatibility issues पैदा कर सकती है।
+
+```{r, echo = FALSE, message = FALSE}
+data.table::setDTthreads(.old.th)
+```
diff --git a/vignettes/hi/datatable-intro.Rmd b/vignettes/hi/datatable-intro.Rmd
new file mode 100644
index 0000000000..b59da7bf04
--- /dev/null
+++ b/vignettes/hi/datatable-intro.Rmd
@@ -0,0 +1,733 @@
+---
+title: "data.table का परिचय"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Introduction to data.table}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+यह विगनेट `data.table` syntax, उसके general form, rows को *subset* करने, columns पर *select और compute* करने, तथा *group द्वारा* aggregations करने का परिचय देता है। Base R की `data.frame` data structure से परिचित होना उपयोगी है, लेकिन इस विगनेट को समझने के लिए आवश्यक नहीं।
+
+***
+
+## `data.table` का उपयोग करके डेटा विश्लेषण
+
+*subset*, *group*, *update*, *join* आदि data-manipulation operations स्वाभाविक रूप से एक-दूसरे से जुड़े हैं। इन *related operations को साथ रखने* से:
+
+* अंतिम लक्ष्य प्राप्त करने के लिए operations का set चाहे जो हो, syntax *संक्षिप्त* और *consistent* रहता है।
+
+* Analysis करते समय प्रत्येक operation को उपलब्ध functions के संभावित बहुत बड़े set में से किसी specific function से map करने का cognitive burden नहीं रहता, जिससे analysis *fluidly* किया जा सकता है।
+
+* प्रत्येक operation के लिए आवश्यक data को ठीक-ठीक जानकर operations को internally और बहुत प्रभावी तरीके से *automatically* optimise किया जा सकता है, जिससे code बहुत तेज़ और memory-efficient बनता है।
+
+संक्षेप में, यदि आप *programming* और *compute* time को बहुत कम करना चाहते हैं, तो यह package आपके लिए है। `data.table` की philosophy इसे संभव बनाती है। हमारा लक्ष्य इस vignette series के माध्यम से इसे समझाना है।
+
+## डेटा {#data}
+
+इस विगनेट में हम [flights](https://github.com/arunsrinivasan/flights) package (केवल GitHub पर उपलब्ध) से प्राप्त [NYC-flights14](https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv) data उपयोग करेंगे। इसमें 2014 में New York City airports से depart हुई सभी flights का Bureau of Transportation Statistics का On-Time flights data है ([nycflights13](https://github.com/tidyverse/nycflights13) से inspired)। Data केवल Jan-Oct'14 के लिए उपलब्ध है।
+
+`data.table` के fast-and-friendly file reader `fread` का उपयोग करके `flights` को सीधे इस प्रकार load कर सकते हैं:
+
+```{r, echo = FALSE}
+options(width = 100L)
+```
+
+```{r}
+input <- if (file.exists("flights14.csv")) {
+ "flights14.csv"
+} else {
+ "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"
+}
+flights <- fread(input)
+flights
+dim(flights)
+```
+
+अतिरिक्त जानकारी: `fread` सीधे `http` और `https` URLs स्वीकार करता है, साथ ही `sed` और `awk` output जैसे operating-system commands भी। Examples के लिए `?fread` देखें।
+
+## परिचय
+
+इस विगनेट में हम
+
+1. Basics से शुरू करेंगे - `data.table` क्या है, उसका general form, rows को *subset* कैसे करें, columns पर *select और compute* कैसे करें;
+
+2. फिर group द्वारा data aggregations करना देखेंगे।
+
+## 1. Basics {#basics-1}
+
+### a) `data.table` क्या है? {#what-is-datatable-1a}
+
+`data.table` एक R package है जो `data.frame` का **enhanced version** देता है; `data.frame`, `base` R में data store करने की standard structure है। ऊपर [Data](#data) section में हमने `fread()` से `data.table` बनाना देखा, लेकिन वैकल्पिक रूप से `data.table()` function से भी बना सकते हैं। उदाहरण:
+
+```{r}
+DT = data.table(
+ ID = c("b","b","b","a","a","c"),
+ a = 1:6,
+ b = 7:12,
+ c = 13:18
+)
+DT
+class(DT$ID)
+```
+
+Existing objects को भी `setDT()` (`data.frame` और `list` structures के लिए) या `as.data.table()` (अन्य structures के लिए) से `data.table` में convert कर सकते हैं। Difference की अधिक details (जो इस विगनेट के scope से बाहर हैं) के लिए `?setDT` और `?as.data.table` देखें।
+
+#### ध्यान दें:
+
+* Row numbers को `:` के साथ print किया जाता है ताकि row number और first column visually अलग दिखें।
+
+* जब print की जाने वाली rows की संख्या global option `datatable.print.nrows` (default = `r getOption("datatable.print.nrows")`) से अधिक होती है, तो यह automatically केवल top 5 और bottom 5 rows print करता है (जैसा [Data](#data) section में देखा जा सकता है)। बड़े `data.frame` में larger tables को print-and-page होते हुए कभी बहुत लंबा इंतज़ार करना पड़ सकता है। यह restriction उसमें मदद करती है, और default number इस प्रकार query कर सकते हैं:
+
+ ```{.r}
+ getOption("datatable.print.nrows")
+ ```
+
+* Option `datatable.prettyprint.char` control करता है कि character columns print करते समय प्रत्येक entry के कितने characters दिखाई दें। Limit से अधिक entries `...` के साथ truncate होती हैं (`strtrim()` के माध्यम से)। इसे इस प्रकार set कर सकते हैं:
+
+ ```{r}
+ old = options(datatable.prettyprint.char = 5L)
+ data.table(x = 1:2, y = c("abcdefghij", "klmnopqrstuv"))
+ options(old) # reset to default
+ ```
+
+* `data.table` कभी भी *row names* set या use नहीं करता। इसका कारण [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html) विगनेट में देखेंगे।
+
+### b) General form - `data.table` किस तरह *enhanced* है? {#enhanced-1b}
+
+`data.frame` के विपरीत, `data.table` के frame के भीतर, अर्थात `[ ... ]` में, केवल rows subset और columns select करने से *काफी अधिक* किया जा सकता है (ध्यान दें: SQL से analogy या relevance के कारण `DT[...]` के अंदर expressions लिखने को हम कभी-कभी "`DT` query करना" भी कह सकते हैं)। इसे समझने के लिए पहले `data.table` syntax का *general form* देखना होगा, जैसा नीचे है:
+
+```r
+DT[i, j, by]
+
+## R: i j by
+## SQL: where | order by select | update group by
+```
+
+SQL background वाले users शायद तुरंत इस syntax से relate कर सकें।
+
+#### इसे (ज़ोर से) पढ़ने का तरीका है:
+
+`DT` लें, `i` से rows subset/reorder करें, फिर `by` से group करते हुए `j` calculate करें।
+
+पहले `i` और `j` देखते हैं - rows subset करना और columns पर operate करना।
+
+### c) `i` में rows subset करना {#subset-i-1c}
+
+#### -- June महीने में origin airport "JFK" वाली सभी flights प्राप्त करें।
+
+```{r}
+ans <- flights[origin == "JFK" & month == 6L]
+head(ans)
+```
+
+* `data.table` के frame के भीतर columns को *variables की तरह* refer किया जा सकता है, ठीक SQL या Stata की तरह। इसलिए हम `origin` और `month` को variables की तरह सीधे refer करते हैं। हर बार `flights$` prefix जोड़ने की आवश्यकता नहीं। फिर भी `flights$origin` और `flights$month` भी सही काम करेंगे।
+
+* Condition `origin == "JFK" & month == 6L` satisfy करने वाले *row indices* compute होते हैं, और क्योंकि कोई अन्य operation नहीं है, उन *row indices* से संबंधित `flights` की सभी columns को `data.table` के रूप में लौटा दिया जाता है।
+
+* `i` की condition के बाद comma आवश्यक नहीं है। लेकिन `flights[origin == "JFK" & month == 6L, ]` भी सही काम करेगा। `data.frame` में, हालाँकि, comma आवश्यक है।
+
+#### -- `flights` की पहली दो rows प्राप्त करें। {#subset-rows-integer}
+
+```{r}
+ans <- flights[1:2]
+ans
+```
+
+* इस case में कोई condition नहीं है। Row indices पहले ही `i` में दिए गए हैं। इसलिए उन *row indices* पर `flights` की सभी columns वाला `data.table` लौटता है।
+
+#### -- `flights` को पहले `origin` column के अनुसार *ascending* order में, और फिर `dest` के अनुसार *descending* order में sort करें:
+
+इसे करने के लिए R function `order()` का उपयोग कर सकते हैं।
+
+```{r}
+ans <- flights[order(origin, -dest)]
+head(ans)
+```
+
+#### `order()` internally optimised है
+
+* `data.table` के frame में `character` columns पर decreasing order में sort करने के लिए "-" का उपयोग कर सकते हैं।
+
+* इसके अतिरिक्त, `data.table` के frame के भीतर `order(...)`, speed के लिए `data.table` का internal fast radix order `forder()` उपयोग करता है। इस sort ने R के `base::order` पर इतना compelling improvement दिया कि R project ने 2016 में R 3.3.0 के लिए `data.table` algorithm को default sort के रूप में adopt किया (reference के लिए `?sort` और [R Release NEWS](https://cran.r-project.org/doc/manuals/r-release/NEWS.pdf) देखें)।
+
+`data.table` के fast order पर अधिक detail *`data.table` internals* विगनेट में देखेंगे।
+
+### d) `j` में column(s) select करना {#select-j-1d}
+
+#### -- `arr_delay` column select करें, लेकिन उसे *vector* के रूप में लौटाएँ।
+
+```{r}
+ans <- flights[, arr_delay]
+head(ans)
+```
+
+* क्योंकि `data.table` के frame में columns को variables की तरह refer किया जा सकता है, हम subset करने वाले *variable* को सीधे refer करते हैं। हमें *सभी rows* चाहिए, इसलिए `i` को छोड़ देते हैं।
+
+* यह `arr_delay` column की *सभी* rows लौटाता है।
+
+#### -- `arr_delay` column select करें, लेकिन इस बार `data.table` के रूप में लौटाएँ।
+
+```{r}
+ans <- flights[, list(arr_delay)]
+head(ans)
+```
+
+* हम *variables* (column names) को `list()` में wrap करते हैं, जिससे `data.table` return होना सुनिश्चित होता है। Single column name को `list()` में wrap न करने पर vector return होता है, जैसा [पिछले example](#select-j-1d) में देखा।
+
+* `data.table`, columns को `list()` के बजाय `.()` में wrap करने की भी अनुमति देता है। यह `list()` का *alias* है; दोनों का अर्थ समान है। आप जो पसंद करें उपयोग करें; हमने देखा है कि अधिकतर users conciseness के लिए `.()` पसंद करते हैं, इसलिए आगे हम `.()` उपयोग करेंगे।
+
+`data.table` (और `data.frame` भी) internally एक `list` है, इस शर्त के साथ कि प्रत्येक element की length समान हो और `list` में `class` attribute हो। `j` को `list` return करने की अनुमति देने से उसे बहुत efficiently `data.table` में convert करके return किया जा सकता है।
+
+#### टिप: {#tip-1}
+
+जब तक `j-expression` एक `list` लौटाती है, list का प्रत्येक element resulting `data.table` में एक column बन जाएगा। इससे `j` बहुत powerful हो जाता है, जैसा हम जल्द देखेंगे। अधिक complicated queries बनाने के लिए इसे समझना भी बहुत महत्वपूर्ण है!
+
+#### -- `arr_delay` और `dep_delay` दोनों columns select करें।
+
+```{r}
+ans <- flights[, .(arr_delay, dep_delay)]
+head(ans)
+
+## alternatively
+# ans <- flights[, list(arr_delay, dep_delay)]
+```
+
+* दोनों columns को `.()` या `list()` में wrap करें। बस इतना ही।
+
+#### -- `arr_delay` और `dep_delay` दोनों columns select करें *और* उनके नाम `delay_arr` और `delay_dep` रखें।
+
+क्योंकि `.()` केवल `list()` का alias है, columns को उसी तरह name कर सकते हैं जैसे `list` बनाते समय करते हैं।
+
+```{r}
+ans <- flights[, .(delay_arr = arr_delay, delay_dep = dep_delay)]
+head(ans)
+```
+
+### e) `j` में compute या *do* करना
+
+#### -- कितनी trips का total delay < 0 रहा है?
+
+```{r}
+ans <- flights[, sum( (arr_delay + dep_delay) < 0 )]
+ans
+```
+
+#### यहाँ क्या हो रहा है?
+
+* `data.table` का `j` केवल *columns select* करने से अधिक कर सकता है - यह *expressions*, अर्थात *columns पर computing*, handle कर सकता है। यह आश्चर्यजनक नहीं होना चाहिए, क्योंकि columns को variables की तरह refer कर सकते हैं। इसलिए उन variables पर functions call करके *compute* भी कर सकते हैं। यहाँ ठीक यही हो रहा है।
+
+### f) `i` में subset *और* `j` में operation
+
+#### -- June महीने में origin airport "JFK" वाली सभी flights के average arrival और departure delay calculate करें।
+
+```{r}
+ans <- flights[origin == "JFK" & month == 6L,
+ .(m_arr = mean(arr_delay), m_dep = mean(dep_delay))]
+ans
+```
+
+* पहले `i` में subset करके matching *row indices* ढूँढते हैं जहाँ `origin` airport `"JFK"` और `month` `6L` है। अभी हम उन rows से संबंधित _पूरे_ `data.table` को subset *नहीं* करते।
+
+* अब `j` देखते हैं और पाते हैं कि यह केवल *दो columns* उपयोग करता है। और हमें उनका `mean()` compute करना है। इसलिए matching rows के केवल उन्हीं columns को subset करके उनका `mean()` compute करते हैं।
+
+क्योंकि query के तीन main components (`i`, `j` और `by`) `[...]` के भीतर *एक साथ* हैं, `data.table` evaluation से पहले तीनों को देख सकता है और query को एक साथ optimise कर सकता है, बजाय हर component को अलग optimise करने के। इसलिए speed और memory efficiency दोनों के लिए पूरे subset से बच सकते हैं (अर्थात `arr_delay` और `dep_delay` के अलावा बाकी columns को subset करने से बचते हैं)।
+
+#### -- 2014 में June महीने में "JFK" airport से कितनी trips हुईं?
+
+```{r}
+ans <- flights[origin == "JFK" & month == 6L, length(dest)]
+ans
+```
+
+Function `length()` को input argument चाहिए। हमें केवल subset में rows की संख्या compute करनी है। `length()` के input argument के रूप में कोई अन्य column भी उपयोग कर सकते थे। यह approach SQL के `SELECT COUNT(dest) FROM flights WHERE origin = 'JFK' AND month = 6` की याद दिलाता है।
+
+इस प्रकार का operation काफी बार होता है, विशेष रूप से grouping के दौरान (जैसा अगले section में देखेंगे), इसलिए `data.table` इसके लिए *special symbol* `.N` देता है।
+
+### g) `i` में non-existing elements संभालना
+
+#### -- Non-existing elements query करने पर क्या होता है?
+
+ऐसे elements के लिए `data.table` query करते समय जो मौजूद नहीं हैं, behaviour उपयोग किए गए method के अनुसार बदलता है।
+
+```r
+setkeyv(flights, "origin")
+```
+
+* **Key-based subsetting: `dt["d"]`**
+
+ यह key column `x` पर right join करता है, जिससे `d` वाली row और not-found columns के लिए `NA` मिलता है। `setkeyv` उपयोग करने पर table specified keys के अनुसार sort होती है और internal index बनता है, जिससे efficient subsetting के लिए binary search संभव होती है।
+
+ ```r
+ flights["XYZ"]
+ # Returns:
+ # origin year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight tailnum ...
+ # 1: XYZ NA NA NA NA NA NA NA NA NA NA NA NA ...
+ ```
+
+* **Logical subsetting: `dt[x == "d"]`**
+
+ यह standard subset operation करता है जिसमें कोई matching row नहीं मिलती, इसलिए empty `data.table` लौटता है।
+
+ ```r
+ flights[origin == "XYZ"]
+ # Returns:
+ # Empty data.table (0 rows and 19 cols): year,month,day,dep_time,sched_dep_time,dep_delay,arr_time,sched_arr_time,arr_delay,...
+ ```
+
+* **`nomatch=NULL` के साथ exact match**
+
+ Non-existing elements के लिए `NA` के बिना exact matches हेतु `nomatch=NULL` उपयोग करें:
+
+ ```r
+ flights["XYZ", nomatch=NULL]
+ # Returns:
+ # Empty data.table (0 rows and 19 cols): year,month,day,dep_time,sched_dep_time,dep_delay,arr_time,sched_arr_time,arr_delay,...
+ ```
+
+इन behaviours को समझना data में non-existing elements के साथ काम करते समय confusion से बचा सकता है।
+
+#### Special symbol `.N`: {#special-N}
+
+`.N` एक special built-in variable है जो _current group_ में observations की संख्या रखता है। `by` के साथ यह विशेष रूप से उपयोगी है, जैसा अगले section में देखेंगे। Group-by operations न होने पर यह simply subset की rows की संख्या लौटाता है।
+
+अब जब यह पता है, तो `.N` का उपयोग करके वही task इस प्रकार कर सकते हैं:
+
+```{r}
+ans <- flights[origin == "JFK" & month == 6L, .N]
+ans
+```
+
+* फिर से, `i` में subset करके वे *row indices* प्राप्त करते हैं जहाँ `origin` airport *"JFK"* और `month` *6* है।
+
+* हम देखते हैं कि `j` केवल `.N` उपयोग करता है, कोई अन्य columns नहीं। इसलिए पूरा subset materialise नहीं होता। हम केवल subset की rows की संख्या लौटाते हैं (जो row indices की length है)।
+
+* ध्यान दें कि `.N` को `list()` या `.()` में wrap नहीं किया। इसलिए vector return होता है।
+
+उसी operation को `nrow(flights[origin == "JFK" & month == 6L])` से भी कर सकते थे। लेकिन इसमें पहले `i` के *row indices* से संबंधित पूरा `data.table` subset करना पड़ता और *फिर* `nrow()` से rows return होतीं, जो unnecessary और inefficient है। इसे और अन्य optimisation aspects को *`data.table` design* विगनेट में detail में cover करेंगे।
+
+### h) बहुत अच्छा! लेकिन `j` में columns को names से कैसे refer करें (`data.frame` की तरह)? {#refer-j}
+
+यदि आप column names explicitly लिख रहे हैं, तो `data.frame` की तुलना में कोई difference नहीं है (v1.9.8 से)।
+
+#### -- `arr_delay` और `dep_delay` दोनों columns को `data.frame` के तरीके से select करें।
+
+```{r j_cols_no_with}
+ans <- flights[, c("arr_delay", "dep_delay")]
+head(ans)
+```
+
+यदि desired columns character vector में stored हैं, तो दो options हैं: `..` prefix का उपयोग या `with` argument का उपयोग।
+
+#### -- `..` prefix का उपयोग करके variable में named columns select करें
+
+```{r j_cols_dot_prefix}
+select_cols = c("arr_delay", "dep_delay")
+flights[ , ..select_cols]
+```
+
+Unix terminal से परिचित users के लिए `..` prefix "up-one-level" command की याद दिला सकता है। यहाँ भी analogous behaviour है -- `..`, `data.table` को `select_cols` variable को "एक level ऊपर" खोजने का संकेत देता है, अर्थात इस case में global environment में।
+
+#### -- `with = FALSE` का उपयोग करके variable में named columns select करें
+
+```{r j_cols_with}
+flights[ , select_cols, with = FALSE]
+```
+
+Argument का नाम R function `with()` के कारण `with` रखा गया है क्योंकि functionality समान है। मान लें आपके पास `data.frame` `DF` है और वे सभी rows subset करनी हैं जहाँ `x > 1`। `base` R में आप निम्न कर सकते हैं:
+
+```{r}
+DF = data.frame(x = c(1,1,1,2,2,3,3,3), y = 1:8)
+
+## (1) normal way
+DF[DF$x > 1, ] # data.frame needs that ',' as well
+
+## (2) using with
+DF[with(DF, x > 1), ]
+```
+
+* (2) में `with()` उपयोग करने से `DF` के column `x` को variable की तरह उपयोग कर सकते हैं।
+
+ इसी कारण `data.table` में argument का नाम `with` है। `with = FALSE` set करने से columns को variables की तरह refer करने की क्षमता disable होती है, जिससे "`data.frame` mode" restore होता है।
+
+* `-` या `!` का उपयोग करके columns को *deselect* भी कर सकते हैं। उदाहरण:
+
+ ```r
+ ## not run
+
+ # returns all columns except arr_delay and dep_delay
+ ans <- flights[, !c("arr_delay", "dep_delay")]
+ # or
+ ans <- flights[, -c("arr_delay", "dep_delay")]
+ ```
+
+* `v1.9.5+` से start और end column names देकर भी select कर सकते हैं, जैसे first three columns के लिए `year:day`।
+
+ ```r
+ ## not run
+
+ # returns year,month and day
+ ans <- flights[, year:day]
+ # returns day, month and year
+ ans <- flights[, day:year]
+ # returns all columns except year, month and day
+ ans <- flights[, -(year:day)]
+ ans <- flights[, !(year:day)]
+ ```
+
+ Interactive work में यह विशेष रूप से उपयोगी है।
+
+`data.table` में `with = TRUE` default है क्योंकि `j` को expressions handle करने देने से हम बहुत अधिक कर सकते हैं - विशेष रूप से `by` के साथ, जैसा अभी देखेंगे।
+
+## 2. Aggregations
+
+पिछले section में हमने `data.table` के general form से `i` और `j` देखे। अब देखेंगे कि इन्हें `by` के साथ combine करके *group द्वारा* operations कैसे किए जाएँ। कुछ examples देखते हैं।
+
+### a) `by` का उपयोग करके grouping
+
+#### -- प्रत्येक origin airport के लिए trips की संख्या कैसे प्राप्त करें?
+
+```{r}
+ans <- flights[, .(.N), by = .(origin)]
+ans
+
+## or equivalently using a character vector in 'by'
+# ans <- flights[, .(.N), by = "origin"]
+```
+
+* हम जानते हैं कि `.N` [एक special variable है](#special-N) जो current group की rows की संख्या रखता है। `origin` से grouping करने पर प्रत्येक group के लिए rows की संख्या `.N` मिलती है।
+
+* `head(flights)` करने पर देख सकते हैं कि origin airports का order *"JFK"*, *"LGA"*, और *"EWR"* है। Grouping variables का original order result में preserve रहता है। _इसे याद रखना महत्वपूर्ण है!_
+
+* क्योंकि `j` में returned column को हमने name नहीं दिया, special symbol `.N` पहचानकर उसका नाम automatically `N` रखा गया।
+
+* `by` column names का character vector भी स्वीकार करता है। Programmatically coding के लिए यह विशेष रूप से उपयोगी है, जैसे grouping columns (एक `character` vector के रूप में) को function argument लेने वाला function design करना।
+
+* जब `j` और `by` में केवल एक column या expression refer करना हो, तो `.()` notation छोड़ी जा सकती है। यह केवल convenience के लिए है। इसके बजाय यह भी कर सकते हैं:
+
+ ```{r}
+ ans <- flights[, .N, by = origin]
+ ans
+ ```
+
+ आगे जहाँ applicable होगा, हम यह convenient form उपयोग करेंगे।
+
+#### -- Carrier code `"AA"` के लिए प्रत्येक origin airport से trips की संख्या कैसे calculate करें? {#origin-N}
+
+Unique carrier code `"AA"`, *American Airlines Inc.* को दर्शाता है।
+
+```{r}
+ans <- flights[carrier == "AA", .N, by = origin]
+ans
+```
+
+* पहले `i` से expression `carrier == "AA"` के row indices प्राप्त करते हैं।
+
+* उन *row indices* का उपयोग करके `origin` से group करते हुए rows की संख्या प्राप्त करते हैं। फिर से, यहाँ वास्तव में कोई columns materialise नहीं होते क्योंकि `j-expression` को कोई column subset करने की आवश्यकता नहीं, इसलिए यह fast और memory-efficient है।
+
+#### -- Carrier code `"AA"` के लिए प्रत्येक `origin, dest` pair की total trips कैसे प्राप्त करें? {#origin-dest-N}
+
+```{r}
+ans <- flights[carrier == "AA", .N, by = .(origin, dest)]
+head(ans)
+
+## or equivalently using a character vector in 'by'
+# ans <- flights[carrier == "AA", .N, by = c("origin", "dest")]
+```
+
+* `by` multiple columns स्वीकार करता है। Grouping के लिए सभी columns दे देते हैं। `by` में `.()` का उपयोग फिर ध्यान दें -- यह केवल `list()` का shorthand है, और `list()` भी उपयोग हो सकता है। इस विगनेट में हम `.()` ही उपयोग करेंगे।
+
+#### -- Carrier code `"AA"` के लिए प्रत्येक month में प्रत्येक `orig,dest` pair का average arrival और departure delay कैसे प्राप्त करें? {#origin-dest-month}
+
+```{r}
+ans <- flights[carrier == "AA",
+ .(mean(arr_delay), mean(dep_delay)),
+ by = .(origin, dest, month)]
+ans
+```
+
+* क्योंकि `j` की expressions के लिए column names नहीं दिए, वे automatically `V1` और `V2` बने।
+
+* फिर ध्यान दें कि grouping columns का input order result में preserve होता है।
+
+अब यदि result को grouping columns `origin`, `dest` और `month` के अनुसार order करना हो तो?
+
+### b) Sorted `by`: `keyby`
+
+`data.table` द्वारा groups का original order retain करना intentional और by design है। कई cases में original order preserve करना essential है। लेकिन कभी grouping variables से automatically sort करना चाहते हैं।
+
+#### -- सभी grouping variables से सीधे order कैसे करें?
+
+```{r}
+ans <- flights[carrier == "AA",
+ .(mean(arr_delay), mean(dep_delay)),
+ keyby = .(origin, dest, month)]
+ans
+```
+
+* हमने केवल `by` को `keyby` में बदला। इससे result grouping variables के increasing order में automatically order हो जाता है। वास्तव में `by` की internal implementation पहले sort करती है और फिर original table order recover करती है; इसलिए `keyby` अक्सर `by` से तेज़ होता है क्योंकि उसे यह second step नहीं करना पड़ता।
+
+**Keys:** वास्तव में `keyby`, *सिर्फ ordering* से थोड़ा अधिक करता है। Ordering के बाद `sorted` नाम का `attribute` set करके *key भी set* करता है।
+
+`keys` के बारे में [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html) विगनेट में अधिक सीखेंगे; फिलहाल बस इतना जानना पर्याप्त है कि `keyby` से `by` में specified columns के अनुसार result automatically order किया जा सकता है।
+
+### c) Chaining
+
+[Carrier *"AA"* के लिए प्रत्येक `origin, dest` pair की total trips प्राप्त करने](#origin-dest-N) वाले task को फिर देखते हैं।
+
+```{r}
+ans <- flights[carrier == "AA", .N, by = .(origin, dest)]
+```
+
+#### -- `ans` को `origin` के ascending order और `dest` के descending order में कैसे order करें?
+
+Intermediate result को variable में store करके उस variable पर `order(origin, -dest)` उपयोग कर सकते हैं। यह काफी straightforward लगता है।
+
+```{r}
+ans <- ans[order(origin, -dest)]
+head(ans)
+```
+
+* याद करें कि `data.table` के frame में `order()` के अंदर `character` column पर `-` उपयोग कर सकते हैं। यह `data.table` के internal query optimisation के कारण संभव है।
+
+* यह भी याद करें कि `data.table` के frame में `order(...)` speed के लिए automatically `data.table` के internal fast radix order `forder()` का उपयोग करता है।
+
+लेकिन इसमें intermediate result assign करना और फिर उसे overwrite करना पड़ता है। हम *chaining* expressions से इस temporary intermediate assignment को पूरी तरह avoid कर सकते हैं।
+
+```{r}
+ans <- flights[carrier == "AA", .N, by = .(origin, dest)][order(origin, -dest)]
+head(ans, 10)
+```
+
+* Expressions को एक के बाद एक जोड़ सकते हैं, operations की *chain* बनाते हुए, जैसे `DT[ ... ][ ... ][ ... ]`।
+
+* या उन्हें vertically भी chain कर सकते हैं:
+
+ ```r
+ DT[ ...
+ ][ ...
+ ][ ...
+ ]
+ ```
+
+### d) `by` में Expressions
+
+#### -- क्या `by` *expressions* भी स्वीकार कर सकता है या केवल columns?
+
+हाँ। उदाहरण के लिए यदि जानना हो कि कितनी flights late start हुईं लेकिन early (या on time) पहुँचीं, कितनी late start और late arrive हुईं, आदि...
+
+```{r}
+ans <- flights[, .N, .(dep_delay>0, arr_delay>0)]
+ans
+```
+
+* अंतिम row `dep_delay > 0 = TRUE` और `arr_delay > 0 = FALSE` से संबंधित है। हम देख सकते हैं कि `r flights[!is.na(arr_delay) & !is.na(dep_delay), .N, .(dep_delay>0, arr_delay>0)][, N[4L]]` flights late start हुईं लेकिन early (या on time) पहुँचीं।
+
+* ध्यान दें कि `by-expression` को कोई names नहीं दिए। इसलिए result में names automatically assign हुए। `j` की तरह इन expressions को किसी `list` के elements की तरह name कर सकते हैं, उदाहरण `DT[, .N, .(dep_delayed = dep_delay>0, arr_delayed = arr_delay>0)]`।
+
+* Expressions के साथ अन्य columns भी दे सकते हैं, उदाहरण: `DT[, .N, by = .(a, b>0)]`।
+
+### e) `j` में Multiple columns - `.SD`
+
+#### -- क्या हर column के लिए `mean()` अलग-अलग compute करना पड़ेगा?
+
+निश्चित रूप से हर column के लिए एक-एक करके `mean(myCol)` type करना practical नहीं है। यदि 100 columns का `mean()` निकालना हो तो?
+
+इसे efficiently और concisely कैसे करें? [इस tip](#tip-1) को याद करें - *"जब तक `j`-expression `list` लौटाती है, list का प्रत्येक element resulting `data.table` में column बन जाएगा"*। यदि grouping के दौरान *प्रत्येक group के data subset* को variable की तरह refer कर सकें, तो पहले से परिचित (या जल्द परिचित होने वाले) base function `lapply()` से उस variable की सभी columns पर loop कर सकते हैं। `data.table` specific कोई नया function name सीखने की आवश्यकता नहीं।
+
+#### Special symbol `.SD`: {#special-SD}
+
+`data.table` `.SD` नाम का *special* symbol देता है। इसका अर्थ है **S**ubset of **D**ata। यह स्वयं एक `data.table` है जो `by` से defined *current group* का data रखता है।
+
+याद करें कि `data.table` internally एक `list` भी है जिसमें सभी columns की length समान होती है।
+
+`.SD` कैसा दिखता है यह देखने के लिए [पहले वाला `data.table` `DT`](#what-is-datatable-1a) उपयोग करते हैं।
+
+```{r}
+DT
+
+DT[, print(.SD), by = ID]
+```
+
+* Default रूप से `.SD` में *grouping columns को छोड़कर* बाकी सभी columns होते हैं।
+
+* यह original order preserve करके भी generate होता है - पहले `ID = "b"`, फिर `ID = "a"`, और फिर `ID = "c"` से संबंधित data।
+
+(Multiple) columns पर compute करने के लिए base R function `lapply()` सीधे उपयोग कर सकते हैं।
+
+```{r}
+DT[, lapply(.SD, mean), by = ID]
+```
+
+* `.SD` उस group के लिए columns `a`, `b` और `c` से संबंधित rows रखता है। इन प्रत्येक columns पर परिचित base function `lapply()` से `mean()` compute करते हैं।
+
+* प्रत्येक group तीन elements की list लौटाता है जिनमें mean values होती हैं और ये resulting `data.table` के columns बनेंगे।
+
+* क्योंकि `lapply()` एक `list` लौटाता है, इसे अतिरिक्त `.()` में wrap करने की आवश्यकता नहीं (आवश्यक हो तो [यह tip](#tip-1) देखें)।
+
+हम लगभग वहाँ पहुँच गए। एक छोटी बात बाकी है। हमारे `flights` `data.table` में हम केवल `arr_delay` और `dep_delay` दो columns का `mean()` calculate करना चाहते थे। लेकिन default रूप से `.SD` में grouping variables के अलावा सभी columns होंगे।
+
+#### -- केवल वे columns कैसे specify करें जिन पर `mean()` compute करना है?
+
+#### .SDcols
+
+Argument `.SDcols` का उपयोग करें। यह column names या column indices स्वीकार करता है। उदाहरण `.SDcols = c("arr_delay", "dep_delay")` सुनिश्चित करता है कि प्रत्येक group के लिए `.SD` में केवल ये दो columns हों।
+
+[Part g)](#refer-j) की तरह, रखने वाले columns के बजाय हटाने वाले columns भी `-` या `!` से specify कर सकते हैं। इसके अलावा consecutive columns को `colA:colB` से select और `!(colA:colB)` या `-(colA:colB)` से deselect कर सकते हैं।
+
+अब `.SD` और `.SDcols` का उपयोग करके `origin`, `dest` और `month` से grouped `arr_delay` और `dep_delay` columns का `mean()` निकालते हैं।
+
+```{r}
+flights[carrier == "AA", ## Only on trips with carrier "AA"
+ lapply(.SD, mean), ## compute the mean
+ by = .(origin, dest, month), ## for every 'origin,dest,month'
+ .SDcols = c("arr_delay", "dep_delay")] ## for just those specified in .SDcols
+```
+
+### f) प्रत्येक group के लिए `.SD` subset करना:
+
+#### -- प्रत्येक `month` के लिए पहली दो rows कैसे लौटाएँ?
+
+```{r}
+ans <- flights[, head(.SD, 2), by = month]
+head(ans)
+```
+
+* `.SD` एक `data.table` है जो *उस group* की सभी rows रखता है। हम केवल पहली दो rows subset करते हैं, जैसा [यहाँ](#subset-rows-integer) पहले देखा।
+
+* प्रत्येक group के लिए `head(.SD, 2)` पहली दो rows `data.table` के रूप में लौटाता है, जो स्वयं `list` भी है, इसलिए इसे `.()` में wrap करने की आवश्यकता नहीं।
+
+### g) `j` को इतना flexible क्यों रखा गया है?
+
+ताकि syntax consistent रहे और नए functions सीखने के बजाय पहले से उपलब्ध (और familiar) base functions उपयोग करते रहें। इसे दिखाने के लिए उस `data.table` `DT` का उपयोग करते हैं जिसे शुरुआत में [What is a data.table?](#what-is-datatable-1a) section में बनाया था।
+
+#### -- `ID` के प्रत्येक group के लिए columns `a` और `b` को concatenate कैसे करें?
+
+```{r}
+DT[, .(val = c(a,b)), by = ID]
+```
+
+* बस इतना ही। किसी special syntax की आवश्यकता नहीं। केवल vectors concatenate करने वाला base function `c()` और [पहले वाली tip](#tip-1) जानना पर्याप्त है।
+
+#### -- यदि columns `a` और `b` की सभी values concatenate करनी हों, लेकिन list column के रूप में return करनी हों तो?
+
+```{r}
+DT[, .(val = list(c(a,b))), by = ID]
+```
+
+* यहाँ पहले प्रत्येक group के लिए `c(a,b)` से values concatenate करते हैं और उसे `list()` में wrap करते हैं। इसलिए प्रत्येक group के लिए सभी concatenated values की list लौटती है।
+
+* ध्यान दें कि commas केवल display के लिए हैं। List column की प्रत्येक cell में कोई भी object हो सकता है; इस example में प्रत्येक cell स्वयं एक vector है और कुछ cells में vectors दूसरों से लंबे हैं।
+
+जब आप `j` का उपयोग internalise करना शुरू करेंगे, तो syntax की शक्ति समझ आएगी। इसे समझने का बहुत उपयोगी तरीका है `print()` की मदद से प्रयोग करना।
+
+उदाहरण:
+
+```{r}
+## look at the difference between
+DT[, print(c(a,b)), by = ID] # (1)
+
+## and
+DT[, print(list(c(a,b))), by = ID] # (2)
+```
+
+```{r, echo = FALSE}
+p = function(x) paste0('', paste(deparse(substitute(x)), collapse = ' '), ' = ', x, '')
+```
+
+(1) में प्रत्येक group के लिए length = 6,4,2 वाला vector लौटता है। जबकि (2) प्रत्येक group के लिए length 1 की list लौटाता है, जिसकी पहली element में length 6,4,2 के vectors होते हैं। इसलिए (1) की length `{r} p(6+4+2)` होती है, जबकि (2) `{r} p(1+1+1)` लौटाता है।
+
+`j` की flexibility हमें किसी भी list object को data.table के element के रूप में store करने देती है। उदाहरण के लिए जब statistical models groups पर fit किए जाते हैं, तो इन models को data.table में store किया जा सकता है। Code concise और समझने में आसान रहता है।
+
+```{r}
+## Do long distance flights cover up departure delay more than short distance flights?
+## Does cover up vary by month?
+flights[, `:=`(makeup = dep_delay - arr_delay)]
+
+makeup.models <- flights[, .(fit = list(lm(makeup ~ distance))), by = .(month)]
+makeup.models[, .(coefdist = coef(fit[[1]])[2], rsq = summary(fit[[1]])$r.squared), by = .(month)]
+```
+
+Data.frames का उपयोग करके वही result प्राप्त करने के लिए अधिक complicated code चाहिए।
+
+```{r}
+setDF(flights)
+flights.split <- split(flights, f = flights$month)
+makeup.models.list <- lapply(flights.split, function(df) c(month = df$month[1], fit = list(lm(makeup ~ distance, data = df))))
+makeup.models.df <- do.call(rbind, makeup.models.list)
+data.frame(t(sapply(
+ makeup.models.df[, "fit"],
+ function(model) c(coefdist = coef(model)[2L], rsq = summary(model)$r.squared)
+)))
+setDT(flights)
+```
+
+## सारांश
+
+`data.table` syntax का general form है:
+
+```r
+DT[i, j, by]
+```
+
+अब तक हमने देखा कि,
+
+#### `i` का उपयोग:
+
+* हम `data.frame` की तरह rows subset कर सकते हैं - अंतर यह है कि `DT$` बार-बार लिखने की आवश्यकता नहीं क्योंकि `data.table` के frame के भीतर columns variables की तरह दिखाई देते हैं।
+
+* `order()` से `data.table` sort भी कर सकते हैं, जो बेहतर performance के लिए internally data.table का fast order उपयोग करता है।
+
+`data.table` को key करके `i` में इससे भी बहुत अधिक किया जा सकता है, जिससे बहुत तेज़ subsets और joins संभव होते हैं। इसे [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html) और [`vignette("datatable-joins", package="data.table")`](datatable-joins.html) vignettes में देखेंगे।
+
+#### `j` का उपयोग:
+
+1. Columns को `data.table` तरीके से select करें: `DT[, .(colA, colB)]`।
+
+2. Columns को `data.frame` तरीके से select करें: `DT[, c("colA", "colB")]`।
+
+3. Columns पर compute करें: `DT[, .(sum(colA), mean(colB))]`।
+
+4. आवश्यकता हो तो names दें: `DT[, .(sA = sum(colA), mB = mean(colB))]`।
+
+5. `i` के साथ combine करें: `DT[colA > value, sum(colB)]`।
+
+#### `by` का उपयोग:
+
+* `by` का उपयोग करके columns की *list*, column names का *character vector*, या यहाँ तक कि *expressions* देकर group कर सकते हैं। `j` की flexibility, `by` और `i` के साथ मिलकर बहुत powerful syntax बनाती है।
+
+* `by` multiple columns और *expressions* दोनों handle कर सकता है।
+
+* Grouped result automatically sort करने के लिए grouping columns को `keyby` कर सकते हैं।
+
+* परिचित base functions का उपयोग करके multiple columns पर operate करने के लिए `j` में `.SD` और `.SDcols` उपयोग कर सकते हैं। कुछ examples:
+
+ 1. `DT[, lapply(.SD, fun), by = ..., .SDcols = ...]` - `by` में specified columns से grouping करते हुए `.SDcols` में specified सभी columns पर `fun` apply करता है।
+
+ 2. `DT[, head(.SD, 2), by = ...]` - प्रत्येक group की पहली दो rows return करता है।
+
+ 3. `DT[col > val, head(.SD, 1), by = ...]` - `i` को `j` और `by` के साथ combine करता है।
+
+#### और tip याद रखें:
+
+जब तक `j` एक `list` लौटाता है, list का प्रत्येक element resulting `data.table` में एक column बन जाएगा।
+
+[अगले विगनेट (`vignette("datatable-reference-semantics", package="data.table")`)](datatable-reference-semantics.html) में देखेंगे कि columns को *by reference add/update/delete* कैसे करें और उन्हें `i` तथा `by` के साथ कैसे combine करें।
+
+***
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
diff --git a/vignettes/hi/datatable-joins.Rmd b/vignettes/hi/datatable-joins.Rmd
new file mode 100644
index 0000000000..a666e5cf22
--- /dev/null
+++ b/vignettes/hi/datatable-joins.Rmd
@@ -0,0 +1,736 @@
+---
+title: "data.table में Joins"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Joins in data.table}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+editor_options:
+ chunk_output_type: console
+---
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+```
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+इस विगनेट में आप `data.table` syntax में उपलब्ध resources का उपयोग करके किसी भी join operation को करना सीखेंगे।
+
+यह मानता है कि आप `data.table` syntax से परिचित हैं। यदि ऐसा नहीं है, तो कृपया निम्नलिखित vignettes पढ़ें:
+
+- [`vignette("datatable-intro", package="data.table")`](datatable-intro.html)
+- [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html)
+- [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html)
+
+***
+
+## 1. उदाहरण डेटा परिभाषित करना
+
+Real-life examples के साथ available method का उपयोग कैसे करें, इसे दिखाने के लिए एक छोटे supermarket का **normalized database** simulate करते हैं और database में निम्न tables define करते हैं:
+
+1. `Products`, एक table जिसमें rows विभिन्न products की characteristics देती हैं। Framework ***missing values*** को कैसे handle करता है यह दिखाने के लिए एक `id` को `NA` रखा गया है।
+
+```{r, define_products}
+Products = rowwiseDT(
+ id=, name=, price=, unit=, type=,
+ 1L, "banana", 0.63, "unit", "natural",
+ 2L, "carrots", 0.89, "lb", "natural",
+ 3L, "popcorn", 2.99, "unit", "processed",
+ 4L, "soda", 1.49, "ounce", "processed",
+ NA, "toothpaste", 2.99, "unit", "processed"
+)
+```
+
+2. `NewTax`, एक table जिसमें rows processed products पर उनकी units के आधार पर कुछ taxes define करती हैं।
+
+```{r define_new_tax}
+NewTax = data.table(
+ unit = c("unit", "ounce"),
+ type = "processed",
+ tax_prop = c(0.65, 0.20)
+)
+
+NewTax
+```
+
+
+3. `ProductReceived`, एक table जिसकी rows weekly incoming inventory को simulate करती हैं।
+
+```{r define_product_received}
+set.seed(2156)
+
+# NB: Jan 8, 2024 is a Monday.
+receipt_dates = seq(from=as.IDate("2024-01-08"), length.out=10L, by="week")
+
+ProductReceived = data.table(
+ id=1:10, # unique identifier for an supply transaction
+ date=receipt_dates,
+ product_id=sample(c(NA, 1:3, 6L), size=10L, replace=TRUE), # NB: product '6' is not recorded in Products above.
+ count=sample(c(50L, 100L, 150L), size=10L, replace=TRUE)
+)
+
+ProductReceived
+```
+
+4. `ProductSales`, एक table जिसकी rows customer transactions को simulate करती हैं।
+
+```{r define_product_sales}
+set.seed(5415)
+
+# Monday-Friday (4 days later) for each of the weeks present in ProductReceived
+possible_weekdays <- as.IDate(sapply(receipt_dates, `+`, 0:4))
+
+ProductSales = data.table(
+ id = 1:10,
+ date = sort(sample(possible_weekdays, 10L)),
+ product_id = sample(c(1:3, 7L), size = 10L, replace = TRUE), # NB: product '7' is in neither Products nor ProductReceived.
+ count = sample(c(50L, 100L, 150L), size = 10L, replace = TRUE)
+)
+
+ProductSales
+```
+
+## 2. `data.table` joining syntax
+
+Join operations करने के लिए `data.table` syntax का लाभ लेने से पहले हमें यह जानना होगा कि सफल joins करने में कौन-से arguments मदद करते हैं।
+
+अगला diagram प्रत्येक basic argument का description दिखाता है। आगे के sections में हम दिखाएँगे कि प्रत्येक का उपयोग कैसे करें और धीरे-धीरे complexity बढ़ाएँगे।
+
+```
+x[i, on, nomatch]
+| | | |
+| | | \__ If NULL only returns rows linked in x and i tables
+| | \____ a character vector or list defining match logic
+| \_____ primary data.table, list or data.frame
+\____ secondary data.table
+```
+
+**ध्यान दें**: याद रखें कि `data.table` में standard argument order `dt[i, j, by]` है। Join operations में `on` और `nomatch` arguments को नाम से pass करने की recommendation है, ताकि `j` और `by` की आवश्यकता न होने पर उनका उपयोग न करना पड़े।
+
+## 3. Equi joins
+
+यह सबसे सामान्य और सरल case है, जहाँ tables के बीच common elements खोजकर उन्हें combine किया जा सकता है।
+
+Tables के बीच relationship हो सकता है:
+
+- **One to one**: जब प्रत्येक matching value दोनों tables में unique हो।
+- **One to many**: जब कुछ matching values एक table में repeated हों और दूसरे में unique हों।
+- **Many to many**: जब matching values दोनों tables में कई बार repeated हों।
+
+आगे के अधिकतर examples में हम *one to many* matches करेंगे, लेकिन *many to many* matches के लिए उपलब्ध resources भी समझाएँगे।
+
+
+### 3.1. Right join
+
+इस method का उपयोग तब करें जब आपको एक या अधिक references के आधार पर 2 tables के columns combine करने हों लेकिन ***दाईं ओर स्थित table (square brackets के अंदर वाली table) की सभी rows बनाए रखनी हों***।
+
+हमारे supermarket context में received products के बारे में अधिक details देखने के लिए right join कर सकते हैं, क्योंकि यह relation *one to many* है; इसके लिए `on` argument को vector pass करेंगे।
+
+```{r}
+Products[ProductReceived,
+ on = c(id = "product_id")]
+```
+
+काफी चीज़ें बदली हैं, इसलिए नई characteristics को निम्न groups में समझते हैं:
+
+- **Column level**
+ - नए `data.table` में columns का *पहला group* `x` table से आता है।
+ - नए `data.table` में columns का *दूसरा group* `i` table से आता है।
+ - यदि join operation में कोई **name conflict** हो (दोनों tables में same column name), तो **right-hand table** (`i` position वाली table) के column names में ***prefix*** `i.` जुड़ता है।
+
+- **Row level**
+ - `ProductReceived` table की row 1 में मौजूद missing `product_id`, `Products` table के missing `id` से सफलतापूर्वक match हुआ; इसलिए `NA` ***values को किसी अन्य value की तरह treat किया जाता है***।
+ - `i` table की सभी rows रखी गईं, जिनमें शामिल हैं:
+ - Non-matching rows, जैसे `product_id = 6` वाली row।
+ - वे rows जिनमें वही `product_id` कई बार repeat होता है।
+
+#### 3.1.1. List argument द्वारा joining
+
+यदि आप विगनेट follow कर रहे हैं, तो आपने देखा होगा कि हमने `on` argument में tables के relations define करने के लिए vector उपयोग किया। यह विशेष रूप से **अपने functions बनाते समय** उपयोगी है, लेकिन columns-to-match define करने के लिए **list** का उपयोग भी एक alternative है।
+
+इस capability का उपयोग करने के लिए हमारे पास 2 equivalent alternatives हैं:
+
+- Related columns को base R `list` function में wrap करना।
+
+```r
+Products[ProductReceived,
+ on = list(id = product_id)]
+```
+
+- Related columns को `list` alias `.` में wrap करना।
+
+```r
+Products[ProductReceived,
+ on = .(id = product_id)]
+```
+
+#### 3.1.2. `on` argument define करने के alternatives
+
+पिछले सभी examples में हमने match किए जाने वाले column names `on` argument को pass किए, लेकिन `data.table` इस syntax के alternatives भी देता है।
+
+- **Natural join**: Common column names के आधार पर matching के columns select करता है। इस method को दिखाने के लिए `Products` table में column `id` का नाम `product_id` करते हैं और keyword `.NATURAL` उपयोग करते हैं।
+
+```{r}
+ProductsChangedName = setnames(copy(Products), "id", "product_id")
+ProductsChangedName
+
+ProductsChangedName[ProductReceived, on = .NATURAL]
+```
+
+- **Keyed join**: Columns के names से स्वतंत्र रूप से keyed columns के आधार पर matching के columns select करता है। इस method को दिखाने के लिए दोनों tables में keys को same order में define करना होगा।
+
+```{r}
+ProductsKeyed = setkey(copy(Products), id)
+key(ProductsKeyed)
+
+ProductReceivedKeyed = setkey(copy(ProductReceived), product_id)
+key(ProductReceivedKeyed)
+
+ProductsKeyed[ProductReceivedKeyed]
+```
+
+#### 3.1.3. Joining के बाद operations
+
+Join के बाद अक्सर कुछ additional transformations करनी होती हैं। इसके लिए निम्न alternatives हैं:
+
+- Brackets की एक और pair `[]` जोड़कर नई instruction chain करना।
+- `j` argument को उन columns की list pass करना जिन्हें रखना या create करना है।
+
+हमारी recommendation है कि संभव हो तो दूसरा alternative उपयोग करें, क्योंकि यह पहले की तुलना में **तेज़** है और **कम memory** उपयोग करता है।
+
+##### `j` argument के साथ shared column names manage करना
+
+`j` argument उन joins को manage करने के लिए अच्छे alternatives देता है जहाँ tables **कई columns के same names share करती हैं**। Default रूप से सभी columns अपना source `x` table से लेते हैं, लेकिन source स्पष्ट करने के लिए `x.` prefix उपयोग कर सकते हैं, और `x` table के `i` argument में declared table के किसी column को उपयोग करने के लिए `i.` prefix उपयोग कर सकते हैं।
+
+छोटे supermarket पर लौटते हैं। `ProductReceived` table को `Products` table से update करने के बाद, मान लें हम निम्न changes करना चाहते हैं:
+
+- Column names `id` से `product_id` और `i.id` से `received_id` में बदलना।
+- `total_value` जोड़ना।
+
+```{r}
+Products[
+ ProductReceived,
+ on = c("id" = "product_id"),
+ j = .(product_id = x.id,
+ name = x.name,
+ price,
+ received_id = i.id,
+ date = i.date,
+ count,
+ total_value = price * count)
+]
+```
+
+
+##### `data.table` में `on` के साथ summarizing
+
+इस alternative का उपयोग `x` table में मौजूद columns के आधार पर aggregated results लौटाने के लिए भी कर सकते हैं।
+
+उदाहरण के लिए, हमें यह जानने में रुचि हो सकती है कि दिनों के दौरान प्रत्येक product खरीदने पर कितना पैसा खर्च किया।
+
+```{r}
+dt1 = ProductReceived[
+ Products,
+ on = c("product_id" = "id"),
+ by = .EACHI,
+ j = .(total_value_received = sum(price * count))
+]
+
+# alternative using multiple [] queries
+dt2 = ProductReceived[
+ Products,
+ on = c("product_id" = "id"),
+][, .(total_value_received = sum(price * count)),
+ by = "product_id"
+]
+
+identical(dt1, dt2)
+```
+
+#### 3.1.4. कई columns के आधार पर joining
+
+अब तक हमने केवल 1 column के आधार पर `data.table`s join किए हैं, लेकिन यह जानना महत्वपूर्ण है कि package कई columns match करके tables join कर सकता है।
+
+इसे दिखाने के लिए मान लें कि हम `Products` table को **update** करने हेतु `NewTax` से `tax_prop` जोड़ना चाहते हैं।
+
+```{r}
+NewTax[Products, on = c("unit", "type")]
+```
+
+### 3.2. Inner join
+
+इस method का उपयोग तब करें जब एक या अधिक references के आधार पर 2 tables के columns combine करने हों लेकिन ***केवल वही rows रखनी हों जो दोनों tables में match करती हैं***।
+
+इस operation के लिए किसी भी पिछले join operation में केवल `nomatch = NULL` जोड़ना है, जिससे वही matching results लौटेंगे।
+
+```{r}
+# First Table
+Products[ProductReceived,
+ on = c("id" = "product_id"),
+ nomatch = NULL]
+
+# Second Table
+ProductReceived[Products,
+ on = .(product_id = id),
+ nomatch = NULL]
+```
+
+दोनों tables में समान information होने के बावजूद कुछ महत्वपूर्ण differences हैं:
+
+- उनका column ordering अलग है।
+- उनके column names में differences हैं:
+ - पहली table का `id` column वही information रखता है जो दूसरी table का `product_id` रखता है।
+ - पहली table का `i.id` column वही information रखता है जो दूसरी table का `id` रखता है।
+
+### 3.3. Anti-join
+
+यह method **केवल वे rows रखता है जो दूसरी table की किसी भी row से match नहीं करतीं**।
+
+इस technique को apply करने के लिए `i` argument में स्थित table को negate (`!`) कर सकते हैं।
+
+```{r}
+Products[!ProductReceived,
+ on = c("id" = "product_id")]
+```
+
+जैसा आप देख सकते हैं, result में केवल 'soda' है, क्योंकि वही एक product था जो `ProductReceived` table में मौजूद नहीं था।
+
+```{r}
+ProductReceived[!Products,
+ on = c("product_id" = "id")]
+```
+
+इस case में operation `product_id = 6` वाली row लौटाता है, क्योंकि वह `Products` table में मौजूद नहीं है।
+
+### 3.4. Semi join
+
+यह method **केवल वे rows extract करता है जो दूसरी table की किसी row से match करती हैं**, लेकिन tables के columns combine नहीं करता।
+
+यह subset-as-join जैसा है, लेकिन इस बार हम `i` को पूरी table pass कर रहे हैं, इसलिए सुनिश्चित करना होगा कि:
+
+- `x` table की कोई row, `i` argument को दी गई table में row duplication के कारण duplicate न हो।
+
+- `x` की सभी remaining rows अपना original row order बनाए रखें।
+
+
+इसे करने के लिए निम्न steps apply कर सकते हैं:
+
+1. `which = TRUE` के साथ **inner join** करें ताकि `x` table की प्रत्येक matching row से संबंधित row numbers save हो जाएँ।
+
+```{r}
+SubSetRows = Products[
+ ProductReceived,
+ on = .(id = product_id),
+ nomatch = NULL,
+ which = TRUE
+]
+
+SubSetRows
+```
+
+2. Unique row ids select और sort करें।
+
+```{r}
+SubSetRowsSorted = sort(unique(SubSetRows))
+
+SubSetRowsSorted
+```
+
+
+3. `x` की रखी जाने वाली rows select करें।
+
+```{r}
+Products[SubSetRowsSorted]
+```
+
+
+### 3.5. Left join
+
+इस method का उपयोग तब करें जब एक या अधिक references के आधार पर 2 tables के columns combine करने हों लेकिन ***बाईं ओर वाली table की सभी rows बनाए रखनी हों***।
+
+इस operation के लिए केवल **दोनों tables का order exchange** करना है और `on` argument में column names उसी के अनुसार बदलने हैं।
+
+```{r}
+ProductReceived[Products,
+ on = list(product_id = id)]
+```
+
+कुछ महत्वपूर्ण considerations:
+
+- **Column level**
+ - Columns का *पहला group* अब `ProductReceived` table से आता है क्योंकि वही `x` table है।
+ - Columns का *दूसरा group* अब `Products` table से आता है क्योंकि वही `i` table है।
+ - किसी column में `i.` prefix नहीं जोड़ा गया।
+
+- **Row level**
+ - `i` table की सभी rows रखी गईं: `Products` की soda entry, जिसे `ProductReceived` की किसी row ने match नहीं किया, result का हिस्सा बनी रहती है।
+ - `product_id = 6` से संबंधित row अब result का हिस्सा नहीं है क्योंकि वह `Products` table में मौजूद नहीं है।
+
+
+#### 3.5.1. Chain operations के बाद joining
+
+`data.table` की प्रमुख features में से एक है कि brackets chain करके final result save करने से पहले कई operations apply किए जा सकते हैं।
+
+```r
+DT[
+ ...
+][
+ ...
+][
+ ...
+]
+```
+
+अब तक, यदि इन सभी operations के बाद **हम किसी row को हटाए बिना नए columns join करना चाहते**, तो chaining process रोकना, temporary table save करना और बाद में join operation apply करना पड़ता।
+
+इस situation से बचने के लिए special symbol `.SD` का उपयोग करके **changed table के आधार पर right join** किया जा सकता है।
+
+```{r}
+NewTax[Products,
+ on = c("unit", "type")
+][, ProductReceived[.SD,
+ on = list(product_id = id)],
+ .SDcols = !c("unit", "type")]
+```
+
+### 3.6. Many-to-many join
+
+कभी-कभी हम **duplicated `id` values** वाले columns के आधार पर tables join करना चाहते हैं ताकि बाद में कुछ transformations की जा सकें।
+
+इस situation को दिखाने के लिए `product_id == 1L` का example लेते हैं, जिसकी `ProductReceived` table में 4 rows हैं।
+
+```{r}
+ProductReceived[product_id == 1L]
+```
+
+और `ProductSales` table में भी 4 rows हैं।
+
+```{r}
+ProductSales[product_id == 1L]
+```
+
+इस join के लिए `i` table में केवल उस product तक join सीमित करने हेतु `product_id == 1L` filter करना है और `allow.cartesian = TRUE` set करना है, ताकि एक table की प्रत्येक row दूसरी table की हर row के साथ combine हो सके।
+
+```{r}
+ProductReceived[ProductSales[list(1L),
+ on = "product_id",
+ nomatch = NULL],
+ on = "product_id",
+ allow.cartesian = TRUE]
+```
+
+Result समझने के बाद यही process **सभी products** पर apply कर सकते हैं।
+
+```{r}
+ProductReceived[ProductSales,
+ on = "product_id",
+ allow.cartesian = TRUE]
+```
+
+**ध्यान दें**: `allow.cartesian` default रूप से FALSE है क्योंकि user आमतौर पर ऐसा नहीं चाहता और ऐसा cross join result में बहुत बड़ी number of rows बना सकता है। उदाहरण के लिए यदि Table A में 100 rows और Table B में 50 rows हैं, तो Cartesian product से 5000 rows (100 * 50) बनेंगी। बड़े datasets के लिए यह जल्दी memory-intensive हो सकता है।
+
+
+#### 3.6.1. एक match चुनना
+
+Tables join करने के बाद हमें पता चल सकता है कि required information निकालने के लिए केवल एक single join result चाहिए। इस case में 2 alternatives हैं:
+
+- **पहला match** चुन सकते हैं, अगले example में `id = 2` से represented।
+
+```{r}
+ProductReceived[ProductSales[product_id == 1L],
+ on = .(product_id),
+ allow.cartesian = TRUE,
+ mult = "first"]
+```
+
+- **अंतिम match** चुन सकते हैं, अगले example में `id = 9` से represented।
+
+```{r}
+ProductReceived[ProductSales[product_id == 1L],
+ on = .(product_id),
+ allow.cartesian = TRUE,
+ mult = "last"]
+```
+
+#### 3.6.2. Cross join
+
+यदि किसी particular id column की परवाह किए बिना **सभी possible row combinations** चाहिए, तो निम्न process follow कर सकते हैं:
+
+1. दोनों tables में constant value वाला नया column बनाएँ।
+
+```{r}
+ProductsTempId = copy(Products)[, temp_id := 1L]
+```
+
+2. उसी नए column के आधार पर दोनों tables join करें और process समाप्त होने पर उसे remove कर दें, क्योंकि join के बाद उसकी आवश्यकता नहीं रहती।
+
+```{r}
+AllProductsMix =
+ ProductsTempId[ProductsTempId,
+ on = "temp_id",
+ allow.cartesian = TRUE]
+
+AllProductsMix[, temp_id := NULL]
+
+# Removing type to make easier to see the result when printing the table
+AllProductsMix[, !c("type", "i.type")]
+```
+
+
+### 3.7. Full join
+
+इस method का उपयोग तब करें जब एक या अधिक references के आधार पर 2 tables के columns combine करने हों ***बिना किसी row को हटाए***।
+
+जैसा पिछले section में देखा, पिछले operations में से कोई भी missing `product_id = 6` और **soda** (`product_id = 4`) दोनों को एक साथ result में नहीं रख सकता।
+
+इस समस्या को हल करने के लिए `merge` function उपयोग कर सकते हैं, भले native `data.table` joining syntax की तुलना में इसका performance कम हो।
+
+```{r}
+merge(x = Products,
+ y = ProductReceived,
+ by.x = "id",
+ by.y = "product_id",
+ all = TRUE,
+ sort = FALSE)
+```
+
+
+## 4. Non-equi join
+
+Non-equi join वह join है जहाँ matching rows की condition equality के अलावा comparison operators जैसे `<`, `>`, `<=`, या `>=` पर आधारित होती है। इससे **अधिक flexible joining criteria** संभव होते हैं। `data.table` में non-equi joins विशेष रूप से इन operations के लिए उपयोगी हैं:
+
+- Nearest match ढूँढना।
+- Tables के बीच value ranges compare करना।
+
+यह अच्छा alternative है जब right या inner join apply करने के बाद आप:
+
+- Tables के numeric columns की comparisons के आधार पर returned rows की संख्या कम करना चाहते हैं।
+- Final result में table x *(secondary `data.table`)* के columns retain नहीं करना चाहते।
+
+यह कैसे काम करता है दिखाने के लिए product 2 की sales और receives पर focus करते हैं।
+
+```{r}
+ProductSalesProd2 = ProductSales[product_id == 2L]
+ProductReceivedProd2 = ProductReceived[product_id == 2L]
+```
+
+यदि उदाहरण के लिए जानना हो कि कौन-सी receive किसी sales date से पहले हुई थी, तो निम्न apply कर सकते हैं।
+
+```{r}
+ProductReceivedProd2[ProductSalesProd2,
+ on = "product_id",
+ allow.cartesian = TRUE
+][date < i.date]
+```
+
+यदि वही logic सीधे `on` को दी गई list पर apply करें तो क्या होता है?
+
+- क्योंकि यह operation अभी भी right join है, यह `i` table की सभी rows लौटाता है, लेकिन `id` और `count` की values केवल तब दिखाता है जब rules meet हों।
+
+- `ProductReceivedProd2` से संबंधित date इस नई table से omit हो गई।
+
+```{r}
+ProductReceivedProd2[ProductSalesProd2,
+ on = list(product_id, date < date)]
+```
+
+अब join apply करने के बाद result को केवल उन cases तक limit कर सकते हैं जो सभी joining criteria meet करते हैं।
+
+```{r}
+ProductReceivedProd2[ProductSalesProd2,
+ on = list(product_id, date < date),
+ nomatch = NULL]
+```
+
+### 4.1 Non-equi joins में output column names
+
+Non-equi joins (`<`, `>`, `<=`, `>=`) करते समय column names इस प्रकार assign होते हैं:
+
+- Left operand (`x` column) result में column name निर्धारित करता है।
+- Right operand (`i` column) values देता है लेकिन अपना original name retain नहीं करता।
+- Default रूप से `data.table` join condition में उपयोग किए गए `i` column को retain नहीं करता जब तक explicitly request न किया जाए।
+
+Non-equi joins में operator की left side (जैसे `x_int` in `x_int >= i_int`) `x` का column होना चाहिए, जबकि right side (जैसे `i_int`) `i` का column होना चाहिए।
+
+Non-equi joins अभी arbitrary expressions support नहीं करते (लेकिन [#1639](https://github.com/Rdatatable/data.table/issues/1639) देखें)। उदाहरण के लिए `on = .(x_int >= i_int)` valid है, लेकिन `on = .(x_int >= i_int + 1L)` नहीं। ऐसी non-equi join करने के लिए पहले expression को नए column के रूप में जोड़ें, जैसे `i[, i_int_plus_one := i_int + 1L]`, फिर `.on(x_int >= i_int_plus_one)` करें।
+
+```{r non_equi_join_example}
+x <- data.table(x_int = 2:4, lower = letters[1:3])
+i <- data.table(i_int = c(2L, 4L, 5L), UPPER = LETTERS[1:3])
+x[i, on = .(x_int >= i_int)]
+```
+
+मुख्य बातें:
+
+- Output column (`x_int`) का name `x` से आता है, लेकिन values `i` के `i_int` से आती हैं।
+- अंतिम row में `NA` है क्योंकि `x` की कोई row `i` की अंतिम row (`UPPER == "C"`) से match नहीं करती।
+- `x` की multiple rows `i` की पहली row `UPPER == "A"` से match करने के लिए लौटती हैं।
+
+यदि `i` से `i_int` column रखना है, तो उसे result में explicitly select करना होगा:
+
+```{r retain_i_column}
+x[i, on = .(x_int >= i_int), .(i_int = i.i_int, x_int = x.x_int, lower, UPPER)]
+```
+
+इस case में prefixes (`x.` और `i.`) strictly necessary नहीं हैं क्योंकि names unambiguous हैं, लेकिन उनका उपयोग output में स्पष्ट करता है कि `i_int` `i` से और `x_int` `x` से आया है।
+
+यदि unmatched rows exclude करनी हैं (_inner join_), तो `nomatch = NULL` उपयोग करें:
+
+```{r retain_i_column_inner_join}
+x[i, on = .(x_int >= i_int), .(i_int = i.i_int, x_int = x.x_int, lower, UPPER), nomatch = NULL]
+```
+
+## 5. Rolling join
+
+Rolling joins विशेष रूप से time-series data analysis में उपयोगी हैं। वे sorted column—आमतौर पर date या time column—में **nearest value के आधार पर rows match** करने देते हैं।
+
+यह तब उपयोगी है जब अलग-अलग sources के data को align करना हो **जहाँ timestamps exact match न करते हों**, या सबसे recent value को carry forward करना हो।
+
+उदाहरण के लिए financial data में, प्रत्येक transaction को सबसे recent stock price assign करने के लिए rolling join उपयोग कर सकते हैं, भले price updates और transactions exact same time पर न हों।
+
+
+हमारे supermarket example में sales को सबसे recent product information से match करने के लिए rolling join उपयोग कर सकते हैं।
+
+मान लें Bananas और Carrots की price हर month की first date पर बदलती है।
+
+```{r}
+ProductPriceHistory = data.table(
+ product_id = rep(1:2, each = 3),
+ date = rep(as.IDate(c("2024-01-01", "2024-02-01", "2024-03-01")), 2),
+ price = c(0.59, 0.63, 0.65, # Banana prices
+ 0.79, 0.89, 0.99) # Carrot prices
+)
+
+ProductPriceHistory
+```
+
+अब sale date के आधार पर प्रत्येक product को अलग price देकर right join कर सकते हैं।
+
+```{r}
+ProductPriceHistory[ProductSales,
+ on = .(product_id, date),
+ roll = TRUE,
+ j = .(product_id, date, count, price)]
+```
+
+यदि केवल matching cases देखने हैं, तो inner rolling join करने के लिए `nomatch = NULL` argument जोड़ना पर्याप्त है।
+
+```{r}
+ProductPriceHistory[ProductSales,
+ on = .(product_id, date),
+ roll = TRUE,
+ nomatch = NULL,
+ j = .(product_id, date, count, price)]
+```
+
+## 6. Joining speed का लाभ उठाना
+
+### 6.1. Joins के रूप में subsets
+
+जैसा पिछले section में देखा, `x` table को `i` table में available values से filter किया जाता है। यह process `i` argument को Boolean expression pass करने से तेज़ है।
+
+`x` table को speed से filter करने के लिए पूरी `data.table` pass करना आवश्यक नहीं; original table से रखने या हटाने वाली values के vectors की `list()` pass कर सकते हैं।
+
+उदाहरण के लिए market ने 100 units bananas (`product_id = 1`) या popcorn (`product_id = 3`) जिन dates पर receive किए, उन्हें filter करने के लिए निम्न उपयोग कर सकते हैं:
+
+```{r}
+ProductReceived[list(c(1L, 3L), 100L),
+ on = c("product_id", "count")]
+```
+
+अंततः हम join operation के आधार पर filter कर रहे हैं, इसलिए code ने **एक ऐसी row लौटाई जो original table में मौजूद नहीं थी**। इस behaviour से बचने के लिए हमेशा `nomatch = NULL` argument जोड़ने की recommendation है।
+
+```{r}
+ProductReceived[list(c(1L, 3L), 100L),
+ on = c("product_id", "count"),
+ nomatch = NULL]
+```
+
+
+इस technique का उपयोग values के किसी combination को filter out करने के लिए भी कर सकते हैं: `i` argument में expression को negate करने के लिए `!` prefix दें और `nomatch` को default value पर रखें। उदाहरण के लिए पहले filter की गई 2 rows को बाहर कर सकते हैं।
+
+```{r}
+ProductReceived[!list(c(1L, 3L), 100L),
+ on = c("product_id", "count")]
+```
+
+यदि single **character column** के लिए केवल एक value filter करनी हो, तो `list()` function call omit कर सकते हैं और filter की जाने वाली value सीधे `i` argument में pass कर सकते हैं।
+
+```{r}
+Products[c("banana","popcorn"),
+ on = "name",
+ nomatch = NULL]
+
+Products[!"popcorn",
+ on = "name"]
+```
+
+### 6.2. By-reference updating
+
+Joins के दौरान columns को **by reference** (बिना copy) modify करने के लिए `:=` उपयोग करें। General syntax: `x[i, on=, (cols) := val]`।
+
+**Simple One-to-One Update**
+
+`Products` को `ProductPriceHistory` की prices से update करें:
+
+```{r}
+Products[ProductPriceHistory,
+ on = .(id = product_id),
+ price := i.price]
+
+Products
+```
+
+- `i.price`, `ProductPriceHistory` की price को refer करता है।
+- `Products` को in-place modify करता है।
+
+**`.EACHI` के साथ Grouped Updates**
+
+प्रत्येक product के लिए last price/date प्राप्त करें:
+
+```{r Updating_with_the_Latest_Record}
+Products[ProductPriceHistory,
+ on = .(id = product_id),
+ `:=`(price = last(i.price), last_updated = last(i.date)),
+ by = .EACHI]
+
+Products
+```
+
+- `by = .EACHI`, i की rows के अनुसार group करता है (प्रत्येक ProductPriceHistory row के लिए 1 group)।
+- `last()` अंतिम value लौटाता है।
+
+**Efficient Right Join Update**
+
+Copy किए बिना `ProductPriceHistory` में product details जोड़ें:
+
+```{r}
+cols <- setdiff(names(Products), "id")
+ProductPriceHistory[, (cols) :=
+ Products[.SD, on = .(id = product_id), .SD, .SDcols = cols]]
+setnafill(ProductPriceHistory, fill=0, cols="price") # Handle missing values
+
+ProductPriceHistory
+```
+
+- `i` में `.SD`, `ProductPriceHistory` को refer करता है।
+- `j` में `.SD`, `Products` को refer करता है।
+- `:=` और `setnafill()` दोनों `ProductPriceHistory` को by reference update करते हैं।
+
+## संदर्भ
+
+- *data.table Rolling Joins को समझना*: https://www.r-bloggers.com/2016/06/understanding-data-table-rolling-joins/
+
+- *data.table के साथ Semi-join*: https://stackoverflow.com/questions/18969420/perform-a-semi-join-with-data-table
+
+- *data.table के साथ Cross join*: https://stackoverflow.com/questions/10600060/how-to-do-cross-join-in-r
+
+- *data.table का उपयोग करके full join कैसे करें?*: https://stackoverflow.com/questions/15170741/how-does-one-do-a-full-join-using-data-table
+
+- *Enhanced data.frame*: https://rdatatable.gitlab.io/data.table/reference/data.table.html
diff --git a/vignettes/hi/datatable-keys-fast-subset.Rmd b/vignettes/hi/datatable-keys-fast-subset.Rmd
new file mode 100644
index 0000000000..de3f95653c
--- /dev/null
+++ b/vignettes/hi/datatable-keys-fast-subset.Rmd
@@ -0,0 +1,501 @@
+---
+title: "Keys और तेज़ binary-search आधारित subset"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Keys and fast binary search based subset}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+यह विगनेट उन पाठकों के लिए है जो पहले से *data.table* syntax, उसके general form, `i` में rows subset करने, columns पर select और compute करने, `j` में columns को *by reference* add/modify/delete करने तथा `by` का उपयोग करके group करने से परिचित हैं। यदि आप इन concepts से परिचित नहीं हैं, तो पहले [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) और [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html) विगनेट पढ़ें।
+
+***
+
+## डेटा {#data}
+
+हम वही `flights` डेटा उपयोग करेंगे जो [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में उपयोग किया गया है।
+
+```{r, echo = FALSE}
+options(width = 100L)
+```
+
+```{r}
+flights <- fread("flights14.csv")
+head(flights)
+dim(flights)
+```
+
+## परिचय
+
+इस विगनेट में हम
+
+* पहले *data.table* में `key` की अवधारणा प्रस्तुत करेंगे, और `i` में *fast binary search* आधारित subsets करने के लिए keys सेट और उपयोग करेंगे,
+
+* देखेंगे कि key-based subsets को `j` और `by` के साथ ठीक उसी तरह combine किया जा सकता है जैसा पहले किया था,
+
+* अन्य उपयोगी arguments - `mult` और `nomatch` - देखेंगे,
+
+* और अंत में keys सेट करने के लाभ को देखकर निष्कर्ष निकालेंगे - *fast binary search based subsets* करना और उसकी तुलना traditional vector scan approach से करना।
+
+## 1. Keys
+
+### a) *Key* क्या है?
+
+[`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में हमने देखा कि `i` में logical expressions, row numbers और `order()` का उपयोग करके rows subset कैसे की जाती हैं। इस section में subsetting का एक और बेहद तेज़ तरीका देखेंगे - *keys* का उपयोग।
+
+लेकिन पहले *data.frames* से शुरुआत करते हैं। सभी *data.frames* में row names attribute होता है। नीचे दिए *data.frame* `DF` पर विचार करें।
+
+```{r}
+set.seed(1L)
+DF = data.frame(ID1 = sample(letters[1:2], 10, TRUE),
+ ID2 = sample(1:3, 10, TRUE),
+ val = sample(10),
+ stringsAsFactors = FALSE,
+ row.names = sample(LETTERS[1:10]))
+DF
+
+rownames(DF)
+```
+
+हम उसके row name का उपयोग करके किसी particular row को *subset* कर सकते हैं, जैसा नीचे दिखाया गया है:
+
+```{r}
+DF["C", ]
+```
+
+अर्थात row names लगभग *data.frame* की rows के लिए *index* की तरह होते हैं। हालाँकि,
+
+1. प्रत्येक row के लिए *ठीक एक* row name ही हो सकता है।
+
+ लेकिन किसी व्यक्ति (उदाहरण के लिए) के कम से कम दो names होते हैं - *first* और *second* name। Telephone directory को पहले *surname* और फिर *first name* के आधार पर व्यवस्थित करना उपयोगी होता है।
+
+2. और row names *unique* होने चाहिए।
+
+ ```r
+ rownames(DF) = sample(LETTERS[1:5], 10, TRUE)
+ # Warning: non-unique values when setting 'row.names': 'C', 'D'
+ # Error in `.rowNamesDF<-`(x, value = value): duplicate 'row.names' are not allowed
+ ```
+
+अब इसे *data.table* में convert करते हैं।
+
+```{r}
+DT = as.data.table(DF)
+DT
+
+rownames(DT)
+```
+
+* ध्यान दें कि row names reset हो गए हैं।
+
+* *data.tables* row names का कभी उपयोग नहीं करता। क्योंकि *data.tables*, *data.frames* से **inherit** करते हैं, इसलिए row names attribute फिर भी मौजूद होता है। लेकिन उसका उपयोग नहीं होता। थोड़ी देर में इसका कारण देखेंगे।
+
+ यदि आप row names सुरक्षित रखना चाहते हैं, तो `as.data.table()` में `keep.rownames = TRUE` उपयोग करें - इससे `rn` नाम का नया column बनेगा और row names उस column में assign होंगे।
+
+इसके बजाय *data.tables* में हम `keys` सेट और उपयोग करते हैं। `key` को **supercharged rownames** की तरह समझें।
+
+#### Keys और उनकी properties {#key-properties}
+
+1. हम *multiple columns* पर keys सेट कर सकते हैं और columns *different types* के हो सकते हैं -- *integer*, *numeric*, *character*, *factor*, *integer64* आदि। *list* और *complex* types अभी supported नहीं हैं।
+
+2. Uniqueness enforce नहीं की जाती, अर्थात duplicate key values allowed हैं। क्योंकि rows key के अनुसार sorted होती हैं, key columns में duplicates लगातार दिखाई देंगे।
+
+3. `key` सेट करने से *दो* काम होते हैं:
+
+ a. दिए गए column(s) के आधार पर *data.table* की rows को *by reference*, हमेशा *increasing* order में, physically reorder करता है।
+
+ b. `sorted` नाम का attribute *data.table* पर सेट करके उन columns को *key* columns के रूप में mark करता है।
+
+ क्योंकि rows reorder होती हैं, इसलिए एक *data.table* में अधिकतम एक key हो सकती है; उसे एक से अधिक तरीकों से simultaneously sort नहीं किया जा सकता।
+
+बाकी विगनेट में हम `flights` dataset के साथ काम करेंगे।
+
+### b) *data.table* पर keys सेट करना, प्राप्त करना और उपयोग करना
+
+#### -- *data.table* `flights` में `origin` column को key के रूप में कैसे सेट करें?
+
+```{r}
+setkey(flights, origin)
+head(flights)
+
+## alternatively we can provide character vectors to the function 'setkeyv()'
+# setkeyv(flights, "origin") # useful to program with
+```
+
+* आप `setkey()` function का उपयोग करके column names दे सकते हैं (बिना quote किए)। Interactive use के दौरान यह उपयोगी है।
+
+* वैकल्पिक रूप से column names का character vector `setkeyv()` function को pass कर सकते हैं। Functions बनाते समय columns को function arguments के रूप में key सेट करने के लिए pass करना हो तो यह विशेष रूप से उपयोगी है।
+
+* ध्यान दें कि result को वापस किसी variable को assign नहीं करना पड़ा। ऐसा इसलिए क्योंकि [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html) विगनेट में देखे `:=` की तरह, `setkey()` और `setkeyv()` input *data.table* को *by reference* modify करते हैं। वे result invisibly लौटाते हैं।
+
+* अब *data.table* दिए गए column - `origin` - के अनुसार reorder (या sort) हो गया है। क्योंकि reorder by reference होता है, हमें केवल *data.table* की rows की संख्या के बराबर length वाले एक column जितनी अतिरिक्त memory चाहिए, इसलिए यह बहुत memory efficient है।
+
+* `data.table()` function से *data.tables* बनाते समय `key` argument का उपयोग करके सीधे keys भी सेट कर सकते हैं। यह column names का character vector लेता है।
+
+#### set* और `:=`:
+
+*data.table* में `:=` operator और सभी `set*` functions (जैसे `setkey`, `setorder`, `setnames` आदि) ही input object को *by reference* modify करते हैं।
+
+जब आप किसी *data.table* को कुछ columns पर *key* कर देते हैं, तो `i` में `.()` notation का उपयोग करके उन key columns पर query करके subset कर सकते हैं। याद रखें कि `.()` वास्तव में `list()` का *alias* है।
+
+#### -- Key column `origin` का उपयोग करके वे सभी rows subset करें जहाँ origin airport *"JFK"* से मेल खाता है
+
+```{r}
+flights[.("JFK")]
+
+## alternatively
+# flights[J("JFK")] (or)
+# flights[list("JFK")]
+```
+
+* *Key* column पहले ही `origin` पर set है। इसलिए value, यहाँ *"JFK"*, सीधे देना पर्याप्त है। `.()` syntax यह पहचानने में मदद करता है कि task में *data.table* के key column में *"JFK"* value lookup करनी है (यहाँ `flights` *data.table* का `origin` column)।
+
+* पहले `origin` में *"JFK"* value से संबंधित *row indices* प्राप्त होते हैं। और क्योंकि `j` में कोई expression नहीं है, उन row indices से संबंधित सभी columns लौटाए जाते हैं।
+
+* *Character* type की single-column key पर subsetting करते समय `.()` notation छोड़ी जा सकती है और values सीधे उपयोग की जा सकती हैं, ठीक वैसे जैसे *data.frames* पर row names से subset करते हैं।
+
+ ```r
+ flights["JFK"] ## same as flights[.("JFK")]
+ ```
+
+* आवश्यकता अनुसार किसी भी संख्या में values subset कर सकते हैं
+
+ ```r
+ flights[c("JFK", "LGA")] ## same as flights[.(c("JFK", "LGA"))]
+ ```
+
+ यह वे सभी columns लौटाता है जो उन rows से संबंधित हैं जहाँ `origin` column या तो *"JFK"* या *"LGA"* से match करता है।
+
+#### -- कैसे पता करें कि *data.table* किन column(s) से keyed है?
+
+`key()` function का उपयोग करके।
+
+```{r}
+key(flights)
+```
+
+* यह सभी key columns का character vector लौटाता है।
+
+* यदि कोई key set नहीं है, तो `NULL` लौटाता है।
+
+### c) Keys और multiple columns
+
+दोहराने के लिए, *keys* *supercharged* row names जैसे हैं। हम multiple columns पर key सेट कर सकते हैं और वे विभिन्न types के हो सकते हैं।
+
+#### -- `origin` *और* `dest` दोनों columns पर keys कैसे सेट करें?
+
+```{r}
+setkey(flights, origin, dest)
+head(flights)
+
+## or alternatively
+# setkeyv(flights, c("origin", "dest")) # provide a character vector of column names
+
+key(flights)
+```
+
+* यह *data.table* को पहले `origin` column और फिर `dest` के अनुसार *by reference* sort करता है।
+
+#### -- Key columns का उपयोग करके वे सभी rows subset करें जहाँ पहली key column `origin` *"JFK"* से और दूसरी key column `dest` *"MIA"* से match करती है
+
+```{r}
+flights[.("JFK", "MIA")]
+```
+
+#### यहाँ subset कैसे काम करता है? {#multiple-key-point}
+
+* Internally यह कैसे काम करता है, इसे समझना महत्वपूर्ण है। पहले *"JFK"* को पहली key column `origin` से match किया जाता है। फिर *उन matching rows के भीतर* *"MIA"* को दूसरी key column `dest` से match किया जाता है, जिससे वे *row indices* प्राप्त होते हैं जहाँ `origin` और `dest` दोनों दिए गए values से match करते हैं।
+
+* क्योंकि `j` नहीं दिया गया है, हम उन row indices से संबंधित *सभी columns* सीधे लौटाते हैं।
+
+#### -- वे सभी rows subset करें जहाँ केवल पहली key column `origin` *"JFK"* से match करती है
+
+```{r}
+key(flights)
+
+flights[.("JFK")] ## or in this case simply flights["JFK"], for convenience
+```
+
+* क्योंकि हमने दूसरी key column `dest` के लिए कोई value नहीं दी, यह केवल पहली key column `origin` में *"JFK"* match करता है और सभी matching rows लौटाता है।
+
+#### -- वे सभी rows subset करें जहाँ केवल दूसरी key column `dest` *"MIA"* से match करती है
+
+```{r}
+flights[.(unique(origin), "MIA")]
+```
+
+#### यहाँ क्या हो रहा है?
+
+* [इसे](#multiple-key-point) फिर पढ़ें। दूसरी key column के लिए दी गई value *"MIA"* को `dest` key column में matching values *उन matching rows पर* ढूँढनी होती हैं जो पहली key column `origin` से मिली हैं। हम *पहले* आने वाले key columns की values skip नहीं कर सकते। इसलिए key column `origin` की *सभी* unique values देते हैं।
+
+* *"MIA"* अपने आप `unique(origin)` की length, जो *3* है, के अनुरूप recycle हो जाता है।
+
+## 2. Keys को `j` और `by` के साथ combine करना
+
+अब तक हमने एक ही concept देखा है -- `i` में *row indices* प्राप्त करना, लेकिन एक अलग method, `keys`, का उपयोग करके। इसलिए यह आश्चर्यजनक नहीं होना चाहिए कि `j` और `by` में वही सब किया जा सकता है जो पिछले vignettes में देखा था। कुछ examples से इसे highlight करेंगे।
+
+### a) `j` में select करना
+
+#### -- `origin = "LGA"` और `dest = "TPA"` के अनुरूप `arr_delay` column को *data.table* के रूप में लौटाएँ।
+
+```{r}
+key(flights)
+flights[.("LGA", "TPA"), .(arr_delay)]
+```
+
+* `origin == "LGA"` और `dest == "TPA"` से संबंधित *row indices* *key-based subset* का उपयोग करके प्राप्त होते हैं।
+
+* Row indices मिलने के बाद `j` को देखते हैं, जिसमें केवल `arr_delay` column चाहिए। इसलिए उन *row indices* के लिए `arr_delay` column को उसी तरह select करते हैं जैसा [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में देखा था।
+
+* हम `with = FALSE` का उपयोग करके भी result लौटा सकते थे।
+
+ ```r
+ flights[.("LGA", "TPA"), "arr_delay", with = FALSE]
+ ```
+
+### b) Chaining
+
+#### -- ऊपर प्राप्त result पर chaining का उपयोग करके column को decreasing order में व्यवस्थित करें।
+
+```{r}
+flights[.("LGA", "TPA"), .(arr_delay)][order(-arr_delay)]
+```
+
+### c) `j` में compute या *do* करना
+
+#### -- `origin = "LGA"` और `dest = "TPA"` के अनुरूप maximum arrival delay ज्ञात करें।
+
+```{r}
+flights[.("LGA", "TPA"), max(arr_delay)]
+```
+
+* हम verify कर सकते हैं कि result पिछले example की पहली value (486) के समान है।
+
+### d) `j` में `:=` का उपयोग करके by reference *sub-assign* करना
+
+यह example हम [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html) विगनेट में पहले देख चुके हैं। आइए `flights` *data.table* में उपलब्ध सभी `hours` देखें:
+
+```{r}
+# get all 'hours' in flights
+flights[, sort(unique(hour))]
+```
+
+हम देखते हैं कि डेटा में कुल `25` unique values हैं। *0* और *24* दोनों hours मौजूद प्रतीत होते हैं। आइए *24* को *0* से बदलें, लेकिन इस बार *key* का उपयोग करके।
+
+```{r}
+setkey(flights, hour)
+key(flights)
+flights[.(24), hour := 0L]
+key(flights)
+```
+
+* पहले `key` को `hour` पर set करते हैं। इससे `flights`, `hour` column के अनुसार reorder होता है और वह column `key` column के रूप में mark हो जाता है।
+
+* अब `.()` notation का उपयोग करके `hour` पर subset कर सकते हैं। हम *24* value के लिए subset करते हैं और संबंधित *row indices* प्राप्त करते हैं।
+
+* और उन row indices पर `key` column को value `0` से replace करते हैं।
+
+* क्योंकि हमने *key* column की values replace की हैं, *data.table* `flights` अब `hour` के अनुसार sorted नहीं है। इसलिए key automatically `NULL` set होकर हट जाती है।
+
+अब `hour` column में कोई *24* नहीं होना चाहिए।
+
+```{r}
+flights[, sort(unique(hour))]
+```
+
+### e) `by` का उपयोग करके aggregation
+
+पहले key को वापस `origin, dest` पर set करते हैं।
+
+```{r}
+setkey(flights, origin, dest)
+key(flights)
+```
+
+#### -- `origin = "JFK"` के अनुरूप प्रत्येक `month` के लिए maximum departure delay प्राप्त करें। Result को `month` के अनुसार order करें
+
+```{r}
+ans <- flights["JFK", max(dep_delay), keyby = month]
+head(ans)
+key(ans)
+```
+
+* हम *key* column `origin` पर subset करके *"JFK"* से संबंधित *row indices* प्राप्त करते हैं।
+
+* Row indices मिल जाने के बाद हमें केवल दो columns चाहिए - group करने के लिए `month` और प्रत्येक group का `max()` प्राप्त करने के लिए `dep_delay`। इसलिए *data.table* का query optimisation speed और memory efficiency के लिए `i` में मिले *row indices* से संबंधित केवल इन दो columns को subset करता है।
+
+* और उस subset पर *month* से group करके `max(dep_delay)` compute करते हैं।
+
+* हम `keyby` का उपयोग result को *month* से automatically key करने के लिए करते हैं। अब इसका अर्थ स्पष्ट है। Ordering के अलावा यह *month* को `key` column भी set करता है।
+
+## 3. अतिरिक्त arguments - `mult` और `nomatch`
+
+### a) *mult* argument
+
+`mult` argument का उपयोग करके प्रत्येक query के लिए चुन सकते हैं कि *सभी* matching rows लौटाई जाएँ, या केवल *पहली* अथवा *अंतिम*। Default value *"all"* है - यही अब तक देखा है।
+
+#### -- उन सभी rows में से केवल पहली matching row subset करें जहाँ `origin` *"JFK"* से और `dest` *"MIA"* से match करता है
+
+```{r}
+flights[.("JFK", "MIA"), mult = "first"]
+```
+
+#### -- उन सभी rows में से केवल अंतिम matching row subset करें जहाँ `origin` *"LGA", "JFK", "EWR"* से और `dest` *"XNA"* से match करता है
+
+```{r}
+flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last"]
+```
+
+* Query *"JFK", "XNA"* `flights` में किसी row से match नहीं करती, इसलिए `NA` लौटाती है।
+
+* एक बार फिर, दूसरी key column `dest` की query *"XNA"* को पहली key column `origin` की query की length के अनुरूप recycle किया जाता है, जिसकी length 3 है।
+
+### b) *nomatch* argument
+
+`nomatch` argument का उपयोग करके चुन सकते हैं कि match न होने वाली queries `NA` लौटाएँ या पूरी तरह skip हो जाएँ।
+
+#### -- पिछले example से, केवल तब सभी rows subset करें जब match हो
+
+```{r}
+flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last", nomatch = NULL]
+```
+
+* `nomatch` की default value `NA` है। `nomatch = NULL` set करने से no-match queries skip हो जाती हैं।
+
+* Query “JFK”, “XNA” flights में किसी row से match नहीं करती और इसलिए skip हो जाती है।
+
+## 4. Binary search बनाम vector scans
+
+अब तक हमने देखा कि keys को set और use करके subset कैसे किया जाता है। लेकिन इसका लाभ क्या है? उदाहरण के लिए, यह करने के बजाय:
+
+```r
+# key by origin,dest columns
+flights[.("JFK", "MIA")]
+```
+
+हम यह कर सकते थे:
+
+```r
+flights[origin == "JFK" & dest == "MIA"]
+```
+
+एक स्पष्ट लाभ छोटा syntax है। लेकिन उससे भी अधिक महत्वपूर्ण, *binary search आधारित subsets* **बहुत तेज़** होते हैं।
+
+समय के साथ `data.table` में नए optimizations जुड़ते रहे हैं और वर्तमान में बाद वाली call अपने आप *binary search* उपयोग करने के लिए optimized हो जाती है।
+Slow *vector scan* उपयोग करने के लिए key हटानी पड़ती है।
+
+```r
+setkey(flights, NULL)
+flights[origin == "JFK" & dest == "MIA"]
+```
+
+### a) Binary search approach का performance
+
+समझाने के लिए 20 million rows और तीन columns वाला sample *data.table* बनाते हैं और columns `x` और `y` पर key करते हैं।
+
+```{r}
+set.seed(2L)
+N = 2e7L
+DT = data.table(x = sample(letters, N, TRUE),
+ y = sample(1000L, N, TRUE),
+ val = runif(N))
+print(object.size(DT), units = "MiB")
+```
+
+`DT` लगभग 380MiB है। यह वास्तव में बहुत बड़ा नहीं है, लेकिन concept दिखाने के लिए पर्याप्त है।
+
+Introduction to data.table section में हमने जो देखा, उसके आधार पर वे rows जहाँ `x = "g"` और `y = 877` हैं, इस प्रकार subset की जा सकती हैं:
+
+```{r}
+key(DT)
+## (1) Usual way of subsetting - vector scan approach
+t1 <- system.time(ans1 <- DT[x == "g" & y == 877L])
+t1
+head(ans1)
+dim(ans1)
+```
+
+अब keys का उपयोग करके subset करने का प्रयास करते हैं।
+
+```{r}
+setkeyv(DT, c("x", "y"))
+key(DT)
+## (2) Subsetting using keys
+t2 <- system.time(ans2 <- DT[.("g", 877L)])
+t2
+head(ans2)
+dim(ans2)
+
+identical(ans1$val, ans2$val)
+```
+
+* Speed-up **~`{r} round(t1[3]/max(t2[3], .001))`x** है!
+
+### b) *data.table* को key करने से subsets इतने तेज़ क्यों होते हैं?
+
+इसे समझने के लिए पहले देखते हैं कि *vector scan approach* (method 1) क्या करता है।
+
+#### Vector scan approach
+
+* Column `x` में value *"g"* को सभी 20 million rows पर एक-एक करके खोजा जाता है। इससे size 20 million का *logical vector* बनता है, जिसमें `x` की value के अनुसार `TRUE, FALSE या NA` values होती हैं।
+
+* इसी प्रकार, column `y` में `877` को सभी 20 million rows पर एक-एक करके खोजा जाता है और एक अन्य logical vector में store किया जाता है।
+
+* Intermediate logical vectors पर element-wise `&` operations किए जाते हैं और वे सभी rows लौटती हैं जहाँ expression `TRUE` evaluate होती है।
+
+इसे हम *vector scan approach* कहते हैं। यह काफी inefficient है, विशेष रूप से बड़ी tables पर और repeated subsetting की स्थिति में, क्योंकि हर बार सभी rows scan करनी पड़ती हैं।
+
+अब binary search approach (method 2) देखते हैं। [Properties of key](#key-properties) से याद करें - *keys सेट करने से data.table key columns के अनुसार reorder होता है*। क्योंकि data sorted है, हमें *पूरे column की length scan नहीं करनी पड़ती*! इसके बजाय *binary search* का उपयोग करके value को `O(log n)` में खोज सकते हैं, जबकि *vector scan approach* में `O(n)` लगता है; यहाँ `n`, *data.table* की rows की संख्या है।
+
+#### Binary search approach
+
+यहाँ एक बहुत सरल illustration है। नीचे दिए (sorted) numbers पर विचार करें:
+
+```
+1, 5, 10, 19, 22, 23, 30
+```
+
+मान लें binary search का उपयोग करके value *1* की matching position ढूँढनी है। क्योंकि हमें पता है कि data *sorted* है, हम इस प्रकार आगे बढ़ेंगे:
+
+* बीच की value = 19 से शुरू करें। क्या 1 == 19? नहीं। 1 < 19।
+
+* क्योंकि खोजी जाने वाली value 19 से छोटी है, वह 19 से पहले कहीं होनी चाहिए। इसलिए >= 19 वाले शेष आधे हिस्से को discard कर सकते हैं।
+
+* अब set घटकर *1, 5, 10* रह गया। फिर middle value = 5 लें। क्या 1 == 5? नहीं। 1 < 5।
+
+* अब set घटकर *1* रह गया। क्या 1 == 1? हाँ। संबंधित index भी 1 है। और यही एकमात्र match है।
+
+इसके विपरीत vector scan approach को सभी values (यहाँ 7) scan करनी पड़तीं।
+
+हम देख सकते हैं कि प्रत्येक search के साथ searches की संख्या आधी होती जाती है। इसी कारण *binary search* आधारित subsets **अत्यंत तेज़** होते हैं। क्योंकि *data.tables* के प्रत्येक column की rows memory में contiguous locations पर रहती हैं, operations बहुत cache-efficient तरीके से होते हैं (यह भी *speed* बढ़ाता है)।
+
+इसके अतिरिक्त, क्योंकि matching row indices सीधे मिल जाते हैं और उन विशाल logical vectors (जिनकी length *data.table* की rows के बराबर होती है) को बनाने की आवश्यकता नहीं होती, यह काफी **memory efficient** भी है।
+
+## सारांश
+
+इस विगनेट में हमने *data.table* को key करके `i` में rows subset करने का एक और method सीखा। Keys set करने से *binary search* के माध्यम से बहुत तेज़ subsets किए जा सकते हैं। विशेष रूप से हमने देखा कि कैसे
+
+* *data.table* पर key set करें और key का उपयोग करके subset करें।
+
+* keys के माध्यम से subset करें, जो `i` में *row indices* प्राप्त करता है, लेकिन बहुत तेज़।
+
+* key-based subsets को `j` और `by` के साथ combine करें। ध्यान दें कि `j` और `by` operations पहले जैसे ही हैं।
+
+Key-based subsets **अत्यंत तेज़** होते हैं और विशेष रूप से तब उपयोगी हैं जब task में *repeated subsetting* शामिल हो। लेकिन key set करके *data.table* को physically reorder करना हमेशा desirable नहीं हो सकता। [अगले विगनेट (`vignette("datatable-secondary-indices-and-auto-indexing", package="data.table")`)](datatable-secondary-indices-and-auto-indexing.html) में हम इसे एक *नई* feature -- *secondary indexes* -- से address करेंगे।
+
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
diff --git a/vignettes/hi/datatable-programming.Rmd b/vignettes/hi/datatable-programming.Rmd
new file mode 100644
index 0000000000..749923562b
--- /dev/null
+++ b/vignettes/hi/datatable-programming.Rmd
@@ -0,0 +1,483 @@
+---
+title: "data.table पर Programming"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Programming on data.table}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r init, include = FALSE}
+require(data.table)
+litedown::reactor(comment = "# ")
+```
+
+## परिचय
+
+`data.table` ने अपने शुरुआती releases से ही `[.data.table` method को define करके `subset` और `with` (या `within`) functions के उपयोग को सक्षम किया। `subset` और `with` base R functions हैं जो code में repetition कम करने, readability बढ़ाने और user द्वारा type किए जाने वाले कुल characters की संख्या घटाने में उपयोगी हैं। यह functionality R की एक काफी अनोखी feature, *lazy evaluation*, के कारण संभव है। यह feature किसी function को उसके arguments को evaluate होने से पहले पकड़ने और उन्हें उस scope से अलग scope में evaluate करने की अनुमति देती है जहाँ से उन्हें call किया गया था। आइए `subset` function के उपयोग को दोहराकर देखें।
+
+```{r df_print, echo=FALSE}
+registerS3method("print", "data.frame", function(x, ...) {
+ base::print.data.frame(head(x, 2L), ...)
+ cat("...\n")
+ invisible(x)
+})
+.opts = options(
+ datatable.print.topn=2L,
+ datatable.print.nrows=20L
+)
+```
+
+```{r subset}
+subset(iris, Species == "setosa")
+```
+
+यहाँ `subset`, दूसरे argument को लेता है और उसे पहले argument के रूप में दिए गए `data.frame` के scope के भीतर evaluate करता है। इससे variables को बार-बार लिखने की आवश्यकता हटती है, errors की संभावना कम होती है और code अधिक readable बनता है।
+
+## समस्या का विवरण
+
+इस प्रकार के interface की समस्या यह है कि इसका उपयोग करने वाले code को आसानी से parameterize नहीं किया जा सकता। ऐसा इसलिए क्योंकि इन functions को दी गई expressions, evaluate होने से पहले substitute हो जाती हैं।
+
+### उदाहरण
+
+```{r subset_error, error=TRUE, purl=FALSE}
+my_subset = function(data, col, val) {
+ subset(data, col == val)
+}
+my_subset(iris, Species, "setosa")
+```
+
+### समस्या के संभावित approaches
+
+इस समस्या से निपटने के कई तरीके हैं।
+
+#### *lazy evaluation* से बचें
+
+सबसे आसान workaround है कि पहले ही *lazy evaluation* से बचा जाए और `df[["variable"]]` जैसे कम intuitive तथा अधिक error-prone approaches का उपयोग किया जाए।
+
+```{r subset_nolazy}
+my_subset = function(data, col, val) {
+ data[data[[col]] == val & !is.na(data[[col]]), ]
+}
+my_subset(iris, col = "Species", val = "setosa")
+```
+
+यहाँ हम `nrow(iris)` length का logical vector compute करते हैं, फिर यह vector `[.data.frame` के `i` argument को दिया जाता है ताकि सामान्य "logical vector"-आधारित subsetting हो सके। `subset()` के अनुरूप रहने के लिए, जो NAs भी drop करता है, हमें उन्हें पकड़ने के लिए `data[[col]]` का एक अतिरिक्त उपयोग शामिल करना पड़ता है। इस सरल उदाहरण में यह पर्याप्त है, लेकिन इसमें flexibility कम है, variables दोहराने पड़ते हैं और user को function interface बदलकर column name को unquoted symbol के बजाय character के रूप में pass करना पड़ता है। जिस expression को parameterize करना है वह जितनी complex होगी, यह approach उतना ही कम practical होगा।
+
+#### `parse` / `eval` का उपयोग
+
+यह method R के नए users को अक्सर पसंद आता है क्योंकि conceptually यह शायद सबसे सीधा लगता है। इसमें आवश्यक expression को string concatenation से बनाना, parse करना और फिर evaluate करना होता है।
+
+```{r subset_parse}
+my_subset = function(data, col, val) {
+ data = deparse(substitute(data))
+ col = deparse(substitute(col))
+ val = paste0("'", val, "'")
+ text = paste0("subset(", data, ", ", col, " == ", val, ")")
+ eval(parse(text = text)[[1L]])
+}
+my_subset(iris, Species, "setosa")
+```
+
+Function को दिए गए objects के actual names पकड़ने के लिए हमें `deparse(substitute(...))` का उपयोग करना पड़ता है, ताकि उन्हीं original names से `subset` function call बनाया जा सके। यद्यपि यह अपेक्षाकृत कम complexity में बहुत flexibility देता है, **`eval(parse(...))` का उपयोग टालना चाहिए**। मुख्य कारण हैं:
+
+- syntax validation का अभाव
+- [code injection की vulnerability](https://github.com/Rdatatable/data.table/issues/2655#issuecomment-376781159)
+- बेहतर alternatives का उपलब्ध होना
+
+R Project Core Developer Martin Machler ने [एक बार कहा था](https://stackoverflow.com/a/40164111/2490497):
+
+> क्षमा करें, लेकिन मुझे समझ नहीं आता कि इतने लोग यह क्यों सोचते हैं कि string कोई ऐसी चीज़ है जिसे evaluate किया जा सकता है। आपको वास्तव में अपनी सोच बदलनी होगी। एक तरफ strings और दूसरी तरफ expressions, calls, evaluation के बीच सभी connections भूल जाइए।
+संभवतः एकमात्र connection `parse(text = ....)` के माध्यम से है, और हर अच्छे R programmer को पता होना चाहिए कि expressions (या calls) बनाने का यह शायद ही कभी efficient या safe तरीका होता है। इसके बजाय `substitute()`, `quote()`, और संभवतः `do.call(substitute, ......)` के उपयोग की शक्ति के बारे में अधिक सीखें।
+
+#### Language पर computing
+
+ऊपर बताए गए functions, कुछ अन्य functions के साथ (जैसे `as.call`, `as.name`/`as.symbol`, `bquote`, और `eval`), *compute on the language* करने वाले functions के रूप में वर्गीकृत किए जा सकते हैं, क्योंकि ये _language_ objects (जैसे `call`, `name`/`symbol`) पर operate करते हैं।
+
+```{r subset_substitute}
+my_subset = function(data, col, val) {
+ eval(substitute(subset(data, col == val)))
+}
+my_subset(iris, Species, "setosa")
+```
+
+यहाँ हमने base R के `substitute` function का उपयोग करके call `subset(data, col == val)` को `subset(iris, Species == "setosa")` में बदला, जहाँ `data`, `col`, और `val` को उनके parent environment से original names (या values) से substitute किया गया। पिछले approaches की तुलना में इस method के लाभ स्पष्ट होने चाहिए। ध्यान दें कि हम language objects के level पर operate करते हैं और string manipulation का सहारा नहीं लेते, इसलिए इसे *computing on the language* कहते हैं। [R language manual](https://cran.r-project.org/doc/manuals/r-release/R-lang.html) में *Computing on the language* पर एक dedicated chapter है। यद्यपि *programming on data.table* के लिए इसे पढ़ना आवश्यक नहीं है, फिर भी R language की इस शक्तिशाली और विशिष्ट feature को बेहतर समझने के लिए हम इसे पढ़ने की सलाह देते हैं।
+
+#### Third-party packages का उपयोग
+
+ऐसे third-party packages उपलब्ध हैं जो base R की computing-on-the-language routines जैसा काम कर सकते हैं (`pryr`, `lazyeval` और `rlang`, कुछ उदाहरण हैं)।
+
+यद्यपि ये उपयोगी हो सकते हैं, यहाँ हम `data.table`-विशिष्ट approach पर चर्चा करेंगे।
+
+## data.table पर Programming
+
+अब जब हमने *lazy evaluation* उपयोग करने वाले code को parameterize करने का उचित तरीका स्थापित कर लिया है, तो इस विगनेट के मुख्य विषय, *programming on data.table*, पर आगे बढ़ सकते हैं।
+
+Version 1.15.0 से data.table, `[.data.table` के `i`, `j`, और `by` (या `keyby`) arguments को दी गई expressions को parameterize करने के लिए एक robust mechanism प्रदान करता है। यह base R के `substitute` function पर आधारित है और उसके interface की नकल करता है। यहाँ हम `substitute2` को base R के `substitute` का अधिक robust और user-friendly version के रूप में प्रस्तुत करते हैं। `base::substitute` और `data.table::substitute2` के बीच differences की पूरी list के लिए [`substitute2` manual](https://rdatatable.gitlab.io/data.table/library/data.table/html/substitute2.html) पढ़ें।
+
+### Variables और names को substitute करना
+
+मान लें हम ऐसा general function चाहते हैं जो दो arguments के sum पर किसी function को apply करे, जबकि उन arguments पर पहले ही दूसरा function apply किया जा चुका हो। Concrete example के रूप में, नीचे एक function है जो right triangle के legs की lengths ज्ञात होने पर hypotenuse की length compute करता है।
+
+${\displaystyle c = \sqrt{a^2 + b^2}}$
+
+```{r hypotenuse}
+square = function(x) x^2
+quote(
+ sqrt(square(a) + square(b))
+)
+```
+
+उद्देश्य यह है कि ऊपर दिए गए call का प्रत्येक name parameter के रूप में pass किया जा सके।
+
+```{r hypotenuse_substitute2}
+substitute2(
+ outer(inner(var1) + inner(var2)),
+ env = list(
+ outer = "sqrt",
+ inner = "square",
+ var1 = "a",
+ var2 = "b"
+ )
+)
+```
+
+Output में हम देख सकते हैं कि functions के names और उन functions को दिए गए variables के names दोनों replace हो गए हैं। सुविधा के लिए हमने `substitute2` उपयोग किया। इस सरल case में base R का `substitute` भी उपयोग हो सकता था, लेकिन उसके लिए `lapply(env, as.name)` का उपयोग करना पड़ता।
+
+अब `[.data.table` के भीतर substitution के लिए हमें `substitute2` function को explicitly call करने की आवश्यकता नहीं है। क्योंकि अब यह internally उपयोग हो रहा है, हमें केवल `env` argument देना है, ठीक उसी तरह जैसे ऊपर के example में `substitute2` function को दिया था। Substitution को `[.data.table` method के `i`, `j` और `by` (या `keyby`) arguments पर लागू किया जा सकता है। ध्यान दें कि `verbose` argument को `TRUE` सेट करके substitution के बाद expressions print की जा सकती हैं। Debugging के लिए यह बहुत उपयोगी है।
+
+Demonstration के लिए `iris` dataset उपयोग करते हैं। केवल example के रूप में मान लें कि हम sepal width और length को right triangle के legs मानते हुए `Sepal.Hypotenuse` compute करना चाहते हैं।
+
+```{r hypotenuse_datatable}
+DT = as.data.table(iris)
+
+str(
+ DT[, outer(inner(var1) + inner(var2)),
+ env = list(
+ outer = "sqrt",
+ inner = "square",
+ var1 = "Sepal.Length",
+ var2 = "Sepal.Width"
+ )]
+)
+
+# return as a data.table
+DT[, .(Species, var1, var2, out = outer(inner(var1) + inner(var2))),
+ env = list(
+ outer = "sqrt",
+ inner = "square",
+ var1 = "Sepal.Length",
+ var2 = "Sepal.Width",
+ out = "Sepal.Hypotenuse"
+ )]
+```
+
+अंतिम call में हमने एक और parameter, `out = "Sepal.Hypotenuse"`, जोड़ा जो output column का intended name बताता है। Base R के `substitute` के विपरीत, `substitute2` call arguments के names की substitution भी संभालता है।
+
+Substitution `i` और `by` (या `keyby`) पर भी काम करता है।
+
+```{r hypotenuse_datatable_i_j_by}
+DT[filter_col %in% filter_val,
+ .(var1, var2, out = outer(inner(var1) + inner(var2))),
+ by = by_col,
+ env = list(
+ outer = "sqrt",
+ inner = "square",
+ var1 = "Sepal.Length",
+ var2 = "Sepal.Width",
+ out = "Sepal.Hypotenuse",
+ filter_col = "Species",
+ filter_val = I(c("versicolor", "virginica")),
+ by_col = "Species"
+ )]
+```
+
+### Functions को substitute करना
+
+Expression में function name को कैसे substitute किया जाता है, इस पर एक छोटा clarification उपयोगी हो सकता है।
+ध्यान दें कि `outer="sqrt"` (string) और `outer=sqrt` (symbol) देना बहुत अलग है:
+```{r substitute_fun1, result='hide'}
+DT[, outer(Sepal.Length), env = list(outer="sqrt"), verbose=TRUE]
+#Argument 'j' after substitute: sqrt(Sepal.Length)
+## DT[, sqrt(Sepal.Length)]
+
+DT[, outer(Sepal.Length), env = list(outer=sqrt), verbose=TRUE]
+#Argument 'j' after substitute: .Primitive("sqrt")(Sepal.Length)
+## DT[, .Primitive("sqrt")(Sepal.Length)]
+```
+और यद्यपि `.Primitive("sqrt")(Sepal.Length)` अभी भी काम करता है, यह लगभग कभी desired form नहीं होता।
+
+और भी महत्वपूर्ण बात यह है कि यदि symbol form उपयोग करना है, तो उसे expression में सीधे उपयोग किया जा सकता है और करना चाहिए, क्योंकि substitution की आवश्यकता नहीं है।
+```{r substitute_fun2, result='hide'}
+DT[, sqrt(Sepal.Length)]
+```
+
+यदि substitute किए जाने वाले function name को namespace-qualified होना आवश्यक है, तो namespace और function name को expression के किसी भी अन्य symbol की तरह substitute किया जा सकता है:
+```r
+DT[, ns::fun(Sepal.Length), env = list(ns="base", fun="sqrt"), verbose=TRUE]
+#Argument 'j' after substitute: base::sqrt(Sepal.Length)
+## DT[, base::sqrt(Sepal.Length)]
+```
+
+### Variables और character values को substitute करना
+
+ऊपर के example में हमने `substitute2` की एक सुविधाजनक feature देखी: strings को names/symbols में automatic conversion। स्वाभाविक प्रश्न है: यदि हम वास्तव में किसी parameter को *character* value से substitute करना चाहें, ताकि base R `substitute` जैसा behaviour मिले, तो क्या करें? Automatic conversion से बचने के लिए elements को base R के `I()` call में wrap करने की सुविधा दी गई है। `I` function किसी object को *AsIs* mark करता है, जिससे उसके arguments character-to-symbol automatic conversion से बचते हैं। (अधिक details के लिए `?AsIs` documentation पढ़ें।) यदि पूरे `env` argument के लिए base R behaviour चाहिए, तो पूरे argument को `I()` में wrap करना बेहतर है। वैकल्पिक रूप से list के प्रत्येक element को अलग-अलग `I()` में wrap किया जा सकता है। नीचे दोनों cases देखते हैं।
+
+```{r rank}
+substitute( # base R behaviour
+ rank(input, ties.method = ties),
+ env = list(input = as.name("Sepal.Width"), ties = "first")
+)
+
+substitute2( # mimicking base R's "substitute" using "I"
+ rank(input, ties.method = ties),
+ env = I(list(input = as.name("Sepal.Width"), ties = "first"))
+)
+
+substitute2( # only particular elements of env are used "AsIs"
+ rank(input, ties.method = ties),
+ env = list(input = "Sepal.Width", ties = I("first"))
+)
+```
+
+ध्यान दें कि conversion प्रत्येक list element पर recursively काम करता है, जिसमें escape mechanism भी शामिल है।
+
+```{r substitute2_recursive}
+substitute2( # all are symbols
+ f(v1, v2),
+ list(v1 = "a", v2 = list("b", list("c", "d")))
+)
+substitute2( # 'a' and 'd' should stay as character
+ f(v1, v2),
+ list(v1 = I("a"), v2 = list("b", list("c", I("d"))))
+)
+```
+
+### मनमानी length वाली lists को substitute करना
+
+ऊपर का example आपके code को अधिक dynamic बनाने का एक सुंदर और शक्तिशाली तरीका दिखाता है। लेकिन developer को कई अधिक complex cases संभालने पड़ सकते हैं। एक common problem arbitrary length वाली arguments की list संभालना है।
+
+एक स्पष्ट use case यह हो सकता है कि `j` argument में `list` call inject करके `.SD` functionality की नकल की जाए।
+
+```{r splice_sd}
+cols = c("Sepal.Length", "Sepal.Width")
+DT[, .SD, .SDcols = cols]
+```
+
+`cols` parameter होने पर हम इसे `list` call में splice करना चाहेंगे, जिससे `j` argument नीचे दिए code जैसा दिखे।
+
+```{r splice_tobe}
+DT[, list(Sepal.Length, Sepal.Width)]
+```
+
+*Splicing* एक operation है जिसमें objects की list को call के arguments की sequence के रूप में expression में inline करना होता है।
+Base R में `cols` को `list` में splice करने के लिए `as.call(c(quote(list), lapply(cols, as.name)))` उपयोग किया जा सकता है। इसके अतिरिक्त, R 4.0.0 से `bquote` function में इस operation के लिए नया interface उपलब्ध है।
+
+data.table में हम इसे आसान बनाते हैं: objects की list को automatically _enlist_ करके उन objects के साथ list call में बदल दिया जाता है। इसका अर्थ है कि `env` list argument के भीतर कोई भी `list` object, list `call` में बदल जाएगा, जिससे इस use case के लिए API नीचे दिखाए अनुसार सरल हो जाती है।
+
+```{r splice_datatable}
+# this works
+DT[, j,
+ env = list(j = as.list(cols)),
+ verbose = TRUE]
+
+# this will not work
+#DT[, list(cols),
+# env = list(cols = cols)]
+```
+
+ऊपर के example की तरह `env` list argument के भीतर केवल list देने के बजाय `as.list` का call देना महत्वपूर्ण है।
+
+आइए _enlist_-ing को अधिक detail में देखें।
+
+```{r splice_enlist}
+DT[, j, # data.table automatically enlists nested lists into list calls
+ env = list(j = as.list(cols)),
+ verbose = TRUE]
+
+DT[, j, # turning the above 'j' list into a list call
+ env = list(j = quote(list(Sepal.Length, Sepal.Width))),
+ verbose = TRUE]
+
+DT[, j, # the same as above but accepts character vector
+ env = list(j = as.call(c(quote(list), lapply(cols, as.name)))),
+ verbose = TRUE]
+```
+
+अब symbols के लिए list call देने के बजाय symbols की list pass करने का प्रयास करते हैं। Automatic _enlist_-ing से बचने के लिए `I()` उपयोग करेंगे, लेकिन क्योंकि इससे character-to-symbol conversion भी बंद हो जाएगा, इसलिए `as.name` भी उपयोग करना होगा।
+
+```{r splice_not, error=TRUE, purl=FALSE}
+DT[, j, # list of symbols
+ env = I(list(j = lapply(cols, as.name))),
+ verbose = TRUE]
+
+DT[, j, # again the proper way, enlist list to list call automatically
+ env = list(j = as.list(cols)),
+ verbose = TRUE]
+```
+
+ध्यान दें कि दोनों expressions visually समान दिखने के बावजूद identical नहीं हैं।
+
+```{r splice_substitute2_not}
+str(substitute2(j, env = I(list(j = lapply(cols, as.name)))))
+
+str(substitute2(j, env = list(j = as.list(cols))))
+```
+
+इस विषय की अधिक detailed explanation के लिए [`substitute2` documentation](https://rdatatable.gitlab.io/data.table/library/data.table/html/substitute2.html) में examples देखें।
+
+### Complex query की substitution
+
+अधिक complex function के example के रूप में root mean square की calculation लेते हैं।
+
+${\displaystyle x_{\text{RMS}}={\sqrt{{\frac{1}{n}}\left(x_{1}^{2}+x_{2}^{2}+\cdots +x_{n}^{2}\right)}}}$
+
+यह input में arbitrary number of variables लेता है, लेकिन अब हम arguments की list को केवल list call में *splice* नहीं कर सकते क्योंकि प्रत्येक argument को `square` call में wrap करना आवश्यक है। इस case में data.table के automatic _enlist_ पर निर्भर रहने के बजाय हमें *splice* manually करना होगा।
+
+पहले हमें प्रत्येक variable के लिए `square` function के calls construct करने होंगे (`inner_calls` देखें)। फिर calls की list को एक single call में reduce करना होगा, जिसमें nested `+` calls की sequence हो (`add_calls` देखें)। अंत में constructed call को surrounding expression में substitute करना होगा (`rms` देखें)।
+
+```{r complex}
+outer = "sqrt"
+inner = "square"
+vars = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width")
+
+syms = lapply(vars, as.name)
+to_inner_call = function(var, fun) call(fun, var)
+inner_calls = lapply(syms, to_inner_call, inner)
+print(inner_calls)
+
+to_add_call = function(x, y) call("+", x, y)
+add_calls = Reduce(to_add_call, inner_calls)
+print(add_calls)
+
+rms = substitute2(
+ expr = outer((add_calls) / len),
+ env = list(
+ outer = outer,
+ add_calls = add_calls,
+ len = length(vars)
+ )
+)
+print(rms)
+
+str(
+ DT[, j, env = list(j = rms)]
+)
+
+# same, but skipping last substitute2 call and using add_calls directly
+str(
+ DT[, outer((add_calls) / len),
+ env = list(
+ outer = outer,
+ add_calls = add_calls,
+ len = length(vars)
+ )]
+)
+
+# return as data.table
+j = substitute2(j, list(j = as.list(setNames(nm = c(vars, "Species", "rms")))))
+j[["rms"]] = rms
+print(j)
+DT[, j, env = list(j = j)]
+
+# alternatively
+j = as.call(c(
+ quote(list),
+ lapply(setNames(nm = vars), as.name),
+ list(Species = as.name("Species")),
+ list(rms = rms)
+))
+print(j)
+DT[, j, env = list(j = j)]
+```
+
+### सामान्य गलतियाँ
+
+किसी object को pass करने और उस object की ओर संकेत करने वाला name pass करने के बीच अंतर समझना महत्वपूर्ण है। नीचे के examples का verbose output देखें।
+
+```{r obj_vs_objname}
+DT[, fun(Petal.Width), env = list(fun = mean), verbose=TRUE]
+DT[, fun(Petal.Width), env = list(fun = "mean"), verbose=TRUE]
+```
+
+Users सामान्यतः actual function body insert करने के बजाय function name substitute करना चाहेंगे। इसलिए दूसरा approach (character string pass करना) अक्सर बेहतर होता है।
+
+`env` interface के काम करने पर कोई संदेह हो तो expressions internally कैसे resolve होती हैं यह देखने के लिए `verbose = TRUE` सेट करें।
+
+### किसी अन्य function के भीतर से `env` argument का उपयोग
+
+यह design decision था कि `env` argument _Standard Evaluation_ (SE) rules follow करेगा, अर्थात `env` को दी गई values अपने original scope में as-is evaluate होंगी। इस विषय पर अधिक जानकारी के लिए [R Language manual: Computing on the language](https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Computing-on-the-language) देखें। परिणामस्वरूप, **किसी function से `env` argument उपयोग करने के लिए किसी special handling की आवश्यकता नहीं है**। इसका अर्थ यह भी है कि `list()` का `.()` alias, जैसे `env = .(.col="Petal.Length")`, काम नहीं करेगा; इसके बजाय `env = list(.col="Petal.Length")` उपयोग करें।
+
+```{r env_se}
+fun = function(x, col.mean) {
+ stopifnot(is.character(col.mean), is.data.table(x))
+ x[, .(col_avg = mean(.col)), env = list(.col = col.mean)]
+}
+fun(DT, col.mean="Petal.Length")
+```
+
+यदि outer function स्वयं NSE (Non-Standard Evaluation) rules follow करता है, तो उसे language objects को उसी तरह resolve करना होगा जैसे अपने arguments किसी अन्य SE function को देते समय करता है।
+
+```{r env_nse}
+fun = function(x, col.mean) {
+ col.mean = substitute(col.mean)
+ stopifnot(is.name(col.mean), is.data.table(x))
+ x[, .(col_avg = mean(.col)), env = list(.col = col.mean)]
+}
+fun(DT, col.mean=Petal.Length)
+```
+
+## Retired interfaces
+
+`[.data.table` में variable substitution या quoted expressions pass करने के लिए अन्य mechanisms भी उपयोग किए जा सकते हैं। इनमें `get` और `mget` शामिल हैं, जो names को strings के रूप में देकर variables को inline inject करते हैं, तथा `eval`, जो `[.data.table` को बताता है कि किसी argument में दी गई expression quoted expression है और उसे अलग तरह से handle करना चाहिए। अब इन interfaces को retired माना जाना चाहिए और हम इनके बजाय नया `env` argument उपयोग करने की सलाह देते हैं।
+
+### `get`
+
+```{r old_get}
+v1 = "Petal.Width"
+v2 = "Sepal.Width"
+
+DT[, .(total = sum(get(v1), get(v2)))]
+
+DT[, .(total = sum(v1, v2)),
+ env = list(v1 = v1, v2 = v2)]
+```
+
+### `mget`
+
+```{r old_mget}
+v = c("Petal.Width", "Sepal.Width")
+
+DT[, lapply(mget(v), mean)]
+
+DT[, lapply(v, mean),
+ env = list(v = as.list(v))]
+
+DT[, lapply(v, mean),
+ env = list(v = as.list(setNames(nm = v)))]
+```
+
+### `eval`
+
+`eval` function उपयोग करने के बजाय quoted expression को `env` argument के किसी element में दिया जा सकता है; तब अतिरिक्त `eval` call की आवश्यकता नहीं होती।
+
+```{r old_eval}
+cl = quote(
+ .(Petal.Width = mean(Petal.Width), Sepal.Width = mean(Sepal.Width))
+)
+
+DT[, eval(cl)]
+
+DT[, cl, env = list(cl = cl)]
+```
+
+```{r cleanup, echo=FALSE}
+options(.opts)
+registerS3method("print", "data.frame", base::print.data.frame)
+```
diff --git a/vignettes/hi/datatable-reference-semantics.Rmd b/vignettes/hi/datatable-reference-semantics.Rmd
new file mode 100644
index 0000000000..ce94d2aecf
--- /dev/null
+++ b/vignettes/hi/datatable-reference-semantics.Rmd
@@ -0,0 +1,444 @@
+---
+title: "रेफरेंस सेमांटिक्स"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Reference semantics}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+यह विगनेट *data.table* की reference semantics पर चर्चा करता है, जो किसी *data.table* के columns को *by reference add/update/delete* करने और उन्हें `i` तथा `by` के साथ combine करने की अनुमति देती है। यह उन पाठकों के लिए है जो पहले से *data.table* syntax, उसके general form, `i` में rows subset करने, columns पर select और compute करने तथा group द्वारा aggregations करने से परिचित हैं। यदि आप इन concepts से परिचित नहीं हैं, तो पहले [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट पढ़ें।
+
+***
+
+## डेटा {#data}
+
+हम वही `flights` डेटा उपयोग करेंगे जो [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में उपयोग किया गया है।
+
+```{r, echo = FALSE}
+options(width = 100L)
+```
+
+```{r}
+flights <- fread("flights14.csv")
+flights
+dim(flights)
+```
+
+## परिचय
+
+इस विगनेट में हम
+
+1. पहले reference semantics पर संक्षेप में चर्चा करेंगे और `:=` operator के उपयोग के दो अलग-अलग forms देखेंगे
+
+2. फिर देखेंगे कि `:=` operator का उपयोग करके `j` में columns को *by reference add/update/delete* कैसे किया जा सकता है और इसे `i` तथा `by` के साथ कैसे combine करें।
+
+3. और अंत में `:=` को उसके *side-effect* के लिए उपयोग करना तथा `copy()` का उपयोग करके उन side effects से बचना देखेंगे।
+
+## 1. Reference semantics
+
+पिछले विगनेट में अब तक देखे गए सभी operations ने एक नया dataset बनाया था। अब हम देखेंगे कि original data में नए column(s) को कैसे *add* करें, existing column(s) को कैसे *update* या *delete* करें।
+
+### a) पृष्ठभूमि
+
+*Reference semantics* देखने से पहले नीचे दिए गए *data.frame* पर विचार करें:
+
+```{r}
+DF = data.frame(ID = c("b","b","b","a","a","c"), a = 1:6, b = 7:12, c = 13:18)
+DF
+```
+
+जब हमने यह किया:
+
+```r
+DF$c <- 18:13 # (1) -- replace entire column
+# or
+DF$c[DF$ID == "b"] <- 15:13 # (2) -- subassign in column 'c'
+```
+
+`R < 3.1` के versions में (1) और (2) दोनों के कारण पूरे data.frame की deep copy बनती थी। [यह एक से अधिक बार copy करता था](https://stackoverflow.com/q/23898969/559784)। इन अनावश्यक copies से बचकर performance सुधारने के लिए *data.table* ने [R में उपलब्ध लेकिन उपयोग न किए गए `:=` operator](https://stackoverflow.com/q/7033106/559784) का उपयोग किया।
+
+`R v3.1` में बड़े performance improvements किए गए, जिसके परिणामस्वरूप (1) के लिए केवल *shallow* copy बनती है, *deep* copy नहीं। हालाँकि (2) के लिए `R v3.1+` में भी पूरा column *deep* copy होता है। इसका अर्थ है कि एक ही query में जितने अधिक columns पर subassign किया जाएगा, R उतनी ही अधिक *deep* copies बनाएगा।
+
+#### *shallow* बनाम *deep* copy
+
+*Shallow* copy केवल column pointers के vector की copy होती है (जो *data.frame* या *data.table* के columns से संबंधित होते हैं)। Actual data को memory में भौतिक रूप से copy नहीं किया जाता।
+
+इसके विपरीत, *deep* copy पूरे data को memory में किसी दूसरे स्थान पर copy करती है।
+
+`i` का उपयोग करके *data.table* subset करने पर (जैसे `DT[1:10]`) *deep* copy बनती है। लेकिन जब `i` नहीं दिया जाता या `TRUE` के बराबर होता है, तब *shallow* copy बनती है।
+
+#
+*data.table* के `:=` operator के साथ (1) और (2) दोनों में, आपके उपयोग किए जा रहे R version से स्वतंत्र रूप से, कोई copy नहीं बनती। ऐसा इसलिए क्योंकि `:=` operator *data.table* columns को *in-place* (by reference) update करता है।
+
+### b) `:=` operator
+
+इसे `j` में दो तरीकों से उपयोग किया जा सकता है:
+
+(a) `LHS := RHS` form
+
+```r
+DT[, c("colA", "colB", ...) := list(valA, valB, ...)]
+
+# when you have only one column to assign to you
+# can drop the quotes and list(), for convenience
+DT[, colA := valA]
+```
+
+(b) Functional form
+
+```r
+DT[, `:=`(colA = valA, # valA is assigned to colA
+ colB = valB, # valB is assigned to colB
+ ...
+)]
+```
+
+ध्यान दें कि ऊपर का code केवल यह समझाता है कि `:=` का उपयोग कैसे किया जा सकता है। ये working examples नहीं हैं। अगले section से हम इसे `flights` *data.table* पर उपयोग करना शुरू करेंगे।
+
+#
+
+* (a) में `LHS` column names का character vector लेता है और `RHS` *values की list* लेता है। `RHS` को केवल `list` होना चाहिए, चाहे वह किसी भी तरह generate की गई हो (जैसे `lapply()`, `list()`, `mget()`, `mapply()` आदि का उपयोग करके)। यह form आमतौर पर program करना आसान होता है और विशेष रूप से तब उपयोगी है जब आपको पहले से पता न हो कि किन columns को values assign करनी हैं।
+
+* दूसरी ओर, यदि आप बाद के लिए कुछ comments लिखना चाहते हैं तो (b) उपयोगी है।
+
+* Result *invisibly* लौटाया जाता है।
+
+* क्योंकि `:=`, `j` में उपलब्ध है, हम इसे `i` और `by` operations के साथ उसी तरह combine कर सकते हैं जैसे पिछले विगनेट में aggregation operations के साथ किया था।
+
+#
+
+ऊपर दिखाए गए `:=` के दोनों forms में ध्यान दें कि हम result को वापस किसी variable को assign नहीं करते। इसकी आवश्यकता ही नहीं है। Input *data.table* by reference modify होता है। उदाहरणों से समझते हैं कि इसका क्या अर्थ है।
+
+बाकी विगनेट में हम `flights` *data.table* के साथ काम करेंगे।
+
+## 2. Columns को *by reference* add/update/delete करना
+
+### a) Columns को by reference जोड़ना {#ref-j}
+
+#### -- `flights` *data.table* में प्रत्येक flight के *speed* और *total delay* columns कैसे जोड़ें?
+
+```{r}
+flights[, `:=`(speed = distance / (air_time/60), # speed in mph (mi/h)
+ delay = arr_delay + dep_delay)] # delay in minutes
+head(flights)
+
+## alternatively, using the 'LHS := RHS' form
+# flights[, c("speed", "delay") := list(distance/(air_time/60), arr_delay + dep_delay)]
+```
+
+#### ध्यान दें कि
+
+* हमें result को वापस `flights` को assign नहीं करना पड़ा।
+
+* `flights` *data.table* में अब दो नए जोड़े गए columns हैं। इसी को हम *by reference जोड़ना* कहते हैं।
+
+* हमने functional form का उपयोग किया ताकि computation क्या करता है यह समझाने के लिए साथ में comments जोड़ सकें। आप `LHS := RHS` form भी देख सकते हैं (commented)।
+
+### b) Columns की कुछ rows को by reference update करना - *sub-assign* by reference {#ref-i-j}
+
+आइए `flights` *data.table* में उपलब्ध सभी `hours` देखें:
+
+```{r}
+# get all 'hours' in flights
+flights[, sort(unique(hour))]
+```
+
+हम देखते हैं कि डेटा में कुल `25` unique values हैं। *0* और *24* दोनों घंटे मौजूद प्रतीत होते हैं। आइए *24* को *0* से बदलें।
+
+#### -- उन rows में जहाँ `hour == 24`, value को `0` से बदलें
+
+```{r}
+# subassign by reference
+flights[hour == 24L, hour := 0L]
+```
+
+* हम `j` में `:=` के साथ `i` का ठीक उसी तरह उपयोग कर सकते हैं जैसा [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में पहले देख चुके हैं।
+
+* Column `hour` को केवल उन *row indices* पर `0` से बदला जाता है जहाँ `i` में दी गई condition `hour == 24L`, `TRUE` evaluate होती है।
+
+* `:=` result invisibly लौटाता है। कभी-कभी assignment के बाद result देखना आवश्यक हो सकता है। इसके लिए query के अंत में खाली `[]` जोड़ सकते हैं, जैसा नीचे दिखाया गया है:
+
+ ```{r}
+ flights[hour == 24L, hour := 0L][]
+ ```
+
+#
+सत्यापित करने के लिए सभी `hours` देखते हैं।
+
+```{r}
+# check again for '24'
+flights[, sort(unique(hour))]
+```
+
+#### अभ्यास: {#update-by-reference-question}
+
+`flights[hour == 24L, hour := 0L]` और `flights[hour == 24L][, hour := 0L]` में क्या अंतर है? संकेत: यदि आप बाद में result का उपयोग करना चाहते हैं तो दूसरे form को assignment (`<-`) की आवश्यकता होगी।
+
+यदि आप इसे समझ नहीं पा रहे हैं, तो `?":="` के `Note` section को देखें।
+
+### c) Column को by reference delete करना
+
+#### -- `delay` column हटाएँ
+
+```{r}
+flights[, c("delay") := NULL]
+head(flights)
+
+## or using the functional form
+# flights[, `:=`(delay = NULL)]
+```
+
+#### {#delete-convenience}
+
+* किसी column को `NULL` assign करने से वह column *delete* हो जाता है। और यह *तुरंत* होता है।
+
+* हम `LHS` में names के बजाय column numbers भी दे सकते हैं, हालाँकि अच्छे programming practice के लिए column names उपयोग करना बेहतर है।
+
+* जब केवल एक column delete करना हो, तो सुविधा के लिए `c()` और double quotes हटाकर column name को सीधे *unquoted* उपयोग कर सकते हैं। अर्थात:
+
+ ```r
+ flights[, delay := NULL]
+ ```
+
+ ऊपर के code के बराबर है।
+
+### d) `by` द्वारा grouping के साथ `:=` {#ref-j-by}
+
+[Section 2b](#ref-i-j) में हम `:=` के साथ `i` का उपयोग पहले ही देख चुके हैं। अब देखते हैं कि `:=` को `by` के साथ कैसे उपयोग करें।
+
+#### -- ऐसा नया column कैसे जोड़ें जिसमें प्रत्येक `orig,dest` pair के लिए maximum speed हो?
+
+```{r}
+flights[, max_speed := max(speed), by = .(origin, dest)]
+head(flights)
+```
+
+* हम `:=` operator का उपयोग करके by reference नया column `max_speed` जोड़ते हैं।
+
+* Group करने के लिए columns उसी तरह देते हैं जैसा *Introduction to data.table* विगनेट में दिखाया गया है। प्रत्येक group के लिए `max(speed)` compute होता है, जो single value लौटाता है। उस value को group की length के अनुरूप recycle किया जाता है। फिर से, कोई copies नहीं बनतीं। `flights` *data.table* *in-place* modify होता है।
+
+* हम `by` को *character vector* भी दे सकते थे, जैसा [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में देखा था, उदाहरण के लिए `by = c("origin", "dest")`।
+
+#### Zero-length RHS और `by` पर टिप्पणी
+
+* यदि `:=` assignment का `RHS` zero-length vector evaluate होता है, तो सामान्यतः error उठती है। लेकिन जब `:=` को `by` के साथ उपयोग किया जाता है, तो किसी group के लिए zero-length result उस group के लिए no-op माना जाता है और error नहीं फेंकी जाती। इससे grouped operations तब भी जारी रह सकते हैं जब कुछ groups कोई result उत्पन्न न करें।
+
+#
+
+### e) Multiple columns और `:=`
+
+#### -- `.SD` का उपयोग करके प्रत्येक month के लिए `dep_delay` और `arr_delay` का `max()` compute करने वाले दो और columns कैसे जोड़ें?
+
+```{r}
+in_cols = c("dep_delay", "arr_delay")
+out_cols = c("max_dep_delay", "max_arr_delay")
+flights[, c(out_cols) := lapply(.SD, max), by = month, .SDcols = in_cols]
+head(flights)
+```
+
+* हम `LHS := RHS` form का उपयोग करते हैं। Input column names और जोड़ने वाले नए columns को अलग variables में store करते हैं और उन्हें `.SDcols` तथा `LHS` में देते हैं (बेहतर readability के लिए)।
+
+* ध्यान दें कि [Section 2c](#delete-convenience) में समझाए अनुसार, एक ही column होने पर हम बिना quote किए column names के साथ by-reference assignment की अनुमति देते हैं। इसलिए हम `out_cols := lapply(.SD, max)` नहीं कर सकते। इससे `out_cols` नाम का एक नया column जुड़ जाएगा। इसके बजाय हमें `c(out_cols)` या केवल `(out_cols)` करना चाहिए। Variable name को `(` में wrap करना दोनों cases के बीच अंतर करने के लिए पर्याप्त है।
+
+* `LHS := RHS` form हमें multiple columns पर operation करने देता है। RHS में `.SDcols` में निर्दिष्ट columns पर `max` compute करने के लिए हम base function `lapply()` को `.SD` के साथ उसी तरह उपयोग करते हैं जैसा [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में पहले देखा था। यह दो elements की list लौटाता है, जिसमें प्रत्येक group के लिए `dep_delay` और `arr_delay` के maximum values होते हैं।
+
+#
+अगले section पर जाने से पहले नए बनाए गए columns `speed`, `max_speed`, `max_dep_delay` और `max_arr_delay` को साफ़ कर देते हैं।
+
+```{r}
+# RHS gets automatically recycled to length of LHS
+flights[, c("speed", "max_speed", "max_dep_delay", "max_arr_delay") := NULL]
+head(flights)
+```
+
+#### -- `.SD` का उपयोग करके multiple existing columns को in place कैसे update करें?
+
+```{r}
+flights[, names(.SD) := lapply(.SD, as.factor), .SDcols = is.character]
+```
+आइए फिर से साफ़ करें और हमारे नए बनाए गए factor columns को वापस character columns में बदलें। इस बार हम `.SDcols` की उस क्षमता का उपयोग करेंगे जिसमें एक function देकर तय किया जाता है कि कौन से columns शामिल हों। यहाँ `is.factor()` वे columns लौटाएगा जो factors हैं। **S**ubset of the **D**ata के बारे में अधिक जानकारी के लिए [SD Usage vignette](https://cran.r-project.org/package=data.table/vignettes/datatable-sd-usage.html) भी देखें।
+
+कभी-कभी उन columns का track रखना भी उपयोगी होता है जिन्हें हम transform करते हैं। इस तरह, conversion के बाद भी हम उन specific columns को reference कर सकते हैं जिन्हें update किया गया था।
+```{r}
+factor_cols <- sapply(flights, is.factor)
+flights[, names(.SD) := lapply(.SD, as.character), .SDcols = factor_cols]
+str(flights[, ..factor_cols])
+```
+
+#### {.bs-callout .bs-callout-info}
+
+* हम `LHS` पर `names(.SD)` के बजाय `(factor_cols)` भी उपयोग कर सकते थे।
+
+## 3. `:=` और `copy()`
+
+`:=` input object को by reference modify करता है। अब तक चर्चा की गई features के अलावा, कभी-कभी हम update-by-reference feature को उसके side effect के लिए उपयोग करना चाहते हैं। और कभी original object modify करना उचित नहीं होता; ऐसी स्थिति में हम `copy()` function का उपयोग कर सकते हैं, जैसा अभी देखेंगे।
+
+### a) `:=` का उसके side effect के लिए उपयोग
+
+मान लें हम ऐसा function बनाना चाहते हैं जो प्रत्येक month के लिए *maximum speed* लौटाए। साथ ही हम *flights* में `speed` column भी जोड़ना चाहते हैं। हम एक simple function इस प्रकार लिख सकते हैं:
+
+```{r}
+foo <- function(DT) {
+ DT[, speed := distance / (air_time/60)]
+ DT[, .(max_speed = max(speed)), by = month]
+}
+ans = foo(flights)
+head(flights)
+head(ans)
+```
+
+* ध्यान दें कि नया column `speed`, `flights` *data.table* में जुड़ गया है। ऐसा इसलिए क्योंकि `:=` operations by reference करता है। चूँकि `DT` (function argument) और `flights` memory में उसी object को refer करते हैं, इसलिए `DT` को modify करने का असर `flights` पर भी दिखाई देता है।
+
+* और `ans` में प्रत्येक month के लिए maximum speed है।
+
+### b) `copy()` function
+
+पिछले section में हमने `:=` को उसके side effect के लिए उपयोग किया। लेकिन निश्चित रूप से यह हमेशा वांछनीय नहीं होता। कभी हम किसी *data.table* object को function में pass करना चाहते हैं और `:=` operator उपयोग करना चाहते हैं, लेकिन original object को update *नहीं* करना चाहते। इसे `copy()` function से किया जा सकता है।
+
+`copy()` function input object की *deep* copy बनाता है, इसलिए copied object पर बाद में किए गए किसी भी by-reference update operation का original object पर कोई प्रभाव नहीं पड़ता।
+
+#
+
+तीन विशेष स्थितियाँ हैं जहाँ `copy()` function आवश्यक है:
+
+1. पिछले point में देखी स्थिति के विपरीत, हम function को दिए गए input data.table को *by reference* modify नहीं करना चाह सकते। उदाहरण के लिए पिछले section वाला task लेते हैं, लेकिन इस बार `flights` को by reference modify नहीं करना चाहते।
+
+ पहले पिछले section में बनाए गए `speed` column को delete करते हैं।
+
+ ```{r}
+ flights[, speed := NULL]
+ ```
+ अब task इस प्रकार पूरा किया जा सकता है:
+
+ ```{r}
+ foo <- function(DT) {
+ DT <- copy(DT) ## deep copy
+ DT[, speed := distance / (air_time/60)] ## doesn't affect 'flights'
+ DT[, .(max_speed = max(speed)), by = month]
+ }
+ ans <- foo(flights)
+ head(flights)
+ head(ans)
+ ```
+
+* `copy()` function के उपयोग से `flights` *data.table* by reference update नहीं हुआ। इसमें `speed` column नहीं है।
+
+* और `ans` में प्रत्येक month से संबंधित maximum speed है।
+
+हालाँकि इस functionality को *deep* copy के बजाय *shallow* copy करके और बेहतर किया जा सकता है। वास्तव में हम [`v1.9.8` के लिए यह functionality प्रदान करना चाहते हैं](https://github.com/Rdatatable/data.table/issues/617)। *data.table design* विगनेट में हम इस पर फिर बात करेंगे।
+
+#
+
+2. जब हम column names को किसी variable में store करते हैं, जैसे `DT_n = names(DT)`, और फिर column(s) को *by reference add/update/delete* करते हैं, तो `DT_n` भी modify हो जाएगा, जब तक हम `copy(names(DT))` का उपयोग न करें।
+
+ ```{r}
+ DT = data.table(x = 1L, y = 2L)
+ DT_n = names(DT)
+ DT_n
+
+ ## add a new column by reference
+ DT[, z := 3L]
+
+ ## DT_n also gets updated
+ DT_n
+
+ ## use `copy()`
+ DT_n = copy(names(DT))
+ DT[, w := 4L]
+
+ ## DT_n doesn't get updated
+ DT_n
+ ```
+
+3. जब दो names एक ही *data.table* को refer करते हैं (जैसे `DT2 = DT1`), तो उनके columns memory में *shared* होते हैं। इसलिए `.ROW := NULL` से by-reference row deletion दोनों names को एक साथ प्रभावित करता है:
+
+ ```{r}
+ DT1 = data.table(i = 1:10)
+ DT2 = DT1 ## shallow copy; same columns in memory
+
+ DT2[i > 5, .ROW := NULL]
+ nrow(DT1) ## 5 -- DT1 is modified too
+ ```
+
+ पहले एक independent object प्राप्त करने के लिए `copy()` का उपयोग करें:
+
+ ```{r}
+ DT1 = data.table(i = 1:10)
+ DT2 = copy(DT1) ## deep copy; independent object
+
+ DT2[i > 5, .ROW := NULL]
+ nrow(DT1) ## 10 -- unchanged
+ nrow(DT2) ## 5
+ ```
+
+### c) Columns चुनना: `$` / `[[...]]` बनाम `[, col]`
+
+जब आप single column को vector के रूप में extract करते हैं, तो standard R methods ($ और [[...]]) तथा data.table की j expression के बीच एक subtle लेकिन महत्वपूर्ण अंतर है। `DT$col` और `DT[['col']]` column का reference लौटा सकते हैं, जबकि `DT[, col]` हमेशा copy लौटाता है।
+
+एक छोटा उदाहरण:
+```{r}
+DT = data.table(a = 1:3)
+
+# three ways to get the column
+x_ref = DT$a # may be a reference
+y_cpy = DT[, a] # always a copy
+z_cpy = copy(DT$a) # forced copy
+
+# modify DT by reference
+DT[, a := a + 10L]
+
+# observe results
+x_ref # may show 11 12 13
+y_cpy # 1 2 3
+z_cpy # 1 2 3
+```
+
+Single column को vector के रूप में select करते समय याद रखें:
+- `DT[, mycol]` अधिक सुरक्षित है क्योंकि यह हमेशा एक नई, independent copy लौटाता है।
+- `DT$mycol` तेज़ है लेकिन reference लौटा सकता है। Independence सुनिश्चित करने के लिए `copy(DT$mycol)` का उपयोग करें।
+
+### d) Side effects और testing
+
+क्योंकि `:=` अपने input को in-place modify करता है, changes बने रहते हैं। Internal `test()` function से किसी expression को multiple optimization levels पर चलाते समय यह महत्वपूर्ण है: प्रत्येक evaluation को उसी data से शुरू होना चाहिए। `test()` को दी गई expression के अंदर `copy()` का उपयोग करें।
+
+```r
+DT = data.table(a = 1L)
+# Run the expression with optimization levels 0, 1, 2, and 3.
+# copy(DT) gives every evaluation an independent input.
+test(1, copy(DT)[, a := a + 1L][], data.table(a = 2L), optimize = 0:3)
+```
+
+`copy(DT)` के बिना, प्रत्येक optimization level अगले evaluation से पहले `DT` को modify कर देगा।
+
+## सारांश
+
+#### `:=` operator
+
+* इसका उपयोग columns को by reference *add/update/delete* करने के लिए होता है।
+
+* हमने यह भी देखा कि `:=` को `i` और `by` के साथ उसी तरह कैसे उपयोग करें जैसा [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में देखा था। उसी तरह हम `keyby` उपयोग कर सकते हैं, operations को chain कर सकते हैं और `by` को expressions दे सकते हैं। Syntax *consistent* है।
+
+* हम `:=` को उसके side effect के लिए उपयोग कर सकते हैं, या by reference update करते समय original object modify न हो इसके लिए `copy()` उपयोग कर सकते हैं।
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
+
+#
+
+अब तक हमने `j` में बहुत कुछ देखा है, उसे `by` के साथ combine करना और `i` का थोड़ा उपयोग भी देखा है। [अगले विगनेट (`vignette("datatable-keys-fast-subset", package="data.table")`)](datatable-keys-fast-subset.html) में अपना ध्यान फिर `i` पर केंद्रित करेंगे और *data.tables को key करके* *बहुत तेज़ subsets* करेंगे।
+
+***
diff --git a/vignettes/hi/datatable-reshape.Rmd b/vignettes/hi/datatable-reshape.Rmd
new file mode 100644
index 0000000000..4aa7c2ee61
--- /dev/null
+++ b/vignettes/hi/datatable-reshape.Rmd
@@ -0,0 +1,323 @@
+---
+title: "data.tables का कुशल reshaping"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Efficient reshaping using data.tables}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+यह विगनेट *data.tables* के लिए reshaping functions `melt` (wide से long) और `dcast` (long से wide) के default उपयोग के साथ-साथ `v1.9.6` से उपलब्ध *multiple columns* पर melting और casting की **नई विस्तारित functionalities** पर चर्चा करता है।
+
+***
+
+```{r, echo = FALSE}
+options(width = 100L)
+```
+
+## डेटा
+
+हम datasets को सीधे संबंधित sections के भीतर load करेंगे।
+
+## परिचय
+
+`data.table`s के लिए `melt` और `dcast` functions क्रमशः wide-to-long और long-to-wide reshaping के लिए हैं; इनके implementations विशेष रूप से बड़े in-memory data (जैसे 10Gb) को ध्यान में रखकर बनाए गए हैं।
+
+इस विगनेट में हम
+
+1. पहले संक्षेप में `data.table`s के default `melt`ing और `dcast`ing को देखेंगे, ताकि उन्हें *wide* से *long* format और _vice versa_ में बदला जा सके।
+
+2. उन परिस्थितियों को देखेंगे जहाँ वर्तमान functionalities जटिल और inefficient हो जाती हैं।
+
+3. अंत में `data.table`s के लिए `melt` और `dcast` दोनों methods में किए गए नए सुधार देखेंगे, जो multiple columns को एक साथ संभालते हैं।
+
+ये विस्तारित functionalities, operations को कुशल और सीधे तरीके से करने की `data.table` की philosophy के अनुरूप हैं।
+
+## 1. Default functionality
+
+### a) `data.table`s को `melt` करना (wide से long)
+
+मान लीजिए हमारे पास नीचे दिखाया गया एक `data.table` (कृत्रिम डेटा) है:
+
+```{r}
+s1 <- "family_id age_mother dob_child1 dob_child2 dob_child3
+1 30 1998-11-26 2000-01-29 NA
+2 27 1996-06-22 NA NA
+3 26 2002-07-11 2004-04-05 2007-09-02
+4 32 2004-10-10 2009-08-27 2012-07-21
+5 29 2000-12-05 2005-02-28 NA"
+DT <- fread(s1)
+DT
+## dob stands for date of birth.
+
+str(DT)
+```
+
+
+#### - `DT` को *long* रूप में बदलें जहाँ प्रत्येक `dob` एक अलग observation हो।
+
+हम `melt()` में `id.vars` और `measure.vars` arguments देकर इसे इस प्रकार कर सकते हैं:
+
+```{r}
+DT.m1 = melt(DT, id.vars = c("family_id", "age_mother"),
+ measure.vars = c("dob_child1", "dob_child2", "dob_child3"))
+DT.m1
+str(DT.m1)
+```
+
+* `measure.vars` उन columns के set को निर्दिष्ट करता है जिन्हें हम एक साथ collapse (या combine) करना चाहते हैं।
+
+* हम *names* के बजाय column *positions* भी दे सकते हैं।
+
+* Default रूप से `variable` column का type `factor` होता है। यदि आप इसके बजाय *`character`* vector लौटाना चाहते हैं, तो `variable.factor` argument को `FALSE` सेट करें।
+
+* Default रूप से molten columns के नाम अपने आप `variable` और `value` रखे जाते हैं।
+
+* `melt` परिणाम में column attributes को बनाए रखता है।
+
+#### - `variable` और `value` columns के नाम क्रमशः `child` और `dob` रखें
+
+
+```{r}
+DT.m1 = melt(DT, measure.vars = c("dob_child1", "dob_child2", "dob_child3"),
+ variable.name = "child", value.name = "dob")
+DT.m1
+```
+
+* Default रूप से, जब `id.vars` या `measure.vars` में से कोई एक नहीं दिया जाता, तो बाकी columns उस missing argument को *स्वचालित रूप से assign* कर दिए जाते हैं।
+
+* जब न `id.vars` और न ही `measure.vars` दिया जाता है, तो `?melt` में बताए अनुसार सभी *non*-`numeric`, `integer`, `logical` columns `id.vars` को assign कर दिए जाते हैं।
+
+ इसके अतिरिक्त, एक warning message जारी होता है जो बताता है कि किन columns को अपने आप `id.vars` माना गया है।
+
+### b) `data.table`s को `dcast` करना (long से wide)
+
+पिछले section में हमने wide form से long form में जाना देखा। अब इस section में इसका reverse operation देखते हैं।
+
+#### - `DT.m1` से मूल data table `DT` वापस कैसे प्राप्त करें?
+
+अर्थात, हम प्रत्येक `family_id, age_mother` से संबंधित सभी *child* observations को एक ही row के अंतर्गत एकत्र करना चाहते हैं। `dcast` का उपयोग करके इसे इस प्रकार किया जा सकता है:
+
+```{r}
+dcast(DT.m1, family_id + age_mother ~ child, value.var = "dob")
+```
+
+* `dcast` *formula* interface का उपयोग करता है। Formula के *LHS* पर variables *id* vars और *RHS* पर variables *measure* vars दर्शाते हैं।
+
+* `value.var` उस column को दर्शाता है जिसके मानों से wide format में casting के दौरान cells भरे जाएँगे।
+
+* `dcast` जहाँ संभव हो, result में attributes को बनाए रखने की कोशिश करता है।
+
+#### - `DT.m1` से शुरू करते हुए, प्रत्येक family में बच्चों की संख्या कैसे प्राप्त करें?
+
+आप `dcast` में `fun.aggregate` argument के माध्यम से aggregation के लिए function भी दे सकते हैं। यह विशेष रूप से तब आवश्यक है जब दी गई formula प्रत्येक cell के लिए एक single observation की पहचान नहीं करती।
+
+```{r}
+dcast(DT.m1, family_id ~ ., fun.aggregate = function(x) sum(!is.na(x)), value.var = "dob")
+```
+
+अन्य उपयोगी arguments और अतिरिक्त examples के लिए `?dcast` देखें।
+
+## 2. पिछले `melt/dcast` approaches की सीमाएँ
+
+अब तक हमने `melt` और `dcast` की वे features देखीं जो `data.table`s के लिए internal `data.table` machinery (*fast radix ordering*, *binary search* आदि) का उपयोग करके कुशलता से implemented हैं।
+
+हालाँकि कुछ परिस्थितियाँ ऐसी हो सकती हैं जहाँ वांछित operation को सीधे तरीके से व्यक्त करना कठिन हो। उदाहरण के लिए नीचे दिखाए गए `data.table` पर विचार करें:
+
+```{r}
+s2 <- "family_id age_mother name_child1 name_child2 name_child3 gender_child1 gender_child2 gender_child3
+1 30 Ben Anna NA 1 2 NA
+2 27 Tom NA NA 2 NA NA
+3 26 Lia Sam Amy 2 2 1
+4 32 Max Zoe Joe 1 1 1
+5 29 Dan Eva NA 2 1 NA"
+DT <- fread(s2)
+DT
+## 1 = female, 2 = male
+```
+
+और मान लें कि आप सभी `name` columns को एक साथ तथा सभी `gender` columns को एक साथ combine (`melt`) करना चाहते हैं। पुरानी functionality का उपयोग करके हम कुछ ऐसा कर सकते थे:
+
+```{r}
+DT.m1 = melt(DT, id.vars = c("family_id", "age_mother"))
+DT.m1[, c("variable", "child") := tstrsplit(variable, "_", fixed = TRUE)]
+DT.c1 = dcast(DT.m1, family_id + age_mother + child ~ variable, value.var = "value")
+DT.c1
+
+str(DT.c1) ## gender column is character type now!
+```
+
+#### समस्याएँ
+
+1. हम वास्तव में सभी `name` type columns को एक साथ और सभी `gender` type columns को एक साथ जोड़ना चाहते थे। इसके बजाय हम *सब कुछ* एक साथ जोड़ रहे हैं और फिर उन्हें दोबारा अलग कर रहे हैं। यह स्पष्ट रूप से काफी घुमावदार (और inefficient) तरीका है।
+
+ तुलना के लिए कल्पना करें कि आपके पास चार shelves वाली अलमारी है और आप shelf 1 और 2 के कपड़ों को एक साथ (shelf 1 में), तथा shelf 3 और 4 के कपड़ों को एक साथ (shelf 3 में) रखना चाहते हैं। यहाँ हम लगभग ऐसा कर रहे हैं कि पहले सारे कपड़े एक साथ मिला दें और फिर उन्हें वापस shelves 1 और 3 में बाँटें!
+
+2. `melt` किए जाने वाले columns अलग-अलग types के हो सकते हैं। सभी को एक साथ `melt` करने पर result में columns को एक common type में coerce किया जाएगा।
+
+3. हम `variable` column को दो columns में split करके एक अतिरिक्त column बना रहे हैं, जिसका उद्देश्य काफी अस्पष्ट है। हम ऐसा इसलिए करते हैं क्योंकि अगले चरण में *casting* के लिए इसकी आवश्यकता होती है।
+
+4. अंत में हम dataset को cast करते हैं। समस्या यह है कि यह *melt* की तुलना में काफी अधिक computationally intensive operation है। विशेष रूप से इसमें formula के variables का order compute करना पड़ता है, जो महँगा है।
+
+वास्तव में `stats::reshape` इस operation को बहुत सीधे तरीके से करने में सक्षम है। यह अत्यंत उपयोगी और अक्सर कम आँका जाने वाला function है। आपको इसे अवश्य आज़माना चाहिए!
+
+## 3. विस्तारित (नई) functionality
+
+### a) विस्तारित `melt`
+
+क्योंकि हम चाहते हैं कि `data.table`s इसी interface का उपयोग करके इस operation को सीधे और कुशल रूप से करें, हमने एक *additional functionality* implement की है, जिसमें multiple columns को *एक साथ* `melt` किया जा सकता है।
+
+#### - Multiple columns को एक साथ `melt` करना
+
+विचार काफी सरल है। हम `measure.vars` में columns की एक list देते हैं, जहाँ list का प्रत्येक element उन columns को रखता है जिन्हें एक साथ combine किया जाना चाहिए।
+
+```{r}
+colA = paste0("name_child", 1:3)
+colB = paste0("gender_child", 1:3)
+DT.m2 = melt(DT, measure.vars = list(colA, colB), value.name = c("name", "gender"))
+DT.m2
+
+str(DT.m2) ## col type is preserved
+```
+
+* आवश्यकता होने पर हम `variable` column को हटा सकते हैं।
+
+* यह functionality पूरी तरह C में implemented है, इसलिए यह *सीधी* होने के साथ-साथ *तेज़* और *memory efficient* भी है।
+
+#### - `patterns()` का उपयोग
+
+आमतौर पर ऐसी समस्याओं में जिन columns को हम melt करना चाहते हैं, उन्हें किसी common pattern से पहचाना जा सकता है। सुविधा के लिए implemented `patterns()` function का उपयोग करके उन columns के लिए regular expressions दिए जा सकते हैं जिन्हें एक साथ combine करना है। ऊपर का operation इस प्रकार फिर से लिखा जा सकता है:
+
+```{r}
+DT.m2 = melt(DT, measure.vars = patterns("^name", "^gender"), value.name = c("name", "gender"))
+DT.m2
+```
+
+#### - Separator या pattern के माध्यम से `measure.vars` निर्दिष्ट करने के लिए `measure()` का उपयोग
+
+यदि, ऊपर दिए गए डेटा की तरह, melt किए जाने वाले input columns के names नियमित
+structure में हों, तो हम `measure` का उपयोग कर सकते हैं, जो separator या regex के माध्यम से columns
+को melt करने की अनुमति देता है। उदाहरण के लिए iris
+data पर विचार करें,
+
+```{r}
+(two.iris = data.table(datasets::iris)[c(1,150)])
+```
+
+Iris data में नियमित structure वाले चार numeric columns हैं: पहले
+flower part, फिर एक period, और फिर measurement dimension। यह
+बताने के लिए कि हमें उन चार columns को melt करना है, हम `measure` का उपयोग
+`sep="."` के साथ कर सकते हैं, जिसका अर्थ है कि सभी column names पर `strsplit` उपयोग किया जाए; splitting के बाद
+जिन columns में सबसे अधिक groups बनते हैं
+उन्हें `measure.vars` के रूप में उपयोग किया जाएगा:
+
+```{r}
+melt(two.iris, measure.vars = measure(part, dim, sep="."))
+```
+
+ऊपर के code में `measure` के पहले दो arguments (`part` और
+`dim`) output columns के नाम रखने के लिए उपयोग होते हैं; arguments की संख्या
+`sep` से splitting के बाद बनने वाले maximum groups की संख्या के बराबर होनी चाहिए।
+
+यदि हम दो value columns चाहते हैं, प्रत्येक part के लिए एक, तो हम
+विशेष `value.name` keyword का उपयोग कर सकते हैं, जिसका अर्थ है उस group में मिलने वाले प्रत्येक unique name
+के लिए एक value column output करना:
+
+```{r}
+melt(two.iris, measure.vars = measure(value.name, dim, sep="."))
+```
+
+ऊपर के code से हमें प्रत्येक flower part के लिए एक value column मिलता है। यदि हम
+इसके बजाय प्रत्येक measurement dimension के लिए value column चाहते हैं, तो कर सकते हैं:
+
+```{r}
+melt(two.iris, measure.vars = measure(part, value.name, sep="."))
+```
+
+Families और children वाले उदाहरण पर लौटते हुए, हम
+`measure` का अधिक complex उपयोग देख सकते हैं, जिसमें एक function शामिल है जो
+`child` string values को integers में बदलने के लिए उपयोग किया जाता है:
+
+```{r}
+DT.m3 = melt(DT, measure.vars = measure(value.name, child=as.integer, sep="_child"))
+DT.m3
+```
+
+ऊपर के code में हमने `sep="_child"` उपयोग किया, जिससे केवल वे columns melt होते हैं
+जिनके नामों में वह string मौजूद है (छह column names, प्रत्येक दो
+groups में split होता है)। `child=as.integer` argument का अर्थ है कि दूसरा group
+`child` नाम का output column बनाएगा, जिसके values
+उस group की character strings को function
+`as.integer` में देने से निर्धारित होंगी।
+
+अंत में हम एक उदाहरण देखते हैं (tidyr package से लिया गया) जहाँ हमें
+groups को separator के बजाय regular expression का उपयोग करके परिभाषित करना
+होता है।
+
+```{r}
+(who <- data.table(id=1, new_sp_m5564=2, newrel_f65=3))
+melt(who, measure.vars = measure(
+ diagnosis, gender, ages, pattern="new_?(.*)_(.)(.*)"))
+```
+
+`pattern` argument का उपयोग करते समय यह Perl-compatible
+regular expression होना चाहिए जिसमें capture groups की वही संख्या हो
+(parenthesized sub-expressions) जितनी अन्य arguments (group
+names) की संख्या है। नीचे का code दिखाता है कि पाँच
+groups, दो numeric output columns और एक anonymous type-conversion
+function के साथ अधिक complex regex कैसे उपयोग करें,
+
+```{r}
+melt(who, measure.vars = measure(
+ diagnosis, gender, ages,
+ ymin=as.numeric,
+ ymax=function(y) ifelse(nzchar(y), as.numeric(y), Inf),
+ pattern="new_?(.*)_(.)(([0-9]{2})([0-9]{0,2}))"
+))
+```
+
+### b) विस्तारित `dcast`
+
+बहुत अच्छा! अब हम एक साथ multiple columns में melt कर सकते हैं। अब ऊपर दिखाए गए dataset `DT.m2` से हम उसी format में कैसे लौटें जिसमें हमारा original data था?
+
+यदि हम `dcast` की current functionality का उपयोग करें, तो हमें दो बार cast करना और फिर results को bind करना पड़ेगा। लेकिन यह फिर से verbose है, सीधा नहीं है और inefficient भी है।
+
+#### - Multiple `value.var`s को एक साथ cast करना
+
+अब हम `data.table`s के लिए सीधे `dcast` को **multiple `value.var` columns** दे सकते हैं, ताकि operations internally और efficiently संभाले जाएँ।
+
+```{r}
+## new 'cast' functionality - multiple value.vars
+DT.c2 = dcast(DT.m2, family_id + age_mother ~ variable, value.var = c("name", "gender"))
+DT.c2
+```
+
+* जहाँ संभव हो, result में attributes सुरक्षित रखे जाते हैं।
+
+* सब कुछ internally और efficiently संभाला जाता है। तेज़ होने के साथ यह memory efficient भी है।
+
+#
+
+#### `fun.aggregate` को multiple functions देना:
+
+आप *data.tables* के लिए `dcast` में `fun.aggregate` को *multiple functions* भी दे सकते हैं। इस functionality को दिखाने वाले examples के लिए `?dcast` देखें।
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
+
+#
+
+***
diff --git a/vignettes/hi/datatable-sd-usage.Rmd b/vignettes/hi/datatable-sd-usage.Rmd
new file mode 100644
index 0000000000..8e3eec559f
--- /dev/null
+++ b/vignettes/hi/datatable-sd-usage.Rmd
@@ -0,0 +1,261 @@
+---
+title: "डेटा विश्लेषण के लिए .SD का उपयोग"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format:
+ options:
+ toc: true
+ number_sections: true
+vignette: >
+ %\VignetteIndexEntry{Using .SD for Data Analysis}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+यह विगनेट आपके `data.table` विश्लेषण में `.SD` variable का उपयोग करने के सबसे सामान्य तरीकों को समझाएगा। यह StackOverflow पर दिए गए [इस उत्तर](https://stackoverflow.com/a/47406952/3576984) का अनुकूलित रूप है।
+
+# `.SD` क्या है?
+
+सबसे व्यापक अर्थ में, `.SD` केवल उस variable को पकड़ने का एक संक्षिप्त तरीका है जो डेटा विश्लेषण के संदर्भ में बार-बार सामने आता है। इसे *S*ubset, *S*elfsame, या *D*ata के *S*elf-reference के रूप में समझा जा सकता है। अर्थात, अपने सबसे मूल रूप में `.SD`, `data.table` स्वयं के लिए एक _reflexive reference_ है -- जैसा कि हम नीचे के उदाहरणों में देखेंगे, यह "queries" (extractions/subsets आदि, `[` का उपयोग करके) को एक-दूसरे से जोड़ने में विशेष रूप से उपयोगी है। इसका अर्थ यह भी है कि `.SD` _स्वयं एक `data.table` है_ (इस अपवाद के साथ कि इसमें `:=` द्वारा assignment की अनुमति नहीं है)।
+
+`.SD` का सरल उपयोग column subsetting के लिए होता है (अर्थात जब `.SDcols` निर्दिष्ट किया जाता है); क्योंकि यह रूप समझने में अधिक सीधा है, हम पहले इसे देखेंगे। `.SD` का दूसरा उपयोग, grouping scenarios में (अर्थात जब `by = ` या `keyby = ` निर्दिष्ट हो), अवधारणात्मक रूप से थोड़ा अलग है (हालाँकि मूल रूप से वही है, क्योंकि non-grouped operation वास्तव में केवल एक group वाले grouping का edge case है)।
+
+## Lahman डेटा लोड करना और उसका पूर्वावलोकन करना
+
+इसे अधिक वास्तविक उदाहरण जैसा बनाने के लिए, काल्पनिक डेटा बनाने के बजाय [Lahman database](https://github.com/cdalzell/Lahman) से baseball से संबंधित कुछ datasets लोड करते हैं। सामान्य R उपयोग में हम इन datasets को सीधे `Lahman` R package से लोड करते; इस विगनेट में हमने इन्हें package के GitHub page से पहले ही डाउनलोड कर लिया है।
+
+```{r download_lahman}
+load('Teams.RData')
+setDT(Teams)
+Teams
+
+load('Pitching.RData')
+setDT(Pitching)
+Pitching
+```
+
+Baseball की शब्दावली से परिचित पाठकों को इन tables की सामग्री परिचित लगेगी; `Teams` किसी दिए गए वर्ष में किसी team के कुछ statistics दर्ज करता है, जबकि `Pitching` किसी दिए गए वर्ष में किसी pitcher के statistics दर्ज करता है। आगे बढ़ने से पहले [documentation](https://github.com/cdalzell/Lahman) अवश्य देखें और डेटा की संरचना समझने के लिए स्वयं थोड़ा अन्वेषण करें।
+
+# Ungrouped डेटा पर `.SD`
+
+`.SD` की reflexive प्रकृति से मेरा क्या तात्पर्य है, इसे दिखाने के लिए इसके सबसे साधारण उपयोग पर विचार करें:
+
+```{r plain_sd}
+Pitching[ , .SD]
+```
+
+अर्थात, `Pitching[ , .SD]` ने बस पूरी table लौटा दी है; दूसरे शब्दों में, यह `Pitching` या `Pitching[]` लिखने का अनावश्यक रूप से विस्तृत तरीका था:
+
+```{r plain_sd_is_table}
+identical(Pitching, Pitching[ , .SD])
+```
+
+Subsetting के संदर्भ में `.SD` अभी भी डेटा का subset है, बस यह एक साधारण subset है (पूरा set स्वयं)।
+
+## कॉलम Subsetting: `.SDcols`
+
+`.SD` में क्या शामिल होगा इसे प्रभावित करने का पहला तरीका है `.SDcols` argument का उपयोग करके `.SD` में शामिल _columns_ को सीमित करना:
+
+```{r simple_sdcols}
+# W: Wins; L: Losses; G: Games
+Pitching[ , .SD, .SDcols = c('W', 'L', 'G')]
+```
+
+यह केवल समझाने के लिए था और काफी साधारण उदाहरण था। Character vector स्वीकार करने के अलावा, `.SDcols` यह भी स्वीकार करता है:
+
+1. _columns_ को filter करने के लिए `is.character` जैसा कोई भी function
+2. regular expression द्वारा _column names_ को filter करने के लिए function[^*] `patterns()`
+3. integer और logical vectors
+
+[^*]: अधिक विवरण के लिए `?patterns` देखें
+
+यह सरल उपयोग कई अत्यंत उपयोगी और सामान्य data-manipulation operations में काम आता है:
+
+## कॉलम Type Conversion
+
+डेटा को व्यवस्थित करने में column type conversion एक सामान्य आवश्यकता है। यद्यपि [`fwrite` ने हाल में प्रत्येक column की class पहले से घोषित करने की क्षमता प्राप्त की है](https://github.com/Rdatatable/data.table/pull/2545), सभी datasets `fread` से नहीं आते (जैसे इस विगनेट में), और `character`/`factor`/`numeric` types के बीच बार-बार conversion सामान्य है। हम `.SD` और `.SDcols` का उपयोग करके columns के समूहों को एक common type में batch-convert कर सकते हैं।
+
+हम देखते हैं कि `Teams` dataset में निम्नलिखित columns `character` के रूप में stored हैं, जबकि तर्कसंगत रूप से इन्हें `factor` के रूप में रखना अधिक उचित हो सकता है:
+
+```{r identify_factors}
+# teamIDBR: Team ID used by Baseball Reference website
+# teamIDlahman45: Team ID used in Lahman database version 4.5
+# teamIDretro: Team ID used by Retrosheet
+fkt = c('teamIDBR', 'teamIDlahman45', 'teamIDretro')
+# confirm that they're stored as `character`
+str(Teams[ , ..fkt])
+```
+
+अब इन columns को `factor` में बदलने का सिंटैक्स सरल है:
+
+```{r assign_factors}
+Teams[ , names(.SD) := lapply(.SD, factor), .SDcols = patterns('teamID')]
+# print out the first column to demonstrate success
+head(unique(Teams[[fkt[1L]]]))
+```
+
+ध्यान दें:
+
+
+1. `:=` एक assignment operator है जो copy बनाए बिना `data.table` को उसी स्थान पर update करता है। अधिक जानकारी के लिए [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html) देखें।
+2. LHS, `names(.SD)`, बताता है कि हम किन columns को update कर रहे हैं - इस स्थिति में हम पूरी `.SD` को update कर रहे हैं।
+3. RHS, `lapply()`, `.SD` के प्रत्येक column पर loop करता है और उस column को factor में बदलता है।
+4. हम `.SDcols` का उपयोग केवल उन columns को चुनने के लिए करते हैं जिनमें `teamID` pattern है।
+
+फिर से, `.SDcols` argument काफी flexible है; ऊपर हमने `patterns` दिया, लेकिन हम `fkt` या column names का कोई भी `character` vector दे सकते थे। अन्य स्थितियों में column _positions_ का `integer` vector या प्रत्येक column को शामिल/बहिष्कृत करने वाला `logical` vector देना अधिक सुविधाजनक हो सकता है। अंत में, columns को filter करने के लिए function का उपयोग बहुत उपयोगी है।
+
+उदाहरण के लिए, सभी `factor` columns को `character` में बदलने के लिए हम निम्न कर सकते हैं:
+
+```{r sd_as_logical}
+fct_idx = Teams[, which(sapply(.SD, is.factor))] # column numbers to show the class changing
+str(Teams[[fct_idx[1L]]])
+Teams[ , names(.SD) := lapply(.SD, as.character), .SDcols = is.factor]
+str(Teams[[fct_idx[1L]]])
+```
+
+अंत में, `.SDcols` में pattern-based column matching का उपयोग करके उन सभी columns को चुन सकते हैं जिनमें `team` शामिल है और उन्हें वापस `factor` में बदल सकते हैं:
+
+```{r sd_patterns}
+Teams[ , .SD, .SDcols = patterns('team')]
+Teams[ , names(.SD) := lapply(.SD, factor), .SDcols = patterns('team')]
+```
+
+** ऊपर दिए गए उदाहरण के लिए एक सावधानी: column numbers को _स्पष्ट रूप से_ उपयोग करना (जैसे `DT[ , (1) := rnorm(.N)]`) खराब अभ्यास है और समय के साथ column positions बदलने पर कोड बिना स्पष्ट त्रुटि के गलत परिणाम दे सकता है। Numbers का अप्रत्यक्ष उपयोग भी खतरनाक हो सकता है यदि numbered index बनाने और उपयोग करने के क्रम पर हमारा सख्त नियंत्रण न हो।
+
+## किसी Model के Right-Hand Side को नियंत्रित करना
+
+Model specification में बदलाव मजबूत statistical analysis का एक मुख्य भाग है। आइए `Pitching` table में उपलब्ध छोटे covariate set का उपयोग करके किसी pitcher के ERA (Earned Runs Average, performance का एक माप) का अनुमान लगाने का प्रयास करें। `W` (wins) और `ERA` के बीच (linear) संबंध इस बात पर कैसे बदलता है कि specification में कौन से अन्य covariates शामिल हैं?
+
+यहाँ `.SD` की शक्ति का उपयोग करने वाली एक छोटी script है जो इस प्रश्न की जाँच करती है:
+
+```{r sd_for_lm, cache = FALSE, fig.cap="Fit OLS coefficient on W, various specifications, depicted as bars with distinct colors."}
+# this generates a list of the 2^k possible extra variables
+# for models of the form ERA ~ G + (...)
+extra_var = c('yearID', 'teamID', 'G', 'L')
+models = unlist(
+ lapply(0L:length(extra_var), combn, x = extra_var, simplify = FALSE),
+ recursive = FALSE
+)
+
+# here are 16 visually distinct colors, taken from the list of 20 here:
+# https://sashat.me/2017/01/11/list-of-20-simple-distinct-colors/
+col16 = c('#e6194b', '#3cb44b', '#ffe119', '#0082c8',
+ '#f58231', '#911eb4', '#46f0f0', '#f032e6',
+ '#d2f53c', '#fabebe', '#008080', '#e6beff',
+ '#aa6e28', '#fffac8', '#800000', '#aaffc3')
+
+par(oma = c(2, 0, 0, 0))
+lm_coef = sapply(models, function(rhs) {
+ # using ERA ~ . and data = .SD, then varying which
+ # columns are included in .SD allows us to perform this
+ # iteration over 16 models succinctly.
+ # coef(.)['W'] extracts the W coefficient from each model fit
+ Pitching[ , coef(lm(ERA ~ ., data = .SD))['W'], .SDcols = c('W', rhs)]
+})
+barplot(lm_coef, names.arg = sapply(models, paste, collapse = '/'),
+ main = 'Wins Coefficient\nWith Various Covariates',
+ col = col16, las = 2L, cex.names = 0.8)
+```
+
+Coefficient का sign हमेशा अपेक्षित रहता है (बेहतर pitchers में अधिक wins और कम runs allowed होते हैं), लेकिन magnitude इस बात के आधार पर काफी बदल सकता है कि हम और किन variables को control करते हैं।
+
+## Conditional Joins
+
+`data.table` सिंटैक्स अपनी सरलता और robustness के कारण बहुत उपयोगी है। सिंटैक्स `x[i]` subsetting के तीन सामान्य तरीकों को लचीले ढंग से संभालता है -- जब `i` एक `logical` vector हो, तो `x[i]`, `x` की वे rows लौटाता है जहाँ `i` `TRUE` है; जब `i` _एक अन्य `data.table`_ (या `list`) हो, तो (right) `join` किया जाता है (साधारण रूप में `x` और `i` की `key`s का उपयोग करके, या `on = ` दिए जाने पर उन columns के matches का उपयोग करके); और जब `i` character हो, तो इसे `x[list(i)]` का shorthand माना जाता है, अर्थात join के रूप में।
+
+सामान्य तौर पर यह बहुत उपयोगी है, लेकिन तब पर्याप्त नहीं होता जब हमें _conditional join_ करना हो, जहाँ tables के बीच संबंध का सटीक स्वरूप एक या अधिक columns की rows की कुछ विशेषताओं पर निर्भर करता है।
+
+यह उदाहरण स्वीकार्य रूप से थोड़ा कृत्रिम है, लेकिन विचार को स्पष्ट करता है; अधिक जानकारी के लिए यहाँ देखें ([1](https://stackoverflow.com/questions/31329939/conditional-keyed-join-update-and-update-a-flag-column-for-matches), [2](https://stackoverflow.com/questions/29658627/conditional-binary-join-and-update-by-reference-using-the-data-table-package))।
+
+उद्देश्य `Pitching` table में `team_performance` नामक column जोड़ना है, जो प्रत्येक team के सर्वश्रेष्ठ pitcher (कम से कम 6 recorded games वाले pitchers में सबसे कम ERA के आधार पर) की team performance (rank) दर्ज करे।
+
+```{r conditional_join}
+# to exclude pitchers with exceptional performance in a few games,
+# subset first; then define rank of pitchers within their team each year
+# (in general, we should put more care into the 'ties.method' of frank)
+Pitching[G > 5, rank_in_team := frank(ERA), by = .(teamID, yearID)]
+Pitching[rank_in_team == 1, team_performance :=
+ Teams[.SD, Rank, on = c('teamID', 'yearID')]]
+```
+
+ध्यान दें कि `x[y]` सिंटैक्स `nrow(y)` values लौटाता है (अर्थात यह right join है), इसलिए `Teams[.SD]` में `.SD` दाईं ओर है (क्योंकि इस स्थिति में `:=` के RHS को `nrow(Pitching[rank_in_team == 1])` values चाहिए)।
+
+# Grouped `.SD` operations
+
+अक्सर हम अपने डेटा पर _group level_ पर कोई operation करना चाहते हैं। जब हम `by =` (या `keyby = `) निर्दिष्ट करते हैं, तो `data.table` द्वारा `j` process किए जाने को ऐसे समझ सकते हैं मानो आपका `data.table` कई छोटे sub-`data.table`s में विभाजित हो गया है, जिनमें से प्रत्येक आपके `by` variable(s) के एक मान से संबंधित है:
+
+
+
+
+Grouping की स्थिति में `.SD` का स्वरूप बहु-भागीय होता है -- यह इन प्रत्येक sub-`data.table`s को _एक समय में एक_ संदर्भित करता है (थोड़ा अधिक सटीक रूप से कहें तो `.SD` का scope एक single sub-`data.table` होता है)। इससे हम संक्षेप में वह operation व्यक्त कर सकते हैं जिसे re-assembled result लौटने से पहले _प्रत्येक sub-`data.table`_ पर करना चाहते हैं।
+
+यह कई परिस्थितियों में उपयोगी है; सबसे सामान्य उदाहरण यहाँ दिए गए हैं:
+
+## Group Subsetting
+
+Lahman data में प्रत्येक team के लिए सबसे हाल का season प्राप्त करते हैं। इसे बहुत सरलता से किया जा सकता है:
+
+```{r group_sd_last}
+# the data is already sorted by year; if it weren't
+# we could do Teams[order(yearID), .SD[.N], by = teamID]
+Teams[ , .SD[.N], by = teamID]
+```
+
+याद रखें कि `.SD` स्वयं एक `data.table` है, और `.N` किसी group की कुल rows की संख्या को दर्शाता है (प्रत्येक group के भीतर यह `nrow(.SD)` के बराबर है), इसलिए `.SD[.N]` प्रत्येक `teamID` से संबंधित अंतिम row के लिए _पूरी `.SD`_ लौटाता है।
+
+इसका एक अन्य सामान्य रूप है प्रत्येक group की _पहली_ observation प्राप्त करने के लिए `.SD[1L]`, या प्रत्येक group से एक _random_ row लौटाने के लिए `.SD[sample(.N, 1L)]` का उपयोग करना।
+
+## Group Optima
+
+मान लीजिए हम प्रत्येक team का _सर्वश्रेष्ठ_ वर्ष लौटाना चाहते हैं, जिसका माप कुल runs scored (`R`; इसे अन्य metrics के लिए आसानी से बदला जा सकता है) से किया जाए। प्रत्येक sub-`data.table` से कोई _fixed_ element लेने के बजाय, अब हम वांछित index को _dynamically_ इस प्रकार परिभाषित करते हैं:
+
+```{r sd_team_best_year}
+Teams[ , .SD[which.max(R)], by = teamID]
+```
+
+ध्यान दें कि इस approach को `.SDcols` के साथ जोड़ा जा सकता है ताकि प्रत्येक `.SD` के लिए `data.table` के केवल कुछ हिस्से लौटाए जाएँ (इस सावधानी के साथ कि विभिन्न subsets में `.SDcols` स्थिर रहना चाहिए)।
+
+_NB_: `.SD[1L]` वर्तमान में [_`GForce`_](https://Rdatatable.gitlab.io/data.table/library/data.table/html/datatable-optimize.html) द्वारा optimized है ([यह भी देखें](https://stackoverflow.com/questions/22137591/about-gforce-in-data-table-1-9-2)), जो `data.table` की internal सुविधा है और `sum` या `mean` जैसे सामान्य grouped operations को बहुत तेज़ बनाती है -- अधिक जानकारी के लिए `?GForce` देखें और इस क्षेत्र में updates के लिए feature improvement requests पर नज़र रखें/समर्थन दें: [1](https://github.com/Rdatatable/data.table/issues/735), [2](https://github.com/Rdatatable/data.table/issues/2778), [3](https://github.com/Rdatatable/data.table/issues/523), [4](https://github.com/Rdatatable/data.table/issues/971), [5](https://github.com/Rdatatable/data.table/issues/1197), [6](https://github.com/Rdatatable/data.table/issues/1414)।
+
+## Grouped Regression
+
+ऊपर `ERA` और `W` के संबंध से जुड़े प्रश्न पर लौटते हुए, मान लें कि हम अपेक्षा करते हैं कि यह संबंध team के अनुसार अलग हो (अर्थात प्रत्येक team का slope अलग हो)। हम इस heterogeneity की जाँच करने के लिए regression को आसानी से दोबारा चला सकते हैं (ध्यान दें कि इस approach से standard errors सामान्यतः गलत होते हैं -- specification `ERA ~ W*teamID` बेहतर होगा -- यह approach पढ़ने में आसान है और _coefficients_ ठीक हैं):
+
+```{r group_lm, results = 'hide', fig.cap="A histogram depicting the distribution of fitted coefficients. It is vaguely bell-shaped and concentrated around -.2"}
+# Overall coefficient for comparison
+overall_coef = Pitching[ , coef(lm(ERA ~ W))['W']]
+# use the .N > 20 filter to exclude teams with few observations
+Pitching[ , if (.N > 20L) .(w_coef = coef(lm(ERA ~ W))['W']), by = teamID
+ ][ , hist(w_coef, 20L, las = 1L,
+ xlab = 'Fitted Coefficient on W',
+ ylab = 'Number of Teams', col = 'darkgreen',
+ main = 'Team-Level Distribution\nWin Coefficients on ERA')]
+abline(v = overall_coef, lty = 2L, col = 'red')
+```
+
+वास्तव में पर्याप्त heterogeneity है, लेकिन observed overall value के आसपास स्पष्ट concentration भी दिखाई देती है।
+
+ऊपर दिया गया भाग केवल एक छोटा परिचय है कि `.SD`, `data.table` में सुंदर और efficient code लिखने को कितना आसान बना सकता है!
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
diff --git a/vignettes/hi/datatable-secondary-indices-and-auto-indexing.Rmd b/vignettes/hi/datatable-secondary-indices-and-auto-indexing.Rmd
new file mode 100644
index 0000000000..da35d856a1
--- /dev/null
+++ b/vignettes/hi/datatable-secondary-indices-and-auto-indexing.Rmd
@@ -0,0 +1,364 @@
+---
+title: "सेकेंडरी इंडेक्स और ऑटो इंडेक्सिंग"
+date: "`{r} Sys.Date()`"
+output:
+ litedown::html_format
+vignette: >
+ %\VignetteIndexEntry{Secondary indices and auto indexing}
+ %\VignetteEngine{litedown::vignette}
+ \usepackage[utf8]{inputenc}
+---
+
+```{r, echo=FALSE, file='_translation_links.R'}
+```
+`{r} .write.translation.links("Translations of this document are available in: %s")`
+
+```{r, echo = FALSE, message = FALSE}
+library(data.table)
+litedown::reactor(comment = "# ")
+.old.th = setDTthreads(1)
+```
+
+यह विगनेट मानता है कि पाठक data.table के `[i, j, by]` सिंटैक्स और तेज़ key-आधारित subsets बनाने की प्रक्रिया से परिचित है। यदि आप इन अवधारणाओं से परिचित नहीं हैं, तो पहले निम्नलिखित विगनेट पढ़ें:
+
+- [`vignette("datatable-intro", package="data.table")`](datatable-intro.html)
+- [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html)
+- [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html)
+
+***
+
+## डेटा {#data}
+
+हम वही `flights` डेटा उपयोग करेंगे जो [`vignette("datatable-intro", package="data.table")`](datatable-intro.html) विगनेट में उपयोग किया गया है।
+
+```{r, echo = FALSE}
+options(width = 100L)
+```
+
+```{r}
+flights <- fread("flights14.csv")
+head(flights)
+dim(flights)
+```
+
+## परिचय
+
+इस विगनेट में हम
+
+* *secondary indices* पर चर्चा करेंगे और ऐसे उदाहरणों के माध्यम से समझेंगे कि उनकी आवश्यकता क्यों है, जहाँ keys सेट करना हमेशा आदर्श नहीं होता,
+
+* फिर से तेज़ subsetting करेंगे, लेकिन इस बार नए `on` argument का उपयोग करके, जो कार्य के लिए आंतरिक रूप से (अस्थायी रूप से) secondary indices बनाता है और यदि कोई पहले से मौजूद हो तो उसका पुनः उपयोग करता है,
+
+* और अंत में *auto indexing* देखेंगे, जो एक कदम आगे जाकर secondary indices को अपने आप बनाता है, लेकिन subsetting के लिए मूल R सिंटैक्स का उपयोग करता है।
+
+## 1. सेकेंडरी इंडेक्स
+
+### a) सेकेंडरी इंडेक्स क्या हैं?
+
+Secondary indices, *data.table* में `keys` के समान होते हैं, लेकिन इनके बीच दो प्रमुख अंतर हैं:
+
+* यह RAM में पूरे data.table को भौतिक रूप से पुनः क्रमित *नहीं* करता। इसके बजाय, यह केवल दिए गए कॉलमों के लिए क्रम की गणना करता है और उस *order vector* को `index` नामक अतिरिक्त attribute में संग्रहीत करता है।
+
+* एक data.table के लिए एक से अधिक secondary index हो सकते हैं (जैसा कि हम नीचे देखेंगे)।
+
+#### Keyed बनाम Indexed Subsetting
+
+हालाँकि **keys** और **indices** दोनों तेज़ binary-search subsetting संभव बनाते हैं, लेकिन उनके उपयोग में महत्वपूर्ण अंतर हैं:
+
+**Keyed subsetting** (कॉलम का अप्रत्यक्ष मिलान)
+
+```{r keyed_operations}
+DT = data.table(a = c(TRUE, FALSE), b = 1:2)
+setkey(DT, a) # Set key, reordering DT
+DT[.(TRUE)] # 'on' is optional; if omitted, the key is used
+```
+
+**Indexed subsetting** (कॉलम का स्पष्ट उल्लेख)
+
+```{r unkeyed_operations}
+DT = data.table(a = c(TRUE, FALSE), b = 1:2)
+setindex(DT, a) # Set index only (no reorder)
+DT[.(TRUE), on = "a"] # 'on' is required
+```
+
+### b) सेकेंडरी इंडेक्स सेट करना और प्राप्त करना
+
+#### -- *data.table* `flights` में `origin` कॉलम को secondary index के रूप में कैसे सेट करें?
+
+```{r}
+setindex(flights, origin)
+head(flights)
+
+## alternatively we can provide character vectors to the function 'setindexv()'
+# setindexv(flights, "origin") # useful to program with
+
+# 'index' attribute added
+names(attributes(flights))
+```
+
+* `setindex` और `setindexv()` data.table में secondary index जोड़ने की अनुमति देते हैं।
+
+* ध्यान दें कि `flights` को `origin` के बढ़ते क्रम में भौतिक रूप से पुनः व्यवस्थित **नहीं** किया गया है, जैसा कि `setkey()` के साथ होता।
+
+* यह भी ध्यान दें कि `flights` में `index` attribute जोड़ा गया है।
+
+* `setindex(flights, NULL)` सभी secondary indices हटा देगा।
+
+#### -- `flights` में अब तक सेट किए गए सभी secondary indices कैसे प्राप्त करें?
+
+```{r}
+indices(flights)
+
+setindex(flights, origin, dest)
+indices(flights)
+```
+
+* `indices()` फ़ंक्शन data.table में वर्तमान सभी secondary indices लौटाता है। यदि कोई मौजूद नहीं है, तो `NULL` लौटाया जाता है।
+
+* ध्यान दें कि `origin, dest` कॉलमों पर एक और index बनाने से `origin` कॉलम पर पहले बनाया गया index समाप्त नहीं होता; अर्थात हम एक से अधिक secondary indices रख सकते हैं।
+
+### c) हमें सेकेंडरी इंडेक्स की आवश्यकता क्यों है?
+
+#### -- data.table को पुनः क्रमित करना महँगा हो सकता है और हमेशा आदर्श नहीं होता
+
+मान लीजिए कि आप `origin` कॉलम में "JFK" मान के लिए तेज़ key-आधारित subset करना चाहते हैं। हम इसे इस प्रकार करेंगे:
+
+```r
+## not run
+setkey(flights, origin)
+flights["JFK"] # or flights[.("JFK")]
+```
+
+#### `setkey()` के लिए आवश्यक है:
+
+a) दिए गए कॉलम(ों), यहाँ `origin`, के लिए order vector की गणना करना, और
+
+b) गणना किए गए order vector के आधार पर पूरे data.table को by reference पुनः क्रमित करना।
+
+#
+
+क्रम की गणना स्वयं अधिक समय लेने वाला भाग नहीं है, क्योंकि data.table integer, character और numeric vectors पर वास्तविक radix sorting का उपयोग करता है। हालाँकि, पूरे data.table को पुनः क्रमित करना समय लेने वाला हो सकता है (rows और columns की संख्या पर निर्भर करता है)।
+
+यदि हमारे कार्य में उसी कॉलम पर बार-बार subsetting शामिल नहीं है, तो data.table के आयामों के आधार पर पुनः क्रमित करने में लगने वाला समय तेज़ key-आधारित subsetting के लाभ को लगभग समाप्त कर सकता है।
+
+#### -- अधिकतम केवल एक `key` हो सकती है
+
+अब यदि हम यही ऑपरेशन `dest` कॉलम पर "LAX" मान के लिए दोहराना चाहें, तो हमें फिर से `setkey()` करना होगा।
+
+```r
+## not run
+setkey(flights, dest)
+flights["LAX"]
+```
+
+और इससे `flights` फिर से `dest` के आधार पर पुनः क्रमित होता है। वास्तव में हम चाहते हैं कि पुनः क्रमित करने वाले चरण को हटाकर तेज़ subsetting कर सकें।
+
+और यही काम *secondary indices* संभव बनाते हैं!
+
+#### -- सेकेंडरी इंडेक्स का पुनः उपयोग किया जा सकता है
+
+क्योंकि एक से अधिक secondary indices हो सकते हैं, और index बनाना केवल order vector को attribute के रूप में संग्रहीत करने जितना सरल है, इसलिए यदि index पहले से मौजूद है तो order vector को फिर से गणना करने में लगने वाला समय भी बचाया जा सकता है।
+
+#### -- नया `on` argument अधिक साफ़ सिंटैक्स तथा secondary indices का स्वचालित निर्माण और पुनः उपयोग संभव बनाता है
+
+जैसा कि हम अगले सेक्शन में देखेंगे, `on` argument कई लाभ देता है:
+
+#### `on` argument
+
+* यह आवश्यकता के समय secondary indices की गणना करके subsetting सक्षम करता है। इससे हर बार `setindex()` करने की आवश्यकता समाप्त हो जाती है।
+
+* केवल attributes की जाँच करके मौजूदा indices का आसानी से पुनः उपयोग करने देता है।
+
+* subset किए जाने वाले कॉलमों को सीधे सिंटैक्स का हिस्सा बनाकर अधिक साफ़ सिंटैक्स देता है। इससे बाद में कोड देखते समय उसे समझना आसान होता है।
+
+ ध्यान दें कि `on` argument का उपयोग keyed subsets पर भी किया जा सकता है। वास्तव में बेहतर readability के लिए हम keys का उपयोग करते समय भी `on` argument देने की सलाह देते हैं।
+
+#
+
+## 2. `on` argument और सेकेंडरी इंडेक्स का उपयोग करके तेज़ subsetting
+
+### a) `i` में तेज़ subsets
+
+#### -- `on` का उपयोग करके वे सभी rows subset करें जहाँ origin airport *"JFK"* से मेल खाता है
+
+```{r}
+flights["JFK", on = "origin"]
+
+## alternatively
+# flights[.("JFK"), on = "origin"] (or)
+# flights[list("JFK"), on = "origin"]
+```
+
+* यह statement भी आवश्यक index को उसी समय गणना करके तेज़ binary-search आधारित subset करता है। हालाँकि ध्यान दें कि यह index को अपने आप attribute के रूप में सहेजता नहीं है। भविष्य में यह व्यवहार बदल सकता है।
+
+* यदि हमने पहले ही `setindex()` का उपयोग करके secondary index बना लिया होता, तो `on` उसे दोबारा गणना करने के बजाय पुनः उपयोग करता। इसे हम `verbose = TRUE` का उपयोग करके देख सकते हैं:
+
+ ```{r}
+ setindex(flights, origin)
+ flights["JFK", on = "origin", verbose = TRUE][1:5]
+ ```
+
+#### -- `origin` *और* `dest` कॉलमों के आधार पर subset कैसे करें?
+
+उदाहरण के लिए, यदि हम `"JFK", "LAX"` संयोजन को subset करना चाहते हैं, तो:
+
+```{r}
+flights[.("JFK", "LAX"), on = c("origin", "dest")][1:5]
+```
+
+* `on` argument, `i-argument` में दिए गए क्रम के अनुरूप कॉलम नामों का character vector स्वीकार करता है।
+
+* क्योंकि secondary index की गणना में बहुत कम समय लगता है, इसलिए जब तक कार्य में एक ही कॉलम पर बार-बार subsetting शामिल न हो, हमें `setindex()` का उपयोग करने की आवश्यकता नहीं है।
+
+* स्पष्टता/readability के लिए `i` में inputs को नाम देना सहायक हो सकता है, उदाहरण के लिए,
+
+```{r}
+flights[.(origin = "JFK", dest = "LAX"), on = c("origin", "dest")]
+```
+
+इससे स्पष्ट हो जाता है कि `j` की कौन-सी entries, `on` के किस element से संबंधित हैं।
+
+### b) `j` में चयन
+
+नीचे जिन सभी operations पर हम चर्चा करेंगे, वे [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html) विगनेट में पहले देखे गए operations से अलग नहीं हैं। अंतर केवल इतना है कि keys सेट करने के बजाय हम `on` argument का उपयोग करेंगे।
+
+#### -- `origin = "LGA"` और `dest = "TPA"` के अनुरूप केवल `arr_delay` कॉलम को data.table के रूप में लौटाएँ
+
+```{r}
+flights[.("LGA", "TPA"), .(arr_delay), on = c("origin", "dest")]
+```
+
+### c) Chaining
+
+#### -- ऊपर प्राप्त परिणाम पर chaining का उपयोग करके कॉलम को घटते क्रम में व्यवस्थित करें।
+
+```{r}
+flights[.("LGA", "TPA"), .(arr_delay), on = c("origin", "dest")][order(-arr_delay)]
+```
+
+### d) `j` में गणना या *do* करना
+
+#### -- `origin = "LGA"` और `dest = "TPA"` के अनुरूप अधिकतम arrival delay ज्ञात करें।
+
+```{r}
+flights[.("LGA", "TPA"), max(arr_delay), on = c("origin", "dest")]
+```
+
+### e) `j` में `:=` का उपयोग करके by reference *sub-assign* करना
+
+हम यह उदाहरण [`vignette("datatable-reference-semantics", package="data.table")`](datatable-reference-semantics.html) और [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html) विगनेट में पहले ही देख चुके हैं। आइए `flights` *data.table* में उपलब्ध सभी `hours` देखें:
+
+```{r}
+# get all 'hours' in flights
+flights[, sort(unique(hour))]
+```
+
+हम देखते हैं कि डेटा में कुल `25` unique values हैं। *0* और *24* दोनों घंटे मौजूद प्रतीत होते हैं। आइए *24* को *0* से बदलें, लेकिन इस बार keys सेट करने के बजाय `on` का उपयोग करें।
+
+```{r}
+flights[.(24L), hour := 0L, on = "hour"]
+```
+
+अब जाँचते हैं कि `hour` कॉलम में `24` को `0` से बदला गया है या नहीं।
+
+```{r}
+flights[, sort(unique(hour))]
+```
+
+* यह secondary indices का विशेष रूप से बड़ा लाभ है। पहले `hour` की केवल कुछ rows अपडेट करने के लिए भी हमें उस पर `setkey()` करना पड़ता था, जिससे पूरा data.table अनिवार्य रूप से पुनः क्रमित हो जाता था। `on` के साथ क्रम बना रहता है और ऑपरेशन बहुत तेज़ होता है। कोड देखकर यह भी स्पष्ट रहता है कि हम कौन-सा कार्य करना चाहते थे।
+
+### f) `by` का उपयोग करके aggregation
+
+#### -- `origin = "JFK"` के अनुरूप प्रत्येक `month` के लिए अधिकतम departure delay प्राप्त करें। परिणाम को `month` के अनुसार क्रमित करें
+
+```{r}
+ans <- flights["JFK", max(dep_delay), keyby = month, on = "origin"]
+head(ans)
+```
+
+* यदि हमने `on` का उपयोग नहीं किया होता, जो आवश्यकता के समय आंतरिक रूप से secondary indices बनाता है, तो हमें `key` को फिर से `origin, dest` पर सेट करना पड़ता।
+
+### g) *mult* argument
+
+अन्य arguments, जिनमें `mult` भी शामिल है, ठीक उसी प्रकार काम करते हैं जैसा हमने [`vignette("datatable-keys-fast-subset", package="data.table")`](datatable-keys-fast-subset.html) विगनेट में देखा था। `mult` का default value "all" है। इसके बजाय हम चुन सकते हैं कि केवल "first" या "last" matching rows लौटाई जाएँ।
+
+#### -- केवल पहली matching row subset करें जहाँ `dest`, *"BOS"* और *"DAY"* से मेल खाता हो
+
+```{r}
+flights[c("BOS", "DAY"), on = "dest", mult = "first"]
+```
+
+#### -- केवल अंतिम matching row subset करें जहाँ `origin`, *"LGA", "JFK", "EWR"* से और `dest`, *"XNA"* से मेल खाता हो
+
+```{r}
+flights[.(c("LGA", "JFK", "EWR"), "XNA"), on = c("origin", "dest"), mult = "last"]
+```
+
+### h) *nomatch* argument
+
+हम `nomatch` argument का उपयोग करके चुन सकते हैं कि मेल न खाने वाली queries `NA` लौटाएँ या उन्हें पूरी तरह छोड़ दिया जाए।
+
+#### -- पिछले उदाहरण से केवल वही सभी rows subset करें जहाँ match मौजूद हो
+
+```{r}
+flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last", on = c("origin", "dest"), nomatch = NULL]
+```
+
+* "JFK" और "XNA" को जोड़ने वाली कोई flight नहीं है। इसलिए परिणाम में उस row को छोड़ दिया जाता है।
+
+## 3. ऑटो इंडेक्सिंग
+
+पहले हमने देखा कि *keys* का उपयोग करके binary search के माध्यम से तेज़ subset कैसे किया जाता है। फिर हमने पाया कि secondary indices का उपयोग करके performance को और बेहतर किया जा सकता है और सिंटैक्स को अधिक साफ़ बनाया जा सकता है।
+
+यही काम *auto indexing* करता है। फिलहाल यह केवल binary operators `==` और `%in%` के लिए लागू है। एक index अपने आप बनाया जाता है *और* attribute के रूप में सहेजा जाता है। अर्थात `on` argument के विपरीत, जो हर बार आवश्यकता के समय index की गणना करता है (जब तक कोई पहले से मौजूद न हो), यहाँ secondary index बनाया जाता है।
+
+लाभ को स्पष्ट रूप से दिखाने के लिए पर्याप्त बड़ा data.table बनाकर शुरू करते हैं।
+
+```{r}
+set.seed(1L)
+dt = data.table(x = sample(1e5L, 1e7L, TRUE), y = runif(100L))
+print(object.size(dt), units = "Mb")
+```
+
+जब हम पहली बार किसी एक कॉलम पर `==` या `%in%` का उपयोग करते हैं, तो secondary index अपने आप बन जाता है और subset करने के लिए उसका उपयोग किया जाता है।
+
+```{r}
+## have a look at all the attribute names
+names(attributes(dt))
+
+## run thefirst time
+(t1 <- system.time(ans <- dt[x == 989L]))
+head(ans)
+
+## secondary index is created
+names(attributes(dt))
+
+indices(dt)
+```
+
+पहली बार subset करने में लगने वाला समय = index बनाने का समय + subset करने का समय होता है। क्योंकि secondary index बनाने में केवल order vector बनाना शामिल है, इसलिए कई मामलों में यह संयुक्त ऑपरेशन vector scans से तेज़ होता है। लेकिन वास्तविक लाभ बाद के subsets में मिलता है। वे अत्यंत तेज़ होते हैं।
+
+```{r}
+## successive subsets
+(t2 <- system.time(dt[x == 989L]))
+system.time(dt[x %in% 1989:2012])
+```
+
+* पहली बार चलाने में `r sprintf("%.3f", t1["elapsed"])` सेकंड लगे, जबकि दूसरी बार `r sprintf("%.3f", t2["elapsed"])` सेकंड लगे।
+
+* global argument `options(datatable.auto.index = FALSE)` सेट करके auto indexing को बंद किया जा सकता है।
+
+* auto indexing बंद करने पर भी `setindex` या `setindexv` से स्पष्ट रूप से बनाए गए indices का उपयोग किया जा सकता है। indices को पूरी तरह बंद करने के लिए global argument `options(datatable.use.index = FALSE)` सेट कर सकते हैं।
+
+#
+
+हाल के संस्करण में हमने auto indexing को एक से अधिक कॉलम वाली expressions (जिन्हें `&` operator से जोड़ा गया हो) तक विस्तारित किया है। भविष्य में हम binary search को `<`, `<=`, `>` और `>=` जैसे और binary operators के साथ काम करने के लिए विस्तारित करने की योजना बना रहे हैं।
+
+हम [अगले विगनेट (`vignette("datatable-joins", package="data.table")`)](datatable-joins.html) में keys और secondary indices का उपयोग करके तेज़ *subsets* से *joins* की ओर बढ़ेंगे।
+
+***
+
+```{r, echo=FALSE}
+setDTthreads(.old.th)
+```
+