Podstawowe obliczenia matematyczne w R

W tym wpisie przedstawione zostały najbardziej elementarne działania i obliczenia matematyczne z użyciem języka R. Przedstawione tu są również pewne elementy logiki działań na wektorach oraz kilka różnych przydatnych w praktyce funkcji (choć na temat tych ostatnich powstanie zapewne oddzielny wpis). Zatem, już bez zbędnego wprowadzenia, uruchamiamy RStudio i bierzemy te zagadnienia na warsztat.

1 Podstawowe działania matematyczne

A Dodawanie, odejmowanie, mnożenie i dzielenie liczb

1 + 5 + 24
# 30

number_1 <- c(1)
number_2 <- c(5)
number_3 <- c(24)
solution1 <- number_1 + number_2 + number_3
solution1
# 30

solution2 <- solution1 + 12.34 
print(solution2, digits = 4)
# 42.34
cat("Nasz wynik dodawania to:", solution2)
# Nasz wynik dodawania to: 42.34

Wykonanie linii 1 (kombinacja klawiszy Ctrl + Enter gdy kursor jest w danej linii) spowoduje wyświetlenie wyniku w konsoli (linia 2). Jeśli chcielibyśmy operować na wektorach (w tym przypadku będą to wektory jednowymiarowe), zawierających składniki naszej sumy, wykonujemy przypisanie wartości liczbowych, tak jak pokazano w liniach 4-6. Samą sumę przypisujemy również do wektora (linia 7). Należy pamiętać, że wykonanie przypisania sumy nie spowoduje automatycznie wyświetlenia jej wyniku. Aby wyświetlić wynik musimy wykonać polecenie wyświetlenia zawartości wektora z naszym wynikiem, czyli wywołać nazwę wektora (linia 8). Jak widzicie, możemy użyć również funkcji dedykowanych do wyświetlania zawartości obiektów w R, tj. print() lub cat(), dzięki którym możemy połączyć obiekt typu liczbowego, na przykład z ciągiem znaków, jak jest pokazane w liniach 11-15. W linii 11 do wyniku została dodana inna liczba z wartością po przecinku i przypisana do nowej zmiennej solution2. Funkcja print() pozwala na wyświetlenie zawartości wektora i określenie ile znaków liczbowych chcemy widzieć. Funkcja cat() wywołana w linii 15 pozwala nam połączyć tekst z wektorem liczbowym i wyświetlić je wg naszej potrzeby.

W sposób analogiczny obliczamy różnicę, a także wyrażenia mieszane oraz mnożenie i dzielenie (od linii 14). Warto też zauważyć jakie są wyniki działań z linii 8 i 10.

25 - 3 - 2
# 20
18 - 5 + 27 - 48 + 6
# -2

18 - 5 + 27
# 40
18 +- 5
# 13
18 -- 5
# 23


2 * 5
# 10
4.25 * 2
# 8.5

10 / 5
# 2
10 / 2.5
# 4

Nic nie stoi na przeszkodzie by wykorzystywać wektory z wieloma wartościami do wykonywania obliczeń. Dane obliczenia wykonają się wówczas równolegle na kolejnych parach liczb z obu wektorów.

x <- c(2, 4.3, 95, 1, -23, 0.24)
y <- c(1, -2, 1.4e-12, -4, 5, -6)

x + y
# 3.00   2.30  95.00  -3.00 -18.00  -5.76
x - y
# 1.00   6.30  95.00   5.00 -28.00   6.24
x * y
# 2.00   -8.60  1.33e-10   -4.00 -115.00   -1.44
x / y
# 2.00000 -2.15000 6.785714e+13 -0.25000 -4.60000 -0.04000

B Potęgowanie i pierwiastkowanie

Oto kilka przykładów potęgowania liczb w R:

3^2
# 9
3**2
# 9
2^10
# 1024
(-4)^2
# 16
(-4)^3
# -64
5^(-3)
# 0.008

Jak widzimy, w języku R operatorem potęgowania może być zarówno ^, jak i **.

Pierwiastek kwadratowy z liczby rzeczywistej większej od zera obliczamy następująco:

sqrt(144)
# 12

Pierwiastki wyższych stopni (oczywiście pierwiastek kwadratowy również możemy policzyć w ten sposób) zapisujemy jako potęgi o wykładnikach wymiernych:

9^(1/2)
# 3
125^(1/3)
#5

Sprawdźmy co się stanie w przypadku próby policzenia pierwiastka z liczby ujemnej:

sqrt(-4)
# NaN
# Komunikat ostrzegawczy:
# W poleceniu 'sqrt(-4)': wyprodukowano wartości NaN

Oczywiście nie da się policzyć takiego pierwiastka na zbiorze liczb rzeczywistych i została zwrócona wartość nieokreślona NaN, a dodatkowo R wyświetlił ostrzeżenie.

C Logarytmowanie

Logarytm, dla przypomnienia, to taka funkcja określona dla liczb: a, b > 0 \land a \ne 1 i zdefiniowana jako: log_a b = c \Leftrightarrow a^c = b

W języku R logarytmowanie możemy wykonać przy pomocy poniższych funkcji:

log(16, 4)
# 2
log(100, 10)
# 2
log2(16)
# 4
log(3)
# 1.098612

W kodzie w liniach 1 i 3, w funkcji log() pierwsza liczba to liczba logarytmowana, a liczba po przecinku to podstawa logarytmu. Jeżeli chcmy obliczać logarytmy przy podstawie 2, możemy również używać funkcji z linii 5. Gdy natomiast pominiemy wpisywanie podstawy logarytmu, R przyjmie za podstawę liczbę e, która jest podstawą logarytmu naturalnego (linia 7).

D Podstawowe działania na liczbach zespolonych

Na początek w kodzie R musimy zapisać liczbę zespoloną postaci z =a+bi, co czynimy następująco:

z <- complex(real = 2, imaginary = 3)
z
# 2+3i

z <- 2 + 3i
z
# 2+3i

Re(z)
# 2
Im(z)
# 3
Mod(z)
# 3.605551
Conj(z)
# 2-3i

sqrt(-1)
# NaN
sqrt(as.complex(-1))
# 0+1i

Tworzymy wektor z (linia 1), przechowujący liczbę zespoloną, którą zapisujemy przy użyciu funkcji complex(). W funkcji tej określamy część rzeczywistą i część urojoną liczby. Możemy też wprost przypisać liczbę zespoloną, w zapisie stosując jednostką urojoną i (linia 5).

Dla liczby zespolonej możemy wyświetlić część rzeczywistą, zespoloną, możemy wyznaczyć moduł liczby zespolonej oraz liczbę sprzężoną (linie 9-16).

Wiadomo, że dla liczb zespolonych zachodzi równość: i^2=-1. Gdy próbujemy to sprawdzić funkcją sqrt(), dostajemy wartość NaN, ponieważ R „nie wie”, że chodzi nam w tym przypadku o działanie na zbiorze liczb zespolonych. Dlatego przed wykonaniem pierwiastkowania musimy określić typ danych funkcją as.complex(), co daje nam poprawny wynik w linii 8.

Sprawdźmy jeszcze dla zasady wykonanie podstawowych działań na liczbach zespolonych, co już raczej nie będzie wymagało dalszego komentarza:

z1 <- 2 + 3i
z2 <- 3 - 4i

z1 + z2
# 5-1i
z1 - z2
# -1+7i
z1 * z2
# 18+1i
z1 / z2
# -0.24+0.68i
log(z)
# 1.282475+0.982794i
sqrt(z)
# 1.674149+0.895977i

E Kilka innych przydatnych funkcji

Warto wiedzieć jak w R można tworzyć na przykład ciągi liczbowe (lub znakowe w innych zastosowaniach). Spójrzmy na funkcje poniżej:

c(1:8)
# 1 2 3 4 5 6 7 8

rep(1.618, 8)
# 1.618 1.618 1.618 1.618 1.618 1.618 1.618 1.618

seq(1, 12, 1)
# 1  2  3  4  5  6  7  8  9 10 11 12

Funkcja z linii 1 pozwala na generowanie ciągów arytmetycznych z krokiem równym wartości 1 (dla ciągów rosnących) lub -1 (dla ciągów malejących). Replikacja rep() z linii 4 (mogą to liczby lub ogólnie znaki alfanumeryczne) pozwala na wypisanie wskazanych elementów n-razy. Funkcja seq() z linii 7 to nic innego jak sekwencja liczb, ale ze wskazanym przez nas krokiem.

Czasem chcemy policzyć silnię danej liczby. Możemy użyć wbudowanej funkcji factorial(), jak w przykładzie poniżej:

factorial(5)
# 120
factorial(145)
# 8.047926e+251
factorial(1000)
#  Inf

Gdy przekroczymy zakres zmiennej typu rzeczywistego double, wówczas R zwraca wyrażenie Inf, czyli nieskończoność.

2 Podstawowe obliczenia matematyczne

A Równanie z jedną niewiadomą

Spróbujmy rozwiązać najprostszy typ równań, czyli równanie z jedną niewiadomą, weźmy równanie: 2,5x=20
Zapisujemy kod w R:

solve(2.5, 20)
# 8

Naszym rozwiązaniem jest x=8

B Równania z dwiema i więcej niewiadomymi

Równanie z dwiema i większą liczbą niewiadomych rozwiązujemy przy pomocy R nieco inaczej niż te z jedną niewiadomą. Zacznijmy od przedstawienia równań, niech to będzie dla przykładu następująca para:
4x+8y=5
12x-0,5y=10

Teraz musimy zapisać równanie na macierzach w postaciach jak poniżej, byśmy lepiej zrozumieli następny krok:

\begin{pmatrix} 4 & 8 \\ 12 & 0.5 \end{pmatrix} \sdot \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} 5 \\ 10 \end{pmatrix}

Zapisujemy obie macierze w kodzie:

m1 <- matrix(c(4, 12, 8, 0.5), nrow = 2, ncol = 2)
m1
#      [,1] [,2]
# [1,]    4   8.0
# [2,]   12   0.5

m2 <- matrix(c(34, 12), nrow = 2, ncol = 1)
m2
#      [,1]
# [1,]   5
# [2,]   10

Teraz, aby wyznaczyć wartości niewiadome rozwiązujemy równanie z użyciem utworzonych macierzy:

> solve(m1, m2)
#           [,1]
# [1,] 0.8244681
# [2,] 0.2127660

Stąd mamy parę szukanych wartości: x = 0,82, \space y = 0,21.

C Różniczkowanie

Weźmy dla przykładu funkcję: f(x)=x^2+3x i spróbujmy wyznaczyć jej pochodną przy pomocy funkcji deriv():

stats::deriv(~ x^2+3*x, "x")
# expression({
#    .value <- x^2 + 3 * x
#    .grad <- array(0, c(length(.value), 1L), list(NULL, c("x")))
#    .grad[, "x"] <- 2 * x + 3
#    attr(.value, "gradient") <- .grad
#    .value
# })

Funkcji deriv() używamy wpisując jako pierwszy argument naszą funkcję poprzedzoną znakiem tyldy, a po przecinku podajemy względem jakiej zmiennej ma nastąpić różniczkowanie, w naszym przypadku – względem x. W powyższym przykładzie w linii 5 kodu funkcja zwróciła nam wynik różniczkowania.

Przy tej okazji warto zrobić dygresję o pewnej kwestii dotyczącej używania funkcji z różnych pakietów w R. W linii 1 intencjonalnie użyte zostało polecenie stats::deriv(), które znaczy tyle, co: „użyj funkcji deriv() z pakietu o nazwie stats. Funkcje, które użyte były wcześniej są zawarte w jednym ze standardowych pakietów R zwanym base i gdybyśmy chcieli być bardzo skrupulatnymi, powinniśmy pisać kod w postaci np.: base::solve(), base::matrix(), stats::deriv(), itd. Jednak nie musimy tego za każdym razem robić, ponieważ R „domyśla się” z jakiego pakietu pochodzą funkcje, ale uwaga – funkcje mające unikatowe nazwy. Należy pamiętać, że jednak może się zdarzyć, że w dwu lub więcej różnych pakietach wystąpią funkcje o tych samych nazwach i wówczas R prawdopodobnie zwróci błąd – wtedy musimy podać nazwę pakietu, z którego chcemy użyć danej funkcji.

Może się oczywiście zdarzyć, że potrzebujemy zróżniczkować funkcję po kilku zmiennych. Użyjmy dla tego przykładu funkcji: f(x,y)=5x-y^2 . Użyjemy w naszym edytorze kodu polecenia jak niżej i dostajemy wynik w takiej formie jak we wcześniejszym przykładzie:

deriv(~ 5*x - y^2, c("x", "y"))
# expression({
#    .value <- 5 * x - y^2
#    .grad <- array(0, c(length(.value), 2L), list(NULL, c("x", 
#        "y")))
#    .grad[, "x"] <- 5
#    .grad[, "y"] <- -(2 * y)
#    attr(.value, "gradient") <- .grad
#    .value
# })

Mamy więc w liniach 6 i 7 wyznaczone pochodne względem x i y, odpowiednio. Jeśli komuś nie podoba się taka długa forma wynikowa, istnieje krótsza, którą wykonujemy poleceniem Deriv():

Deriv(~ 5*x - y^2, c("x", "y"))
# c(x = 5, y = -(2 * y))

W linii 2 mamy zwięzły wynik w postaci wektora. Na marginesie warto wspomnieć (choć metody operowania na wektorach pojawią się w oddzielnym wpisie) jak w języku R sprytnie używa się wektorów i ich składowych:

D <- Deriv(~ 5*x - y^2, c("x", "y"))
D
# c(x = 5, y = -(2 * y))

D$x
# 5
D$y
# -(2 * y)

Użyłem tej samej funkcji Deriv(), którą tym razem przypisałem do zmiennej D. Po jej wywołaniu pojawia się wektor c() jak w przykładzie wcześniej, ale możemy równie dobrze wypisać w konsoli tylko pochodną względem x lub y. Używamy w tym celu operatora $, przy pomocy którego wybieramy składową wektora D nazwaną x lub y i dostajemy wyniki, jak w linii 6 i 8.

D Całkowanie

W tym wpisie przedstawię standardową metodę całkowania w R, z użyciem całki w określonych granicach, a więc policzona zostanie końcowa wartość liczbowa. Spróbujmy scałkować funkcję postaci: f(y)=3x^2 +1 w granicach, które wynoszą: 1 i 2 .

f <- function(x) {3*x^2 + 1}
integrate(f, lower = 1, upper = 2)
# 8 with absolute error < 8.9e-14

W linii 3, gdzie zwrócony jest poprawny wynik operacji całkowania pojawia się informacja o błędzie, który wynosi praktycznie zero, ale istnieje. Bierze się to stąd, że całkowanie w R wykonywane jest numerycznie i wynik jest tak naprawdę przybliżony, ale jak widać na tym prostym przykładzie wynik jest oczywiście zadowalający, a błąd można zaniedbać.

Przedstawione w tym wpisie informacje to tylko zarys tego, jak możemy wykorzystać język R w obliczeniach, ale może być podstawą do dalszych samodzielnych ćwiczeń i prób.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *