सिलेबस के दूसरे भागों में हम अलग-अलग विशेषताओं वाले कई डेटा टाइप देख चुके हैं।
यह कॉन्सेप्ट देखेगा कि बहु-आयामी और विषमरूपी डेटा को कैसे संग्रहीत किया जाए। व्यवहार में, वास्तविक दुनिया का ज़्यादातर डेटा ऐसा ही होता है। इसलिए अब हम उस मुख्य बात तक पहुँच रहे हैं कि R का इस्तेमाल (ज़्यादातर) व्यवहार में कैसे किया जाता है।
पिछले कई दशकों में R ने टेबल के रूप वाले डेटा को संभालने के लिए कई डेटा टाइप जोड़े हैं।
यह सिलेबस मुख्य रूप से tibble पर केंद्रित रहेगा, लेकिन दूसरे विकल्पों के बारे में जानना भी काम का है।
data.frameबेस R में एक data.frame, बराबर लंबाई वाले vectors की एक list होती है।
इसे डेटा की एक आयताकार टेबल की तरह समझा जा सकता है, जिसमें हर कॉलम एक ही तरह का होता है, लेकिन हर पंक्ति में अलग-अलग तरह का डेटा हो सकता है (और आम तौर पर होता भी है)।
इसे समझाने के लिए एक उदाहरण:
# create the column vectors
languages <- c("Fortran", "R", "Python", "Julia")
created <- c(1957, 1993, 1991, 2012)
has.syllabus <- c(FALSE, TRUE, TRUE, TRUE)
# join columns to create the dataframe
df <- data.frame(languages, created, has.syllabus)
df
#> languages created has.syllabus
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
यहाँ एक कॉलम ऐसा है जिसमें अक्षरों की स्ट्रिंग हैं, दूसरे में संख्याएँ हैं, और तीसरे में बूलियन हैं। जब डेटा बड़ा हो जाता है, तो यह वास्तविक दुनिया के कई तरह के डेटा को दिखाने का एक सहज तरीका है।
tibbledata.frame का डिज़ाइन बहुत पुराना है।
कई दशकों के अनुभव और R के इस्तेमाल के बदलते तरीकों की वजह से इसे दोबारा डिज़ाइन किया गया, और Tidyverse में एक आधुनिक विकल्प बना: tibble।
बेस R की तुलना में tibble में ये बातें हैं:
संक्षेप में, tibble का उद्देश्य है "कम काम करना और ज़्यादा शिकायत करना", जिसे "आलसी और चिड़चिड़ा" भी कहा जाता है।
ये टाइप आम तौर पर एक-दूसरे की जगह इस्तेमाल किए जा सकते हैं: जो भी फंक्शन एक data.frame लेता है, वह tibble भी ले लेगा, और इसका उलटा भी।
इन टाइपों के बीच बदलाव करना आसान है, as_tibble(df) और as.data.frame(tbl) की मदद से।
नए काम के लिए, tibble का इस्तेमाल करने से आपको ज़्यादा मज़बूत कोड लिखने में मदद मिलेगी।
लेकिन R की दुनिया में पुराना कोड और पुराना डेटा बहुत ज़्यादा है, इसलिए data.frame आगे भी काफी समय तक आम बना रहेगा।
# column vectors are same as for data.frame
library(tibble)
tbl <- tibble(languages, created, has.syllabus)
tbl
# A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
डिफॉल्ट प्रिंट फॉर्मेट पर ध्यान दीजिए: आयाम वाली कमेंट लाइन अपने आप छपती है, और कॉलम के टाइप भी दिखाए जाते हैं।
tibble Tidyverse का मुख्य हिस्सा हैं, इसलिए इन्हें library(tibble) या library(tidyverse) से जोड़िए।
सबसे आसान तरीका यह है कि हम tibble() फंक्शन से कॉलम वेक्टर जोड़ सकते हैं, ठीक वैसे जैसे data.frame() में।
इसका एक उदाहरण पिछले भाग में दिखाया गया था।
अगर वैल्यू को पंक्ति-दर-पंक्ति भरना ज़्यादा सुविधाजनक लगे, तो उसके लिए फंक्शन है tribble()।
व्यवहार में, tibble बनाने के दर्जनों तरीके हैं, क्योंकि Tidyverse के कई अलग-अलग फंक्शन इन्हें डिफॉल्ट आउटपुट फॉर्मेट के रूप में लौटाते हैं।
फंक्शनल प्रोग्रामिंग कॉन्सेप्ट में purrr लाइब्रेरी की चर्चा थी, जिससे वेक्टर और लिस्ट (एक-आयामी डेटा संरचनाएँ) में बदलाव किए जाते हैं।
डेटाफ्रेम (चाहे पुराने ढंग के हों या tibble) के लिए इस्तेमाल की जाने वाली लाइब्रेरी है dplyr।
डेटाफ्रेम, जिनमें tibble भी शामिल हैं, को कॉलम वेक्टर की लिस्ट की तरह देखा जा सकता है, इसलिए लिस्ट की इंडेक्सिंग से चुना हुआ कॉलम मिल जाता है।
tbl
# A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
tbl$created
#> [1] 1957 1993 1991 2012
डेटाफ्रेम को मैट्रिक्स जैसी इंडेक्सिंग से भी इंडेक्स किया जा सकता है।
tbl[c(2, 4), 1:2]
# A tibble: 2 × 2
#> languages created
#> <chr> <dbl>
#> 1 R 1993
#> 2 Julia 2012
आधुनिक R और Tidyverse के साथ, dplyr के फंक्शन आम तौर पर ज़्यादा लचीले और सुविधाजनक होते हैं, और इस कॉन्सेप्ट के बाकी हिस्से में हम इन्हीं पर ध्यान देंगे।
pull() से एक ही कॉलम निकाला जा सकता है, उसका नाम या क्रमिक संख्या देकर (ऋणात्मक संख्या देने पर गिनती दाएँ से बाएँ होती है)।
tbl |> pull(created)
#> [1] 1957 1993 1991 2012
यह नतीजा tbl$created जैसा ही है, लेकिन यहाँ पाइपलाइन में आसानी से इस्तेमाल होने वाला फंक्शन लगा है।
कई कॉलम निकालने के लिए सही फंक्शन है select(), जो बहुत लचीला है।
कॉलम के नाम या टाइप के गुणों के आधार पर कॉलम निकालिए (या हटाइए)।
# Range with position and/or name
tbl |> select(1:created)
# A tibble: 4 × 2
#> languages created
#> <chr> <dbl>
#> 1 Fortran 1957
#> 2 R 1993
#> 3 Python 1991
#> 4 Julia 2012
# Use type of column
tbl |> select(where(is.numeric))
# A tibble: 4 × 1
#> created
#> <dbl>
#> 1 1957
#> 2 1993
#> 3 1991
#> 4 2012
बूलियन ऑपरेटर &, | और ! (और, या, नहीं) इस्तेमाल करके कई शर्तें एक साथ दी जा सकती हैं।
इतनी शक्ति हमारे भाषाओं वाले छोटे डेटाफ्रेम पर कुछ बेतुकी लगती है।
अच्छी बात है कि dplyr में starwars नाम का tibble भी शामिल है, जो अभ्यास के लिए बड़ा डेटा देता है।
# pick a subset of columns
starwars |>
select(name | ends_with("color")) |>
head(5)
# A tibble: 5 × 4
#> name hair_color skin_color eye_color
#> <chr> <chr> <chr> <chr>
#> 1 Luke Skywalker blond fair blue
#> 2 C-3PO NA gold yellow
#> 3 R2-D2 NA white, blue red
#> 4 Darth Vader none white yellow
#> 5 Leia Organa brown light brown
किसी शर्त से मेल खाती पंक्तियाँ filter() से लीजिए, या उन्हें filter_out() से हटा दीजिए।
starwars |>
select(name:mass) |>
filter(between(height, 150, 165) & !is.na(mass))
# A tibble: 4 × 3
#> name height mass
#> <chr> <int> <dbl>
#> 1 Leia Organa 150 49
#> 2 Beru Whitesun Lars 165 75
#> 3 Nien Nunb 160 68
#> 4 Ben Quadinaros 163 65
फिल्टर की शर्तें मनमाने ढंग से जटिल हो सकती हैं, लेकिन हमेशा पंक्ति की सामग्री पर आधारित होती हैं।
अगर पंक्ति की संख्याएँ पता हों, तो हम slice() वाले कई फंक्शन इस्तेमाल कर सकते हैं।
starwars |>
select(name | homeworld) |>
slice(20:25)
# A tibble: 6 × 2
#> name homeworld
#> <chr> <chr>
#> 1 Palpatine Naboo
#> 2 Boba Fett Kamino
#> 3 IG-88 NA
#> 4 Bossk Trandosha
#> 5 Lando Calrissian Socorro
#> 6 Lobot Bespin
पहली चेतावनी: कॉपी-ऑन-मॉडिफाई डिफॉल्ट का मतलब है कि मूल tibble आम तौर पर वैसा ही रहता है।
ज़्यादातर बदलाव कॉलम के हिसाब से किए जाते हैं।
कॉलम के नाम rename(newname = oldname) से बदले जा सकते हैं, या फंक्शन लगाने के लिए rename_with() से।
rename() के अंदर के सिंटैक्स पर ध्यान दीजिए।
कॉलम oldname की सामग्री नाम newname से जुड़ती है, इसलिए क्रम ऐसा है।
कॉलम का क्रम relocate() से बदला जा सकता है।
डिफॉल्ट रूप से बताए गए कॉलम सबसे बाईं ओर चले जाते हैं, लेकिन बारीक जगह तय करने के लिए .before या .after आर्गुमेंट इस्तेमाल किया जा सकता है।
sw <- starwars |> select(name:species) |> slice(1:4)
sw
# A tibble: 4 × 11
#> name height mass hair_color skin_color eye_color birth_year sex gender homeworld species
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr> <chr>
#> 1 Luke Skywalker 172 77 blond fair blue 19 male masculine Tatooine Human
#> 2 C-3PO 167 75 NA gold yellow 112 none masculine Tatooine Droid
#> 3 R2-D2 96 32 NA white, blue red 33 none masculine Naboo Droid
#> 4 Darth Vader 202 136 none white yellow 41.9 male masculine Tatooine Human
sw |> relocate(c(species, homeworld), .after = name)
# A tibble: 4 × 11
#> name species homeworld height mass hair_color skin_color eye_color birth_year sex gender
#> <chr> <chr> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
#> 1 Luke Skywalker Human Tatooine 172 77 blond fair blue 19 male masculine
#> 2 C-3PO Droid Tatooine 167 75 NA gold yellow 112 none masculine
#> 3 R2-D2 Droid Naboo 96 32 NA white, blue red 33 none masculine
#> 4 Darth Vader Human Tatooine 202 136 none white yellow 41.9 male masculine
बड़े बदलावों के लिए, mutate() से आप ये कर सकते हैं:
NULL करके उसे हटा देना।साफ है कि mutate() बहुत शक्तिशाली है, कभी-कभी उलझाने वाला भी, और यही वजह है कि हमें कॉपी-ऑन-मॉडिफाई के लिए बहुत आभारी होना चाहिए।
Star Wars के पात्रों के बॉडी मास इंडेक्स की परवाह करने की कोई साफ वजह नहीं है, लेकिन फिर भी, अगर ज़रूरत पड़े तो:
starwars |>
select(c(name, species, height, mass)) |>
mutate(BMI = mass / (height / 100)^2) |>
head(4)
# A tibble: 4 × 5
#> name species height mass BMI
#> <chr> <chr> <int> <dbl> <dbl>
#> 1 Luke Skywalker Human 172 77 26.0
#> 2 C-3PO Droid 167 75 26.9
#> 3 R2-D2 Droid 96 32 34.7
#> 4 Darth Vader Human 202 136 33.3
अकेले tibble में बदलाव के लिए पंक्ति के हिसाब से काम कम आम है (कई tibble जोड़ने की बात आगे किसी कॉन्सेप्ट में होगी)।
एक अपवाद: arrange() पंक्तियों को एक या ज़्यादा कॉलम की वैल्यू के हिसाब से क्रम में लगाता है।
tbl
#> A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
tbl |> arrange(languages)
#> A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 Julia 2012 TRUE
#> 3 Python 1991 TRUE
#> 4 R 1993 TRUE
चेरी के एक बड़े बाग में आम तौर पर थोड़े-से अतिरिक्त पेड़ों का भंडार रहता है, जिन्हें वे इमारती लकड़ी के रूप में बेचना चाहते हैं। इससे बाग स्वस्थ बना रहता है और साथ ही कुछ अतिरिक्त आमदनी भी हो जाती है। उन्होंने आपसे कहा है कि पेड़ चुनने की प्रक्रिया में मदद के लिए आप कुछ सुविधाएँ कोड करें। इनकी ज़रूरत तब पड़ेगी जब कोई संभावित ग्राहक कॉल करे।
बिल्ट-इन datasets लाइब्रेरी में कई छोटे नमूना डेटासेट मौजूद हैं, और इस अभ्यास में trees डेटासेट इस्तेमाल किया गया है। डेटासेट को कॉल करने के लिए बस trees नाम लिखिए।
library(datasets) # loading is optional
trees |> head(3)
#> A data.frame: 3 × 3
#> Girth Height Volume
#> <dbl> <dbl> <dbl>
#> 1 8.3 70 10.3
#> 2 8.6 65 10.3
#> 3 8.8 63 10.2
ध्यान दीजिए कि इसमें तीन कॉलम हैं: Girth, Height और Volume.
शुरू में यह डेटासेट data.frame के रूप में रहता है, पर आप चाहें तो इसके साथ tibble की तरह काम करना चुन सकते हैं।
दुर्भाग्य से लिखने में एक गलती हो गई थी, जो आज तक ठीक नहीं की गई। डेटासेट में एक कॉलम का नाम Girth रह गया है, जबकि वह असल में Diameter है।
tree_data नाम का एक data.frame/tibble बनाइए, जो trees में Girth कॉलम का नाम बदलकर Diameter कर दे।
head(tree_data, 3)
#> A tibble: 3 × 3
#> Diameter Height Volume
#> <dbl> <dbl> <dbl>
#> 8.3 70 10.3
#> 8.6 65 10.3
#> 8.8 63 10.2
परिधि, यानी तने का घेरा, अब भी एक काम की माप है, और लकड़ी का भार भी। परिधि को πD से आसानी से निकाला जा सकता है, जहाँ D व्यास है। सारी लकड़ी का भार ρV से निकाला जा सकता है, जहाँ ρ = 35 चेरी की लकड़ी का घनत्व है और V लकड़ी का आयतन है।
girth_n_weight(data, rnd_digits) फंक्शन बनाइए, जो एक डेटाफ्रेम और संख्या वाले आर्गुमेंट लेता है। यह फंक्शन एक नया डेटाफ्रेम लौटाता है, जिसमें दो अतिरिक्त कॉलम होंगे: Girth और Weight, जिनके मान rnd_digits में दी गई अंकों की संख्या तक गोल किए गए हों।
girth_n_weight(tree_data, 1) |> head(3)
#> A tibble: 3 × 5
#> Diameter Height Volume Girth Weight
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 8.3 70 10.3 26.1 360.5
#> 8.6 65 10.3 27.0 360.5
#> 8.8 63 10.2 27.6 357.0
हर संभावित ग्राहक के लिए बाग डेटासेट की एक खास कॉपी रखता है, जो बिक्री में मदद करती है। इस डेटासेट में सबसे ज़रूरी कॉलम, यानी Weight और Height, सबसे आगे रखे जाते हैं, और सारी पंक्तियाँ Weight के हिसाब से क्रम में लगाई जाती हैं।
orchard_copy(data, important_cols) फंक्शन बनाइए, जो एक डेटाफ्रेम इनपुट के रूप में लेता है। यह एक नया डेटाफ्रेम लौटाता है, जिसमें Weight और Height सबसे आगे हों और पंक्तियाँ Weight के हिसाब से क्रम में लगी हों।
tree_data |> girth_n_weight() |> orchard_copy() |> head(3)
#> A tibble: 3 × 5
#> Weight Height Diameter Volume Girth
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 357 63 9 10 28
#> 360 70 8 10 26
#> 360 65 9 10 27
हर संभावित ग्राहक को उसकी पसंद के आधार पर डेटासेट का अपना एक संस्करण मिलेगा। इसके लिए ग्राहक कुछ बातें बता सकता है: किसी एक पेड़ की न्यूनतम ऊँचाई, अधिकतम ऊँचाई और अधिकतम भार।
customer_copy(data, min_height, max_height, max_weight) फंक्शन बनाइए। यह डेटाफ्रेम की एक ग्राहक वाली कॉपी लौटाता है, जिसमें Height, Weight, Diameter और Girth कॉलम हों और सिर्फ़ वही पंक्तियाँ हों जो दी गई सीमाओं के भीतर आती हैं।
tree_data |>
girth_n_weight(1) |>
orchard_copy() |>
customer_copy(65, 75, 1500) |>
head(3)
#> A tibble: 3 × 4
#> Height Weight Diameter Girth
#> <dbl> <dbl> <dbl> <dbl>
#> 70 360.5 8.3 26.1
#> 65 360.5 8.6 27.0
#> 66 546.0 11.0 34.6
Exercism पर साइन अप कीजिए और R को 21 कॉन्सेप्ट111 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।