-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path02-base-R.Rmd
More file actions
431 lines (285 loc) · 18.7 KB
/
02-base-R.Rmd
File metadata and controls
431 lines (285 loc) · 18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# תחביר בסיסי {#sec-base-r}
בפרק זה נלמד את הבסיס של R, בעיקר נכיר את הפקודות הבסיסיות, אופרטורים (תנאים לוגיים) שונים, התניות, לולאות, סוגי משתנים ובניית פונקציות. בסיס זה נקרא הרבה פעמים גם Base R משום שהוא אינו מכיל חבילות הרחבה כלשהן, ומגיע עם התקנה חדשה של R.
פרק זה הוא בין הפרקים הבודדים בספר שמתמקד בעקרונות של תכנות כמו התניות, לולאות, ופונקציות. עקרונות אלו יהיו מוכרים מאוד למי שכבר למד תכנות, אך ייתכן שיהיו קצת יותר מאתגרים למישהו שמעולם לא למד תכנות. זה לא נורא, משום שהיכרות בסיסית עם העקרונות ומעט תרגול (כמו בתרגילים שמופיעים בפרק זה) יאפשרו גם למי שאין לו היכרות עם תכנות להתקדם לפרקים הבאים.
כדי לתרגל את הפקודות שתלמדו בפרק זה (ובפרקים הבאים) מומלץ לפתוח חלון של RStudio ולנסות את הפקודות השונות תוך כדי שאתם קוראים את הפרק.
## השמת משתנים, פעולות אריתמטיות ופונקציות
ניתן להריץ ב-R פעולות אריתמטיות (חיבור, חיסור, כפל, חילוק), פונקציות, ולהגדיר משתנים שונים. לדוגמה, הקוד הבא מגדיר משתנה `a` משתנה `b` ומכניס את הסכום שלהם למשתנה חדש שיקרא `a_plus_b`.
```{r sumab}
a <- 5
b <- 3
a_plus_b <- a + b
a_plus_b
```
שימו לב שההשמה לתוך משתנה מתבצעת עם האופרטור `->`, ניתן גם להשתמש ב`=` לצורך השמה, כתיב זה פחות נפוץ. לדוגמה:
```{r}
a_plus_b = a + b # this form of assignment `=` is less common, don't use it (use `<-`)
```
### קביעת שמות משתנים
קודם השתמשנו בשמות `a`, `b` , ו-`a_plus_b` כדי לקבוע משתנים. ככלל, מומלץ להשתמש בשמות קצרים בעלי משמעות. שמות משתנים חייבים להתחיל באות באנגלית, ויכולים להכיל אותיות, מספרים, קו תחתון, ונקודה. לדוגמה `gender`, `age`, `raw_data`, וכו'.
### סוגי משתנים
בבסיס השפה יש כמה סוגי משתנים, שקובעים מה סוג הערכים שהמשתנה יכול לקבל:
- מספר שלם (Integer)
- מספר רציף (Double)
- מחרוזת (Character)
- משתנה קטגוריות (Factor)
- תאריך (Date)
- משתנה לוגי (Logical)
כל משתנה חדש אנחנו מגדירים ב-R הוא למעשה וקטור. אגב, גם כשאנחנו מגדירים משתנה כערך בודד, בעצם הוא וקטור עם איבר אחד. אנחנו יכולים להשתמש בפקודה `c` (קיצור של המילה combine) כדי לשלב וקטורים שונים.
נראה דוגמאות להגדרות של וקטורים מסוגים שונים.
```{r variable definition examples}
some_integer <- c(1L, 2L, 3L) # The L sign stands for "Long integer"
some_integer
some_double <- c(1, 2, pi, exp(1))
some_double
some_character <- c("This", "is", "a", "character", "vector")
some_character
some_factor <- factor(c("Apples", "Oranges", "Paers", "Mangos", "Apples", "Oranges"))
some_factor
some_date <- c(Sys.Date(), as.Date("1993-08-01"))
some_date
some_logical <- c(TRUE, FALSE, FALSE, TRUE) # can also use c(T, F, F, T) is the same
some_logical
```
למשתני קטגוריות יש שימוש חשוב בסטטיסטיקה שעוד נראה אותו בפרקים הבאים, ולכן הוא מובחן ממשתנה מחרוזת ומקבל מקום של כבוד. כפי שניתן לראות, כאשר מדפיסים אותו, R מדווח גם על הרמות השונות שכלולות בו.
שימוש בפקודה `typeof(some_variable)` יציג את סוג המשתנה.
```{r using typeof}
typeof(some_integer)
typeof(some_double)
typeof(some_character)
typeof(some_date)
typeof(some_factor)
typeof(some_logical)
```
ניתן לשים לב ש-R מחשיב את המשתנה הקטגוריאלי כמספר שלם (integer) ואת התאריך כמספר רציף (double).
::: callout-tip
# תרגיל: חיבור של וקטורים מסוגים שונים
באמצעות הפקודה c והפקודה typeof בדקו מה קורה כאשר מחברים משתנים מסוגים שונים אחד לשני. האם התוצאה הגיונית? מה ההיגיון? האם יש מקרים בהם התוצאה של חיבור משתנים עשויה להטעות?
בדקו את `c(some_factor, some_character)` ודוגמאות נוספות.
:::
### קריאה לתתי וקטורים
ב-R ניתן לקרוא לחלק מסוים מתוך וקטור. לדוגמה, אם אנחנו רוצים רק את שני האיברים הראשונים מתוך הוקטור some_factor או את האיבר הראשון והרביעי מתוך some_character נשתמש בכתיב:
```{r subsetting vectors}
some_factor[1:2]
some_character[c(1,4)]
```
## רשימה (list)
כעת נדון במבנה נתונים שנקרא רשימה (list). רשימה היא אובייקט מרכזי ב-R שמאפשר לנו לאחד משתנים ווקטורים מסוגים שונים, לתוך dataset שיאפשר לנו בהמשך לנתח נתונים. ישנן מספר דרכים להגדיר רשימה, אחת מהן באמצעות הפקודה `list`. לדוגמה, הרשימה הבאה תכיל את כל הוקטורים שהגדרנו עד כה, מבלי שהם יאבדו מהמשמעות שלהם (כפי שקורה כשמנסים לעשות חיבור רגיל).
```{r a complete list example}
my_list <- list(my_int = some_integer,
my_double = some_double,
my_character = some_character,
my_factor = some_factor,
my_date = some_date)
my_list
typeof(my_list)
```
כדי לקרוא לוקטור מסוים מתוך רשימה ניתן להשתמש ב-\$ באופן הבא:
```{r select out of a list}
my_list$my_int
```
החלק הסופי בהצגה שלנו הוא רשימה מסוג מאוד מסוים, data.frame. מבנה נתונים זה הוא רשימה שבה כל הוקטורים באותו האורך. הוקטורים יכולים להיות מסוגים שונים כפי שציינו, ומה שחשוב ב-data.frame הוא שהוא הולך להיות אבן הפינה שלנו בכל ניתוח נתונים סטטיסטי. בינתיים נסתפק בהדגמה קצרה של הגדרת data.frame אך נרחיב עליו בפרקים הבאים.
```{r data frame demonstration}
my_data <- data.frame(name = c("Danny", "Moshe", "Iris", "Ronit"),
favorite_fruit = factor(c("Mango", "Apple", "Apple", "Paer")),
age = c(25L, 32L, 22L, 30L),
height = c(1.8, 1.75, 1.6, 1.68),
married = c(F, T, F, T))
my_data
typeof(my_data)
```
## שימוש בפונקציות
ניתן גם להפעיל פונקציות שונות, לדוגמה לוגריתם, פונקציות טריגונומטריות. למעשה בסעיף הקודם כבר ראינו מספר פונקציות כגון `c` ו-`typeof`. כעת נראה עוד מספר דוגמאות.
נסו להריץ את הקוד הבא, ולאחר מכן לענות על השאלות שלאחר מקטע הקוד. יש לשים לב שעל מנת להריץ את הפקודות בסוף המקטע (שקשורות ב-`my_data` נדרש קודם להגדיר את `my_data` כפי שהוגדר במקטע הקוד הקודם.
```{r functions-example, eval=FALSE}
log(100) # natural logarithm
log10(100) # base 10 logarithm
sin(pi) # sin(pi) is 0 but may give you a surprising answer, why?
sqrt(4) # square root of 4
mean(my_data$age)
sd(my_data$age)
summary(my_data)
```
::: callout-tip
# חשיבה על דיוק
שאלה למחשבה: בחלק מהמחשבים התשובה שמתקבלת ל-`sin(pi)` שונה מ-0.
לדוגמה
```
> sin(pi)
[1] 1.224606e-16
```
למה לדעתך?
:::
::: callout-tip
# הפקודה `summary`
הפקודה האחרונה שהרצנו בדוגמה היא פקודת summary. מה עושה הפקודה summary עבור כל סוג עמודה שהיא מוצאת בdata.frame?
:::
ככלל, הפעלת פונקציה ב-R תיראה כך:
```{r, eval=FALSE}
# some code which defines the variable `bar` and then
some_result <- some_function(foo = bar)
# or simply
some_result <- some_function(bar)
```
כאשר `some_result` יחזיק את התוצאה של הפונקציה. הפונקציה עצמה נקראת `some_function`, היא מקבלת ארגומנט (משתנה) יחיד שנקרא `foo` ואנחנו משתמשים במשתנה שערכו `bar` שנכנס לארגומנט.
כדי להמחיש נראה דוגמה נוספת, הפעם עם פונקציה פשוטה שגם נגדיר בעצמנו. נסו לעיין בקוד ולהבין מה המשמעות של כל שורה בקוד. שלושת השורות הראשונות בקוד מגדירות פונקציה חדשה, וההמשך מריץ אותה.
```{r new_function_def}
# define a new function which adds a number
one_plus <- function(number){
number + 1
}
# use the function:
one_plus(1)
one_plus(one_plus(1))
```
## אופרטורים
אופרטורים משמשים כדי להגדיר תנאים לוגיים שונים, לדוגמה אם אנחנו רוצים לבדוק את נכונותם של שני תנאים או יותר. ב-R נשתמש בתו כפול `||` כדי לציין "או" לוגי (or), ונשתמש בתו כפול `&&` על מנת לציין "וגם" לוגי (and).
```{r demonstration of logical operators}
a <- 5
b <- 6
(a < 3) && (b >= 3)
(a >= 5) || (b > 10)
```
ישנם גם פעולות לוגיות **וקטוריות**: כמו שניתן לחבר שני וקטורים, ניתן גם לבצע פעולות לוגיות איבר-איבר. פעולות אלו מבוצעות על ידי תו בודד: `|` או `&`.
```{r demonstration of vector logical operators}
v1 <- c(T, T, F, F)
v2 <- c(T, F, T, F)
v1 | v2
v1 & v2
```
::: callout-tip
# אופרטור `||` ו-`&&`
נסו לבחון מה קורה במקרה של `v1 || v2` או `v1 && v2`. מה החוקיות?
:::
אופרטור נוסף הוא אופרטור השלילה (not), נשתמש בתו `!` על מנת לייצג אותו. לדוגמה:
```{r not operator}
!c(T, F)
!v1
a==5
!(a==5)
```
שימו לב שבדוגמה האחרונה השתמשנו באופרטור נוסף אשר בודק אם שני אובייקטים הם בעלי אותו הערך. אופרטור זה מצויין עם שיוויון כפול `==`. ניתן גם להשוות שני משתנים זה לזה או להשוות שני וקטורים (איבר-איבר) באופן הבא:
```{r comparison}
a == b
c(1, 2, 3) == c(2, 1 , 3)
```
ישנם אופרטורים נוספים ב-R, אך בינתיים נסתפק באופרטורים שצוינו לעיל. כעת, לאחר שלמדנו על אופרטורים לוגיים, באפשרותנו ללמוד על התניות (if cluases) ועל לולאות (loops).
## התניות
במקרים רבים אנחנו רוצים להתנות פעולות מסוימות בקיומו של תנאי כלשהו. ניתן לבצע התניה זו באמצעות הפקודות `if`, `else if`, `else`.
המבנה הכללי של התניות יראה כך:
```{r, eval=FALSE}
if (condition1) {
# some code which evaluates if condition1 == TRUE
} else if (condition2) {
# some code which evaluates if condition1 == FALSE and condition2 == TRUE
} else {
# some code which evaluates if condition1 == FALSE and condition2 == FALSE
}
```
על מנת להדגים, נשתמש בדוגמה הבאה: נניח שאנחנו רוצים לבדוק אם ערך מסוים הוא מספר או לא. נוכל להשתמש בשילוב של `if` והפונקציה `is.numeric`:
```{r if demonstration}
some_value <- 100
if (is.numeric(some_value)) {
cat("This is indeed a numeric value!")
} else {
cat("This is not a numeric value!")
}
some_value <- "foobar"
if (is.numeric(some_value)) {
cat("This is indeed a numeric value!")
} else {
cat("This is not a numeric value!")
}
```
אפשר גם להשתמש בהתניות בתוך התניות, כלומר בתוך פקודת `if` להגדיר פקודת `if` נוספת.
::: callout-tip
# תרגיל בבניית פונקציה
נסו להרחיב את הדוגמה הקודמת, כך שבמידה ו-`some_value` הוא מספר המתחלק ב-2 אז יודפס "some_value is even" ובמידה ואינו מתחלק ב-2 יודפס "some_value is odd".
נסו למצוא יותר מדרך אחת לביצוע הרחבה זו.
:::
## לולאות
לולאות הן דרך נוחה כדי לגרום למחשב לעשות הרבה חזרות של אותה הפעולה (רק בשינוי ערכים מסוימים). ב-R ישנם סוגים שונים של לולאות, המתאפיינות בפקודות שונות אבל גם בזמן ריצה (יעילות) שונה.
- לולאות `for`
- לולאות `while`
- לולאות `repeat` `break` `next`
- לולאות באמצעות תכנות פונקציונלי (כדוגמת פקודת `lapply`, או פקודות מחבילות אחרות כמו `map` שנמצאת בחבילת `purrr`)
עדיף להימנע ככל שניתן משימוש בכל סוגי הלולאות `for`, `while`, ו-`repeat` בעבודה עם `R`. לולאות אלו מאוד לא יעילות וזמן הריצה שלהן ארוך. תכנות פונקציונלי הוא יותר יעיל, אך דורש קצת יותר "התרגלות". בכל זאת, נדגים את אופן הפעולה של לולאת `for`.
על מנת להגדיר לולאת `for` עלינו להגדיר ראשית את טווח הפעולה של הלולאה. לדוגמה, לולאה שרצה על המספרים 1 עד 100 תוגדר באופן הבא:
```{r, eval=FALSE}
for (i in 1:100){
# do some action
# you can use i for that but don't have to
}
```
טווח הפעולה של הלולאה לא חייב להיות מספרי, אפשר גם להשתמש באובייקטים נוספים. לדוגמה נשתמש בלולאה על סוגי פירות. בדוגמה הבאה נשלב גם שימוש בהתניות, כפי שלמדנו בסעיף הקודם. השילוב של לולאות והתניות די נפוץ בתכנות.
```{r}
fruits <- c("Mango", "Bannana", "Pineapple", "Orange", "Apple", "Prune", "Lemon", "Loquat")
my_garden <- c("Mango", "Orange", "Lemon", "Loquat")
for (current_fruit in fruits) {
if (current_fruit %in% my_garden) {
cat("\nI grow", current_fruit)
} else {
cat("\nI don't grow", current_fruit)
}
}
```
::: callout-tip
# ההבדל בין `%in%` לבין `in`
בדוגמה הקודמת השתמשנו ב-`in` וגם ב-`%in%`.
`in` נחשבת מילת מפתח, בעוד ש-`%in%` נחשבת אופרטור. עמדו על ההבדלים ביניהן וכתבו מה המשמעות של כל אחת, ומה התפקיד שלה בקוד.
:::
נדגים גם כיצד ניתן להחליף לולאה באמצעות פעולה וקטורית, ובאמצעות נוסחה מתמטית. נניח שאנחנו רוצים לחשב סכום של טור הנדסי, כלומר הסכום של $1, q, q^2, q^3, \ldots$ כאשר $q<1$. נציג שלוש דרכים לבצע את הפעולה הזו: לולאה (הדרך הכי פחות יעילה), פעולה וקטורית (להיעזר בוקטור, כלומר מערך של מספרים), והדרך היעילה ביותר שהיא כמובן פשוט להשתמש בנוסחה של טור הנדסי ($\frac{1}{1-q}$).
```{r three ways to calculate geometric series}
q <- 1/2
series_sum_loop <- 0
for (element_i in 0:50){
series_sum_loop <- series_sum_loop + q^element_i
}
series_sum_loop
series_sum_vector <- sum(q^(0:50))
series_sum_vector
series_sum_analytic <- 1/(1-q)
series_sum_analytic
```
חישוב של טור הנדסי הוא כמובן פעולה פשוטה, אבל לא בכל פעולה שנרצה לעשות תהיה נוסחה סגורה כמו שיש שלנו במקרה של טור הנדסי, וגם לא תמיד יהיה אפשר לבצע את הפעולה כפעולה וקטורית.
::: callout-tip
# תרגיל בבניית סדרת פיבונצ'י
סדרת פיבונצ'י היא סדרה של מספרים שבה כל איבר הוא הסכום של שני האיברים שקדמו לו. שני האיברים הראשונים מקבלים את הערך 1, ולכן האיברים הראשונים של הסדרה נראים כך:
$$
1, 1, 2, 3, 5, 8, 13, 21, 34,\ldots
$$ היעזרו בקוד הבא על מנת לבנות לולאה שתדפיס את 50 האיברים הראשונים של סדרת פיבונצ'י. עליכם להשלים את הקוד במקומות בהם מופיע סימן שאלה.
:::
```{r, eval=FALSE}
# Fibonachi code exercise, fill in the blanks (where you see `?`)
total <- ?
element_i_minus1 <- 1
element_i_minus2 <- 1
for (? in 3:total){
next_element <- element_i_minus1 + ?
element_i_minus2 <- element_i_minus1
? <- next_element
cat("\n", ?)
}
```
```{r fibonnachi solution, eval=FALSE, include=FALSE}
total <- 50
element_i_minus1 <- 1
element_i_minus2 <- 1
for (index in 3:total){
next_element <- element_i_minus1 + element_i_minus2
element_i_minus2 <- element_i_minus1
element_i_minus1 <- next_element
cat("\n", next_element)
}
```
## סיכום
בפרק זה למדנו את התחביר הבסיסי של שפת `R`.
- ראינו כיצד עובדת השמת משתנים
- למדנו על סוגי משתנים שונים
- ראינו כיצד לקרוא לתתי וקטורים (חלק מוקטור)
- למדנו על אובייקטים מסוג רשימה (list)
- למדנו על פונקציות שונות - כיצד להפעיל אותן וכיצד להגדיר פונקציות חדשות
- למדנו על אופרטורים (וגם, או, לא - NOT, והשוואה)
- למדנו על התניות (אם... אז... אחרת)
- למדנו על לולאות (מסוג `for`), והזכרנו סוגים נוספים.
עד כה הכלים שתיארנו הם כלים כלליים, במובן שבמרבית שפות התכנון ישנן מקבילות דומות. בפרק הבא נתעמק בכלים ייעודיים אשר נבנו ומשמשים לניתוח נתונים סטטיסטי (או ליתר דיוק בהכנת נתונים לניתוח נתונים סטטיסטיקה).
::: end-page