সিলেবাসের অন্য অংশগুলোতে আমরা ভিন্ন ভিন্ন বৈশিষ্ট্যসম্পন্ন নানা ডেটা টাইপ দেখেছি।
এই কনসেপ্টে আমরা বহুমাত্রিক, ভিন্নধর্মী ডেটা সংরক্ষণের উপায়গুলো দেখব। বাস্তবে বেশিরভাগ রিয়েল-ওয়ার্ল্ড ডেটাই এ রকম, তাই আমরা এখন R আসলে (বেশিরভাগ ক্ষেত্রে) কীভাবে ব্যবহৃত হয় তার একেবারে কেন্দ্রে পৌঁছে যাচ্ছি।
কয়েক দশক ধরে R ট্যাবুলার ডেটা সামলানোর জন্য একাধিক ডেটা টাইপ যোগ করেছে।
এই সিলেবাসে মূলত টিবল নিয়েই আলোচনা হবে, তবে বিকল্পগুলো সম্পর্কেও জানা কাজে লাগে।
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-এ এসেছে আধুনিক বিকল্প: টিবল।
বেস R-এর তুলনায় টিবলে আছে:
সংক্ষেপে, একটি tibble-এর লক্ষ্য হলো "কম কাজ করা আর বেশি অভিযোগ করা", যাকে "অলস ও রুক্ষ"ও বলা হয়।
টাইপগুলো সাধারণত বিনিময়যোগ্য: যে ফাংশন data.frame গ্রহণ করে, সেটি tibble-ও গ্রহণ করবে, এবং উল্টোটাও।
টাইপগুলোর মধ্যে রূপান্তর সহজ, as_tibble(df) ও as.data.frame(tbl) দিয়ে।
নতুন কাজের জন্য, টিবল ব্যবহার করলে সম্ভবত আরও শক্তসমর্থ কোড লিখতে সাহায্য হবে।
তবে 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
ডিফল্ট প্রিন্ট ফরম্যাটটি লক্ষ করুন: মাত্রা লেখা কমেন্ট লাইনটি স্বয়ংক্রিয়ভাবে প্রিন্ট হয়, আর কলামের টাইপগুলোও দেখানো হয়।
টিবল হলো Tidyverse-এর একটি মূল অংশ, তাই library(tibble) বা library(tidyverse) যেকোনোটি দিয়ে এটি যোগ করুন।
সবচেয়ে সহজে, data.frame()-এর মতোই, কলাম ভেক্টরগুলো জোড়া লাগাতে আমরা tibble() ফাংশনটি ব্যবহার করতে পারি।
এর একটি উদাহরণ আগের একটি অংশে দেখানো হয়েছে।
সারি ধরে মান লিখতে বেশি সুবিধা হলে, তার জন্য সংশ্লিষ্ট ফাংশনটি হলো tribble()।
বাস্তবে টিবল তৈরির ডজনখানেক উপায় আছে, কারণ Tidyverse-এর নানা রকম ফাংশনের ডিফল্ট আউটপুট ফরম্যাটই হলো টিবল।
ফাংশনাল প্রোগ্রামিং কনসেপ্টে ভেক্টর ও লিস্ট (একমাত্রিক ডেটা স্ট্রাকচার) ম্যানিপুলেট করার জন্য purrr লাইব্রেরি নিয়ে আলোচনা করা হয়েছে।
ডেটাফ্রেমের জন্য (সেটা প্রচলিত হোক বা টিবল), ব্যবহার করার মতো সংশ্লিষ্ট লাইব্রেরি হলো dplyr।
ডেটাফ্রেম, টিবলসহ, কলাম ভেক্টরের লিস্ট হিসেবে ধরা যায়, তাই লিস্ট ইনডেক্সিং দিয়ে নির্দিষ্ট একটি কলাম পাওয়া যায়।
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
Tidyverse ইকোসিস্টেমসহ আধুনিক R-এ, 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
বুলিয়ান অপারেটর &, | ও ! (and, or, not) ব্যবহার করে একাধিক শর্ত দেওয়া যায়।
ভাষার এই খেলনা ডেটাফ্রেমটি নিয়ে এত ক্ষমতা বেশ হাস্যকরই লাগে।
ভাগ্যক্রমে, dplyr-এর সঙ্গে starwars টিবলটি দেওয়া আছে, যা দিয়ে অনুশীলন করার জন্য বড় কিছু পাওয়া যায়।
# 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
প্রথম সতর্কতা: কপি-অন-মডিফাই ডিফল্টের কারণে মূল টিবলটি সাধারণত অপরিবর্তিত থাকে।
বেশিরভাগ পরিবর্তন কলাম-ভিত্তিকভাবে প্রয়োগ করা হয়।
কলামের নাম 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() শক্তিশালী, সম্ভাব্য বিভ্রান্তিকর, আর কপি-অন-মডিফাইয়ের জন্য ভীষণ কৃতজ্ঞ থাকার একটি কারণ।
স্টার ওয়ার্স চরিত্রগুলোর বডি মাস ইনডেক্স নিয়ে ভাবতে যাওয়ার তেমন স্পষ্ট কোনো কারণ নেই, তবে শুধু যদি কাজে লাগে:
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
সারি-ভিত্তিক অপারেশন একক টিবল পরিবর্তনের জন্য ততটা কমন নয় (একাধিক টিবল মার্জ করা নিয়ে পরের কোনো কনসেপ্টে আলোচনা হবে)।
একটি ব্যতিক্রম: 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।
trees ডেটাসেটের Girth কলামের নাম সংশোধন করে Diameter করে দেয় এমন একটি data.frame/tibble ডিফাইন করুন, যার নাম tree_data।
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টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।