जब आप समरूप एटॉमिक डेटा के संग्रह संभालते हैं (जैसे सब संख्यात्मक या सब अक्षर), तो R में ये संरचनाएँ उपलब्ध हैं:
vector एक-आयामी डेटा के लिए, जिसे एक ही इंडेक्स से एक्सेस किया जाता है।matrix दो-आयामी, आयताकार (जिनकी लंबाई असमान न हो) डेटा के लिए।array सामान्य N-आयामी स्थिति के लिए। एक मैट्रिक्स बस N=2 वाला array ही है।Python प्रोग्रामर के लिए: यह हिस्सा आपको जाना-पहचाना लग सकता है।
NumPy से इसकी समानता महज़ संयोग नहीं है (लेकिन R थोड़ा पुराना है, और यह सब इसकी मूल भाषा में ही मौजूद है)।
मैट्रिक्स मूल रूप से एक वेक्टर ही है, बस इसके साथ आकार तय करने के लिए पंक्तियों और स्तंभों की संख्या जुड़ी होती है।
इन्हें बनाने के लिए एक matrix() फंक्शन है:
# data already available
matrix(1:6, nrow=2, ncol=3)
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
# empty matrix to be filled in later
matrix(nrow=2, ncol=3)
#> [,1] [,2] [,3]
#> [1,] NA NA NA
#> [2,] NA NA NA
ध्यान दीजिए कि डेटा कॉलम-मेजर क्रम में संग्रहीत होता है (यानी पहले नीचे की ओर, फिर अगले स्तंभ की ओर)। यह Fortran, Matlab और Julia जैसा ही है, लेकिन C/C++ और NumPy से अलग है।
किसी मैट्रिक्स के आयाम जानने के लिए dim() पंक्तियों और स्तंभों दोनों देता है, और एक ही आयाम पाने के लिए nrow() या ncol() इस्तेमाल कीजिए। length() सिर्फ अंदर वाले वेक्टर की लंबाई लौटाता है: यानी एलिमेंट की कुल संख्या (ऊपर के उदाहरण में 6)।
कुछ और फंक्शन भी मैट्रिक्स वाला आउटपुट बनाते हैं, जैसे str_split_fixed()।
library(stringr)
str_split_fixed(c("split some words", "and some more"), " ", 3)
#> [,1] [,2] [,3]
#> [1,] "split" "some" "words"
#> [2,] "and" "some" "more"
यह वेक्टर इंडेक्सिंग की तरह ही हर तरीके से काम करता है, बस इसमें पंक्ति(यों) और स्तंभ(ओं) को उसी क्रम में बताना पड़ता है। किसी आयाम को खाली छोड़ने पर पूरी पंक्ति या पूरा स्तंभ मिल जाता है।
ऊपर वाला ही मैट्रिक्स लेते हैं:
m <- matrix(1:6, nrow=2, ncol=3)
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m[2, 3] # row 2, col 3
#> [1] 6
m[1:2, 3] # rows 1:2, col 3
#> [1] 5 6
m[2, ] # all of row 2
#> [1] 2 4 6
ध्यान दीजिए कि अगर सबसेट में एक ही पंक्ति और/या एक ही स्तंभ हो, तो वह मैट्रिक्स नहीं, बल्कि एक वेक्टर के रूप में लौटाया जाता है।
class(m)
#> [1] "matrix" "array"
class(m[2,])
#> [1] "integer"
इस तरह का dimension reduction हमेशा मनचाहा नहीं होता, खासकर ऐसी फंक्शन पाइपलाइन में जो मैट्रिक्स इनपुट की उम्मीद करती है।
drop=FALSE देने पर मैट्रिक्स इनपुट से मैट्रिक्स ही रिटर्न वैल्यू मिलने की गारंटी रहती है। ध्यान दीजिए कि जिस मैट्रिक्स में सिर्फ एक पंक्ति या एक स्तंभ हो, वह वेक्टर जैसा टाइप नहीं होता।
m[2, , drop = FALSE]
#> [,1] [,2] [,3]
#> [1,] 2 4 6
as.matrix() फंक्शन एक वेक्टर को एक-स्तंभ वाले मैट्रिक्स में बदल देता है।
as.matrix(1:3)
#> [,1]
#> [1,] 1
#> [2,] 2
#> [3,] 3
वेक्टर की तरह ही, मैट्रिक्स के हिस्सों को बस नई वैल्यू असाइन करके बदला जा सकता है।
m[, 2] <- c(10, 20)
m
#> [,1] [,2] [,3]
#> [1,] 1 10 5
#> [2,] 2 20 6
वेक्टर में डेटा जोड़ने और आगे बढ़ाने के लिए c() फंक्शन काफी था। मैट्रिक्स की बनावट ज़्यादा जटिल है, इसलिए यह बताने में ज़्यादा ध्यान देना पड़ता है कि आपको क्या चाहिए।
rbind() और cbind() फंक्शन मैट्रिक्स में नई पंक्ति(याँ) या स्तंभ जोड़ते हैं।
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m2 <- matrix(7:9, nrow = 1, ncol = 3)
rbind(m, m2)
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
#> [3,] 7 8 9
किसी पंक्ति या स्तंभ को हटाने के लिए वेक्टर की तरह ही ऋणात्मक इंडेक्सिंग इस्तेमाल होती है।
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m[, -2]
#> [,1] [,2]
#> [1,] 1 5
#> [2,] 2 6
वेक्टर के सारे फंक्शन मैट्रिक्स पर भी काम करते हैं, और सभी एलिमेंट पर लागू होते हैं। लेकिन हो सकता है कि वे मैट्रिक्स की बनावट खो दें और एक वेक्टर लौटा दें।
sqrt(m)
#> [,1] [,2] [,3]
#> [1,] 1.000000 1.732051 2.236068
#> [2,] 1.414214 2.000000 2.449490
sum(m)
#> [1] 21
m[m > 3]
#> [1] 4 5 6
मैट्रिक्स के लिए colSums() और rowSums() फंक्शन भी काम आ सकते हैं।
यहाँ आयाम में कमी लाना जान-बूझकर किया जाता है, इसलिए रिटर्न वैल्यू वेक्टर होती हैं।
# m as defined above
rowSums(m)
#> [1] 9 12
colSums(m)
#> [1] 3 7 11
इसी तरह, औसत निकालने के लिए rowMeans() और colMeans() फंक्शन भी हैं।
अन्य कॉन्सेप्ट में हमने देखा था कि Tidyverse की लाइब्रेरी वेक्टर और लिस्ट (purrr) या डेटाफ्रेम (dplyr) पर काम करना आसान बना देती हैं।
लेकिन मैट्रिक्स और ऐरे ज़्यादातर Tidyverse इनपुट के दायरे से बाहर हैं (कम से कम अभी तो)।
apply() फंक्शन Base R का हिस्सा है, और मैट्रिक्स प्रोग्रामिंग पर बारीक नियंत्रण के लिए आज भी सबसे अच्छा टूल है।
पैरामीटर इस क्रम में हैं:
rowMeans() फंक्शन Base R में मौजूद है, लेकिन हम इसे apply() और mean() से भी बना सकते हैं।
randm <- sample(1:10, 16, replace = TRUE) |> matrix(4, 4)
randm
#> [,1] [,2] [,3] [,4]
#> [1,] 4 9 8 3
#> [2,] 6 8 5 5
#> [3,] 4 2 1 2
#> [4,] 4 2 5 4
apply(randm, 1, mean)
#> [1] 6.00 6.00 2.25 3.75
rowMeans(randm)
#> [1] 6.00 6.00 2.25 3.75
यह हिस्सा गणित का ऐसा स्तर मान लेता है जो Exercism के दायरे से बाहर है।
अगर यह आपको समझ न आए, तो इसे छोड़ दीजिए।
लेकिन R एक डेटा साइंस भाषा है, और बहुत से यूज़र का बैकग्राउंड अनुप्रयुक्त गणित और सांख्यिकी में रहता है। यह संक्षिप्त सारांश कुछ छात्रों के काम का हो सकता है।
आधुनिक R "साफ़ तौर पर" एक डेटा साइंस भाषा है: यह मुख्य रूप से सारणीबद्ध डेटा के साथ काम करने के लिए बनी है, और सांख्यिकीविदों, महामारी-विज्ञानियों और समाज-विज्ञानियों जैसे यूज़र में लोकप्रिय है। इस क्षेत्र में इसका मुख्य प्रतिद्वंद्वी Python का साइंटिफिक स्टैक है, जिसमें Pandas या Polars जैसे पैकेज शामिल हैं।
लेकिन पहले के दशकों में R को एक संभावित इंजीनियरिंग भाषा भी माना जाता था, जो सीधे Matlab से मुकाबला करती थी।
शायद हैरानी की बात है कि Base R अब भी वेक्टर और मैट्रिक्स पर रेखीय बीजगणित की पूरी श्रृंखला के ऑपरेशन समर्थित करता है। यह साफ़ नहीं है कि अब ये एंड-यूज़र कोड में कितने काम आते हैं, लेकिन सांख्यिकीय फंक्शन लागू करने के लिए ऐसी तकनीकें अंदरूनी तौर पर बहुत ज़रूरी बनी हुई हैं।
R में LinAlg अब भी कुछ कॉलेज कोर्सों में पढ़ाया जाता है, जैसे यह इकोनोमेट्रिक्स क्लास। गणित (प्रोग्रामिंग नहीं) की अच्छी मार्गदर्शिका 3Blue1Brown पर है।
Exercism के भीतर रेखीय बीजगणित की सबसे पूरी चर्चा Julia सिलेबस में है (वही लेखक जो R सिलेबस बनाते हैं)।
हमारा मानना है कि गंभीर रेखीय बीजगणित के काम के लिए अब Julia (या जहाँ numpy.linalg मॉड्यूल उपलब्ध हो, वहाँ Python) R से ज़्यादा उपयुक्त भाषा होगी।
वर्गाकार (N x N)) मैट्रिक्स A का मुख्य विकर्ण ऊपर-बाएँ (A[1,1]) से नीचे-दाएँ (A[N,N]) तक जाता है।
diag() फंक्शन का व्यवहार इस पर निर्भर करता है कि उसे कौन-सा आर्गुमेंट दिया गया है।
वेक्टर v के साथ diag(v) एक विकर्ण मैट्रिक्स बनाता है।
diag(1:3)
#> [,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 2 0
#> [3,] 0 0 3
वर्गाकार मैट्रिक्स A के साथ diag(A) मुख्य विकर्ण के एलिमेंट को एक वेक्टर के रूप में लौटाता है।
A
#> [,1] [,2] [,3]
#> [1,] 10 5 2
#> [2,] 9 3 7
#> [3,] 8 10 4
diag(A)
#> [1] 10 3 4
एक अकेले पूर्णांक k के साथ diag(k) एक k × k तत्समक मैट्रिक्स बनाता है: शायद यह आपके अनुमान के मुताबिक न हो।
गंभीर R की किताबें भी इस जगह "सोचिए ज़रा" जैसी टिप्पणी जोड़ देती हैं।
diag(3)
#> [,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 1 0
#> [3,] 0 0 1
पंक्तियों और स्तंभों की जगह बदलने के लिए t() फंक्शन किसी भी मैट्रिक्स का ट्रांसपोज़ बना देता है।
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
t(m)
#> [,1] [,2]
#> [1,] 1 2
#> [2,] 3 4
#> [3,] 5 6
किसी सम्मिश्र-मान वाले मैट्रिक्स के लिए नतीजा सम्मिश्र संयुग्मी नहीं होता।
मैट्रिक्स A का संयुग्मी ट्रांसपोज़ (एडजॉइंट) पाने के लिए Conj(t(A)) इस्तेमाल कीजिए।
अधिक आयाम वाले ऐरे के लिए aperm() आयामों का क्रम बदल देता है।
वर्गाकार मैट्रिक्स A का व्युत्क्रम निकालने के लिए हम solve(A) इस्तेमाल कर सकते हैं।
हममें से कुछ को यह सिंटैक्स ... अप्रत्याशित लगता है।
किसी मैट्रिक्स को उसके व्युत्क्रम से गुणा करने पर तत्समक मैट्रिक्स मिलता है (बहुत छोटी राउंडऑफ त्रुटियों के अधीन)।
A <- matrix(sample(1:10, 9, replace = TRUE), 3, 3)
A
#> [,1] [,2] [,3]
#> [1,] 10 5 2
#> [2,] 9 3 7
#> [3,] 8 10 4
solve(A)
#> [,1] [,2] [,3]
#> [1,] 0.16666667 0.00000000 -0.08333333
#> [2,] -0.05747126 -0.06896552 0.14942529
#> [3,] -0.18965517 0.17241379 0.04310345
A %*% solve(A) |> round(digits = 10)
[,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 1 0
#> [3,] 0 0 1
+, -, *, ^ जैसे सामान्य अंकगणितीय ऑपरेटर मैट्रिक्स पर एलिमेंट-दर-एलिमेंट काम करते हैं।
अगर आयाम मेल न खाएँ, तो रीसाइक्लिंग के सामान्य नियम लागू होते हैं।
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m * m
#> [,1] [,2] [,3]
#> [1,] 1 9 25
#> [2,] 4 16 36
# same calculation, different syntax
m ^ 2
#> [,1] [,2] [,3]
#> [1,] 1 9 25
#> [2,] 4 16 36
वेक्टर और मैट्रिक्स के गुणा के बाकी तरह-तरह के प्रकार समझाना हमारे दायरे से बाहर है, फिर भी बहुत संक्षेप में:
%*% matmult() का इन्फिक्स ऑपरेटर रूप है, जो मैट्रिक्स गुणनफल देता है।%o% outer() का इन्फिक्स ऑपरेटर रूप है, जो बाह्य गुणनफल देता है।crossprod() क्रॉस गुणनफल देता है।