ট্র্যাক
/
R
R
/
সিলেবাস
/
ডেটাফ্রেম
ডে

ডেটাফ্রেম মধ্যে R

১টি অনুশীলনী

ডেটাফ্রেম সম্পর্কে

সিলেবাসের অন্য জায়গায় আমরা ভিন্ন ভিন্ন বৈশিষ্ট্যের নানা ডেটা টাইপ দেখেছি।

এই কনসেপ্টে আমরা বহু-মাত্রিক ও ভিন্ন ধরনের ডেটা সংরক্ষণের উপায় দেখব। বাস্তবে দেখা যায়, বাস্তব-জগতের বেশিরভাগ ডেটাই এমন, তাই এখন আমরা R-কে (বেশিরভাগ ক্ষেত্রেই) কীভাবে ব্যবহার করা হয় সেই মূল জায়গায় পৌঁছে যাচ্ছি।

ডেটাফ্রেমের নানা প্রকার

কয়েক দশক ধরে R টেবিল আকারের ডেটা সামলাতে একাধিক ডেটা টাইপ যোগ করেছে।

এই সিলেবাসে আমরা মূলত tibble-এর দিকেই মনোযোগ দেব, তবে কিছু বিকল্প সম্পর্কে জানাও কাজে আসবে।

data.table

Tibble (নিচে বর্ণিত) আর data.table দুটিই Base R-এর প্রচলিত data.frame-কে উন্নত করার সমাদৃত প্রচেষ্টা। কোনটা "ভালো", তা নিয়ে বিতর্ক অনিবার্য। হয়তো নিচের বিষয়গুলো নিয়ে কিছুটা ঐকমত্য আছে (যদিও এগুলোকে অতিসরলীকরণ বলে সমালোচনা করা হবে)।

  • tibble মূলত ব্যবহারের সহজতা এবং Tidyverse ইকোসিস্টেমের সাথে সংযোগের জন্য অপটিমাইজ করা হয়েছে।
  • data.table মূলত কাঁচা ক্ষমতা ও স্কেলযোগ্যতার জন্য অপটিমাইজ করা হয়েছে, বিশেষ করে খুব বড় ডেটাসেট নিয়ে কাজ করার সময়।

যাই হোক, Exercism-এর ভিতরে data.table পাওয়া যায় না, তাই এখানে শুধু পূর্ণতার জন্য এর উল্লেখ করা হলো।

data.frame

Base 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

# look at the structure
str(df)
#> 'data.frame':	4 obs. of  3 variables:
 #> $ languages   : chr  "Fortran" "R" "Python" "Julia"
 #> $ created     : num  1957 1993 1991 2012
 #> $ has.syllabus: logi  FALSE TRUE TRUE TRUE

এখানে আমাদের আছে ক্যারেক্টার স্ট্রিং-এর একটি কলাম, সংখ্যার একটি কলাম আর বুলিয়ান-এর একটি কলাম। আকারে বড় করলে দেখা যায়, বাস্তব জগতের অনেক ডেটাসমষ্টি উপস্থাপনের সহজবোধ্য উপায় হলো এটি।

tibble

data.frame-এর ডিজাইন পুরনো।

কয়েক দশকের অভিজ্ঞতা, সঙ্গে R কীভাবে ব্যবহৃত হয় তার বদলে যাওয়া ধরন, এসব মিলে একটি নতুন ডিজাইনের দিকে নিয়ে গেল, আর Tidyverse-এ তৈরি হলো একটি আধুনিক বিকল্প: tibble।

Base 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      
  
str(tbl)
#> tibble [4 × 3] (S3: tbl_df/tbl/data.frame)
#>  $ languages   : chr [1:4] "Fortran" "R" "Python" "Julia"
#>  $ created     : num [1:4] 1957 1993 1991 2012
#>  $ has.syllabus: logi [1:4] FALSE TRUE TRUE TRUE

ডিফল্ট প্রিন্ট ফরম্যাটটি খেয়াল করুন: মাত্রা লেখা কমেন্ট লাইনটি স্বয়ংক্রিয়ভাবে ছাপা হয়, আর কলামের টাইপও দেখানো হয়।

tibble নিয়ে কাজ করা

Tibble হলো Tidyverse-এর মূল অংশ, তাই library(tibble) বা library(tidyverse) দিয়ে এটি যোগ করুন।

ডকুমেন্টেশন বেশ বিস্তৃত, Tidyverse-এর ধরনে:

tibble তৈরি করা

সবচেয়ে সহজে, data.frame()-এর মতোই, কলাম ভেক্টর জোড়া দিতে আমরা tibble() ফাংশনটি ব্যবহার করতে পারি। এর একটি উদাহরণ আগের একটি অংশে দেখানো হয়েছে।

সারি ধরে মান লিখতে বেশি সুবিধা হলে, তার জন্য সংশ্লিষ্ট ফাংশনটি হলো tribble()।

tbl_r <- tribble(
  # column names marked with tilde prefix
  ~languages, ~created, ~has.syllabus,
  "Fortran", 1957, FALSE,
  "R", 1993, TRUE,
  "Python", 1991, TRUE,
  "Julia", 2012, TRUE
)

tbl_r
# 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-এর নানা রকম ফাংশনের ডিফল্ট আউটপুট ফরম্যাটই হলো tibble। ভবিষ্যতের একটি কনসেপ্টে আমরা এখানে ফিরে আসব।

tibble ম্যানিপুলেট করা

ফাংশনাল প্রোগ্রামিং কনসেপ্টে ভেক্টর আর লিস্ট (১-মাত্রিক ডেটা স্ট্রাকচার) ম্যানিপুলেট করার purrr লাইব্রেরি নিয়ে আলোচনা করা হয়েছে।

ডেটাফ্রেমের জন্য (প্রচলিত হোক বা tibble), ব্যবহারযোগ্য সংশ্লিষ্ট লাইব্রেরিটি হলো dplyr।

আমরা আগে Switch কনসেপ্টে 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

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

# Exclude a column
tbl |> select(!created)
  # A tibble: 4 × 2
#>   languages has.syllabus
#>   <chr>     <lgl>       
#> 1 Fortran   FALSE       
#> 2 R         TRUE        
#> 3 Python    TRUE        
#> 4 Julia     TRUE        

# 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) ব্যবহার করে।

বৈধ R আইডেন্টিফায়ার এমন কলামের নাম একটি select()-এর ভিতরে কোটেশন ছাড়াই চলে। অবৈধ নাম (যেমন স্পেস থাকা নাম) ব্যাকটিক-এর ভিতরে রাখা যায়, যদিও সেগুলোর নাম বদলে ফেলা বরং ভালো হতে পারে।

select() ফাংশনটি কলামের নাম বাছাই করার অনেক হেল্পার ফাংশনের সাথে কাজ করতে পারে: starts_with, contains, num_range আর আরও নানা রকম। matches পূর্ণ RegEx মিল খোঁজার সুযোগ দেয়। বিস্তারিত জানতে ডকুমেন্টেশন দেখুন।

ভাষার ছোট্ট এই খেলনা ডেটাফ্রেম নিয়ে এত ক্ষমতা একেবারেই বাড়তি মনে হয়। ভাগ্য ভালো, starwars tibble টি dplyr-এর সাথেই দেওয়া আছে, তাই অনুশীলনের জন্য বড় কিছু পাওয়া গেল।

# limit display to top 3 rows of non-list columns
starwars |> 
  select(!where(is.list)) |> 
  head(3)
  # A tibble: 3 × 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  

# 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    

সারিভিত্তিক অপারেশন

Note

স্পষ্টতই, dplyr নাম দিয়ে কলাম বাছাই করার শক্তিশালী উপায় দেয়।

সারির নাম দিয়েও কি একই ধরনের কাজ করা যায়?

না! প্রচলিত R ডেটাফ্রেমে সারির নাম থাকতে পারে, কিন্তু (এরর আর পারফরম্যান্স সমস্যার দীর্ঘ ইতিহাসের পর) tibbles-এ সারির নাম অনুমোদিত নয়।

নাম চাইলে সেগুলো একটি <chr> কলামে (সাধারণত ১ নম্বর কলামে) রাখুন, যাকে অন্য যেকোনো কলামের মতোই ব্যবহার করা যায়। নামযুক্ত সারি সহ ডেটা ইমপোর্ট করার সময় as_tibble()-এর মতো ইমপোর্ট ফাংশন স্বয়ংক্রিয়ভাবে এটি তৈরি করে দেবে।

সারির নামের এই সীমাবদ্ধতা যদি অদ্ভুতভাবে কঠোর মনে হয়, মনে রাখবেন বেশিরভাগ বড় ডেটাবেস সিস্টেমও টেবিল একইভাবে সামলায়: Oracle, SQL Server, PostgreSQL, MySQL...

কিছু শর্তের সাথে মেলে এমন সারি 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   

# random sample of rows
starwars |> 
  select(name | homeworld) |> 
  slice_sample(n = 4)
  # A tibble: 4 × 2
#>   name            homeworld
#>   <chr>           <chr>    
#> 1 Shaak Ti        Shili    
#> 2 Luminara Unduli Mirial   
#> 3 Grievous        Kalee    
#> 4 Palpatine       Naboo    

ডুপ্লিকেট সারি সরাতে distinct() ব্যবহার করুন।

একটি tibble পরিবর্তন করা

প্রথম সতর্কতা: copy-on-modify ডিফল্টের কারণে মূল tibble সাধারণত অপরিবর্তিত থাকে।

বেশিরভাগ পরিবর্তনই কলামভিত্তিকভাবে প্রয়োগ করা হয়।

কলামের নাম বদলানো যায় rename(newname = oldname) দিয়ে, বা কোনো ফাংশন প্রয়োগ করতে rename_with() দিয়ে। সাধারণ একটি ব্যবহার হলো ইমপোর্ট করা নামগুলো পরিষ্কার করা, যাতে R-এ সেগুলো নিয়ে কাজ করা সহজ হয়: হোয়াইটস্পেস সরিয়ে এবং সম্পর্কিত নামগুলোর জন্য একই রকম ফরম্যাট নিশ্চিত করে।

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() শক্তিশালী, সম্ভাব্য বিভ্রান্তিকর, আর copy-on-modify-এর জন্য কৃতজ্ঞ থাকার একটি বড় কারণ।

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

mutate()-এর মতো ফাংশন দিয়ে কলামের একটি উপসেটে কাজ করতে চাইলে, উপরের উদাহরণের select() |> mutate() ক্রমটি একটি উপায়। ফলাফলে শুধু বাছাই করা কলামগুলোই থাকবে।

বিকল্পভাবে, mutate() কলের ভিতরে pick() ব্যবহার করাও সুবিধাজনক হতে পারে:

starwars |> mutate(pick(c(name, species, height, mass)), BMI = mass / (height / 100)^2) |> head(4)
When you want to operate on a subset of the columns with functions such as `mutate()`, the `select() |> mutate()` sequence in the above example is one option.
Only the selected columns will be in the result.

Alternatively, it can be convenient to use `pick()` _within_ the `mutate()` call:

```R
starwars |> mutate(pick(c(name, species, height, mass)), BMI = mass / (height / 100)^2) |> head(4)
    # A tibble: 4 × 15
#>   name         height  mass hair_color skin_color eye_color birth_year sex   gender homeworld species films vehicles
#>   <chr>         <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr>  <chr>     <chr>   <lis> <list>  
#> 1 Luke Skywal…    172    77 blond      fair       blue            19   male  mascu… Tatooine  Human   <chr> <chr>   
#> 2 C-3PO           167    75 NA         gold       yellow         112   none  mascu… Tatooine  Droid   <chr> <chr>   
#> 3 R2-D2            96    32 NA         white, bl… red             33   none  mascu… Naboo     Droid   <chr> <chr>   
#> 4 Darth Vader     202   136 none       white      yellow          41.9 male  mascu… Tatooine  Human   <chr> <chr>   
   # ℹ 2 more variables: starships <list>, BMI <dbl>

মিউটেশনে শুধু pick করা কলামগুলোই ব্যবহার হয়, কিন্তু ফেরত আসে সব কলাম।

একক 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     

সারসংক্ষেপ

ডেটাফ্রেম, প্রচলিত হোক বা tibble, আধুনিক R সাধারণত যেভাবে ব্যবহৃত হয় তার কেন্দ্রে রয়েছে।

Tidyverse-এর বেশিরভাগ ফাংশন (শুধু dplyr নয়) ইনপুট হিসেবে tibble নেয় এবং/অথবা আউটপুট হিসেবে tibble তৈরি করে।

এই কনসেপ্টটি শুধু একটি সংক্ষিপ্ত পরিচয় দিল, যা সম্ভব তার সবেমাত্র শুরু।

পরের কনসেপ্টগুলোতে ডেটাফ্রেমের আরও কয়েকটি দিক নিয়ে আলোচনা হবে (Exercism-এর কারিগরি সীমাবদ্ধতার মধ্যে)।

GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি নতুন একটি উইন্ডো বা ট্যাবে খুলবে

ডেটাফ্রেম শিখুন