Vectors কনসেপ্টে আমরা দেখেছি যে একটি ভেক্টর শর্তভিত্তিক এক্সপ্রেশনে ব্যবহার করা যায়, ফলে একটি লজিক্যাল ভেক্টর পাওয়া যায়। এটি আবার all() আর any()-এর মতো ফাংশনে ব্যবহার করা যায়।
v <- c(4, 7, 10)
v >= 6
#> [1] FALSE TRUE TRUE
all(v > 6)
#> [1] FALSE # not all elements match this condition
any(v > 6)
#> [1] TRUE # at least one element matches
এই কৌশলটি এর চেয়ে অনেক বেশি শক্তিশালী।
কোনো ভেক্টরের বাছাই করা এলিমেন্ট ইনডেক্স নম্বর বা ইনডেক্সের একটি ভেক্টর দিয়ে বের করে আনা যায়:
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
অথবা, মূল ভেক্টরটি ফিল্টার করতে একটি লজিক্যাল ভেক্টর ব্যবহার করুন, যেটি TRUE মানের সাথে মেলে এমন এন্ট্রিগুলোর একটি সাবসেট ফিরিয়ে দেয়:
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
সেখান থেকে বুলিয়ান ভেক্টরটি একটি শর্তভিত্তিক এক্সপ্রেশন দিয়ে তৈরি করা এক ধাপের ব্যাপার:
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
এই শর্তভিত্তিক এক্সপ্রেশনগুলো ইচ্ছেমতো জটিল হতে পারে, যেখানে একাধিক ভেক্টর বা ফাংশন জড়িত থাকে:
v <- 1:10
w <- 10:1
# pairwise comparison
v > w
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
# shorter form
v[v > w]
#> [1] 6 7 8 9 10
আমরা এই কনসেপ্টের নাম দিয়েছি "ভেক্টর ফিল্টারিং", এই আশায় যে এতে সবচেয়ে বেশি সংখ্যক প্রোগ্রামারের কাছে বিষয়টি বোধগম্য হবে।
R কমিউনিটিতে অবশ্য "Subsetting" শব্দটি সম্ভবত বেশি প্রচলিত।
সহজ রাখতে উপরের সব উদাহরণে একই দৈর্ঘ্যের ভেক্টর তুলনা করা হয়েছে। দৈর্ঘ্য মিলে না গেলে কী হয়?
উত্তরটি হলো, সচেতনভাবে ব্যবহার করলে জিনিসটা খুব শক্তিশালী, কিন্তু অপ্রত্যাশিতভাবে ঘটলে বিভ্রান্তিকর।
একটি উদাহরণ:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
এখানে কী হয়েছে? আমরা দৈর্ঘ্য ৬-এর একটি ভেক্টরকে দৈর্ঘ্য ২-এর একটি বুলিয়ান দিয়ে ইনডেক্স করেছিলাম, কিন্তু ফলাফলে পেলাম দৈর্ঘ্য ৩-এর একটি ভেক্টর।
কোনো এক্সপ্রেশনে ব্যবহার করার সময় কোনো ভেক্টর যদি কোনোভাবে "খুব ছোট" হয়ে পড়ে, তাহলে R সেটি কাঙ্ক্ষিত দৈর্ঘ্যে পৌঁছানো পর্যন্ত পুনরাবৃত্তি করে: এটাকেই বলে "রিসাইক্লিং"। এতে আমাদের c(TRUE, FALSE) ভেক্টরটি হয়ে গেল c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE), আর v[cond] তখন v-এর TRUE মানগুলো ফিরিয়ে দিল: এই ক্ষেত্রে বিজোড় সংখ্যাগুলো।
তাহলে পরের উদাহরণে কী ঘটছে?
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
প্রথম দেখায় এটা স্কেলার গুণ আর (একটু অদ্ভুত) স্কেলার যোগের মতো মনে হয়, কিন্তু R-এ স্কেলার বলে কিছু নেই।
বরং সংখ্যাগুলো রিসাইকেল করা হয়, যাতে v-এর দৈর্ঘ্যের সাথে মেলে এমন একটি ভেক্টর পাওয়া যায়, তারপর c(0.5, 0.5, 0.5) বা c(2, 2, 2) দিয়ে গাণিতিক অপারেশনটি এলিমেন্ট ধরে ধরে প্রয়োগ করা হয়।
রিসাইক্লিং খুব, খুবই কাজের, তবে আপনি সত্যিই এটাই চান কি না নিশ্চিত হয়ে নিন।
ভেক্টরের দৈর্ঘ্যে সামান্য অমিল (যেমন ৯৯ বনাম ১০০) বিভ্রান্তিকর bug তৈরি করতে পারে।
কিছু লেখক মনে করেন, রিসাইক্লিং শুধু তখনই ব্যবহার করা উচিত যখন কোনো একটি ভেক্টরের দৈর্ঘ্য ১।
which() ফাংশনটি একটি বুলিয়ান আর্গুমেন্ট নেয় এবং এমন ইনডেক্সের একটি ভেক্টর ফিরিয়ে দেয় যেগুলো TRUE দেয়।
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
আগের অংশগুলোতে কোনো একটি মানদণ্ডের সাথে মেলে এমন এন্ট্রি খোঁজার কথা ছিল।
আপনার যদি শুধু জানতে হয় যে ভেক্টরে নির্দিষ্ট কোনো এলিমেন্ট আছে কি না, তাহলে %in% অপারেটরটি ব্যবহার করা হয়।
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
এটি কার্যকারিতার দিক থেকে নিচের কোডের সমতুল্য, তবে অনেক বেশি সুবিধাজনক:
length(v[v == 3]) > 0
#> [1] TRUE
এছাড়া %notin% অপারেটরও আছে, যা %in%-এর লজিক্যাল not দেয়।