-
Notifications
You must be signed in to change notification settings - Fork 1
/
06.Rmd
189 lines (138 loc) · 26.7 KB
/
06.Rmd
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
# Повторливи проекти {#proekti}
Во претходните две поглавја разгледавме како можеме нашиот едноставен код да го конвертираме во скрипта која ќе биде лесна за користење и до некоја мера издржлива во различни работни средини (Поглавје \@ref(conversion)) и како да составиме повторлив извештај со повеќе табели и графици со помош на `Rmd` (Поглавје \@ref(rmd)). Но како што напоменавме, сеуште правиме некои претпоставки за начинот на кој ќе биде користена скриптата и за ресурсите кои ќе им бидат достапни на корисниците. Една од овие претпоставки беше дека колегите кои ќе ја користат нашата скрипта ќе имаат адекватни табели за обработување. Ова е до некоја мера разумна претпоставка доколку скриптата се користи рамките на една организација, или доколку типот на табели кои ги обработува се доволно познати и распространети. Но ваквите случаи, иако значајни, претставуваат само дел инстанците кога треба да разговараме за повторливи анализи. Како што споменавме претходно во Секција \@ref(audience) голем дел од сферата на статистички анализи, наука за податоци, и истражувања од било кое поле на науката се однесува на споделување на програмскиот код _заедно_ со податоците, извештаите, презентациите, итн. Значи ни треба начин да го споделиме нашиот програмски код заедно со податоците кои ги обработуваме, инаку никој надвор од нашата организација нема да може да ја повтори анализата.
Во ова поглавје ќе се запознаеме со две стратегии за организирање на повторливи проекти. Првата стратегија (Направи сам) е едноставна за поставување и не зависи од дополнителна инфраструктура (додатни библиотеки), но ако проектот е комплексен, оваа опција може да бара повеќе грижа, особено за стабилност на подолг рок. Втората стратегија е да се повикаме на некоја веќе готова платформа за поставување на безбедна структура за повторливи проекти. Втората стратегија генерално треба да се преферира, но имајќи во предвид дека секој нов пакет си бара одреден период на запознавање и дека во некои случаи првата опција е сосема адекватна, ќе збориваме за двете стратегии.
## Стратегија 1: Направи сам
### Структура
Креирање безбедна структура за повторливост во основа е едноставно. Да речеме дека имаме извештај со два графикони и три табели, и дека секој график и табела се базирани на различни сетови на податоци. Имајќи ги во предвид нашите цели:
- да направиме што е можно помалку претпоставки за средината во којашто ќе биде повторена нашата анализа и
- да направиме што е можно полесно да се снајде некој без никакво претходно искуство со нашиот проект
можеме секој елемент од нашиот извештај да го енкапсулираме во посебен фолдер. Во секој фолдер мора да ги вклучиме **податоците и програмскиот код** коишто се неопходни за ре-креирање на графиконот или табелата. За детални објаснувања, можеме во (фали нешто тука?) да вклучиме `README` фалј во оние фолдери каде што е неопходно. Главен `README` фалј на првото ниво на нашиот проект **мора да биде вклучен**. Тука, го опишуваме нашиот проект, што подетално тоа подобро, и даваме објаснување за како да се користат податоците и кодот којшто е вклучен во субфолдерите. Ова е особено важно доколку скриптите зависат една од друга и треба да се извршат во некој редослед, на пример ако мора прво да го извршиме кодот во `tabela_1` пред да можеме да работиме со `tabela_2`.
Во нашиот случај, со два графикони и три табели сите со различни податоци, би ја имаме следната структура:
```
strategija_1/
├── grafik_1
│ ├── code_grafik_1.R
│ └── data_grafik_1.csv
├── grafik_2
│ ├── code_grafik_2.R
│ └── data_grafik_2.csv
├── README
├── tabela_1
│ ├── code_tabela_1.R
│ └── data_tabela_1.csv
├── tabela_2
│ ├── code_tabela_2.R
│ └── data_tabela_2.csv
└── tabela_3
├── code_tabela_3.R
└── data_tabela_3.csv
```
Доколку графиконите 1 и 2 се базирани на исти податоци како табелите 1 и 2, тогаш можеме да ги групираме, со што би избегнало непотребно копирање на истите податоци на две места:
```
strategija_1/
├── grafik_1
│ ├── code_grafik_1.R
│ ├── code_tabela_1.R
│ └── data_grafik_1.csv
├── grafik_2
│ ├── code_grafik_2.R
│ ├── code_tabela_2.R
│ └── data_grafik_2.csv
├── README
└── tabela_3
├── code_tabela_3.R
└── data_tabela_3.csv
```
Но генерално, при одлучување за структурата на проектот треба да си го поставиме прашањето „како најлесно ќе се снајде некој што никогаш претходно нема работено со овие податоци или тип на анализи?“. Доколку одговорот на ова прашање сугерира да се копираат истите податоци на две места, и податоците се доволно мали да го дозволат тоа, тогаш е подобро да се направи вишокот отколку да се направи ре-анализата покомплицирана.
### Предности
Како оваа стратегија овозможува повторливост? Доколку нашиот код ги следи принципите за кои збориваме претходно, спарувањето на податоците со кодот што треба да се користи за нивна обработка во еден фолдер изолирани од остатокот на проектот значи дека:
- шансите корисникот на нашиот проект да ги помеша фајловите се минимални,
- нашиот код може да биде поедноставен бидејќи вчитува податоци во истиот фолдер, и конечно
- сите резултати од извршувањето на нашиот код (на пример пдф од графикон) ќе се зачуваат во истиот фолдер.
Оваа едноставна, линеарна поврзаност е веројатно најбезбедна од аспект на поставување и користење подоцна од некој кој нема никакво претходно познавање. Дополнително, ваквата структура овозможува модуларност, во смисла на тоа дека секоја табела или графикон може да се ре-креира независно од остатокот на анализата, ако, на пример, нашиот соработник или рецензент сака да провери само одреден аспект на истражувањето.
### Недостатоци
Главните недостатоци на оваа стратегија се непотребното повторување на код или податоци (не секогаш, но често), и тоа што оваа организација станува незгодна ако имаме голем или комплициран извештај со многу анализи коишто би сакале да ги одделиме.
Можното непотребно повторување да податоци веќе спомнавме погоре како разлика помеѓу недостатоците на дуплицирање наспроти предноста што копирањето на една табела на две места може да подобри леснотијата на користење. Имено, доколку некоја скрипта има соодветно именувана табела што ја користи во соодветно именуван фолдер, тогаш дури и истата табела да се јави под друго име во друг фолдер каде што се отвара од друга скрипта, тоа не создава забуна. Од друга страна, доколку истата табела, под исто име, се наоѓа во заедничи фолдер `Табели`, тогаш треба за двете скрипти да објасниме дека ја користат истата табела и двете скрипти треба да повикуваат табела што е сместена надвор од соодветните фолдери. Второто сценарио е малку покомплицирано, и доколку табелата во прашање е доволно мала (во смисол на меморија) тогаш од перспектива на корисник кој прв пат го гледа проектот можеби не е проблем доколку се јавува два пати.
Сепак, генералната препорака е дека најдобро е да се избегне дупликација на податоци, бидејќи податоците се (типично) најголемиот (во смисол на меморија) дел од нашиот пакет, а сакаме нашиот пакет да биде што попортабилен.
За повторувањето на програмски код имаме слична дилема. Доколку нашите табели или графици, кои ги правиме со посебни скрипти во посебни фолдери, имаат делови со идентичен код (на пример сите скрипти ги прават истите проверки за табелите, или сите скрипти за табела ја прават истата сумација по групи), тогаш можеме делот од кодот што се повторува да го изолираме во посебна скрипта што сите други скрипти ќе ја повикуваат. Оваа опција е подобра бидејќи доколку најдеме грешка или сакаме да направиме некое подобрување, тоа го правиме само во скриптата со заеднички код, додека пак доколку тој код се дуплицира во повеќе скрипти, ќе треба да ги промениме сите. Во ваков случај со изолиран заеднички код, нашата структура може да се промени во нешто налик на следниот дијаграм.
```
strategija_1/
├── README
├── zaednicki-kod.R
├── grafik_1
│ ├── code_grafik_1.R
│ ├── code_tabela_1.R
│ └── data_grafik_1.csv
├── grafik_2
│ ├── code_grafik_2.R
│ ├── code_tabela_2.R
│ └── data_grafik_2.csv
└── tabela_3
├── code_tabela_3.R
└── data_tabela_3.csv
```
Вториот недостаток, незгодноста кога проектот станува голем и комплициран, станува јасен со претходниот дијаграм. Кога скриптите, и фунцкии внатре во скриптите, почнуваат да стануваат зависни една од друга, предностите на линерано изолирање на кодот и податоците почнуваат да се смалуваат. На пример, ако `code_tabela_3.R` има функција `sumiraj-po-grupi()` која повикува две функции од `zaednicki-kod.R` и ја вчитува табелата што резултира од модулот `grafik_2`, тогаш немаме баш линеарна зависност туку нешто покомплицирано. И иако сеуште можеме рачно да ги организираме ваквите врски помеѓу код и податоци, веројатно е време да разгледаме други опции за организирање на нашиот проект.
## Стратегија 2: Формален `R` пакет или некоја поедноставена варијанта
### Структура
Разгледувајќи ја првата стратегија за организирање на повторливи проекти дојдовме до моментот кога зависностите помеѓу нашиот код и податоци почнуваа да стануваат не-линеарни. Од една страна, неколку скрипти можат да ги користат истите податоци и да повикуваат функции од една скрипта за заеднички код, од друга страна, една скрипта може да зависи од резултатите на повеќе други прекурсори. Кога нашиот проект ќе ја стигне оваа фаза, тогаш подобро е да ја напуштиме линеарната структура што изолира еден сет на податоците и нивната обработка од остатокот на пакетот и да го реорганизираме проектот така што податоците, кодот, извештате, итн, ќе бидат во засебни фолдери. Во овој случај, структурата на проектот може да биде нешто налик на следниот дијаграм:
```
strategija_2/
├── izvestai
├── podatoci
├── README
└── skripti
```
Доколку некогаш сте ја погледнале структурата на еден формален `R` пакет (под „формален„ мислиме пакет или библиотека која што може да се инсталира и повика со `library(mojpaket)`), веднаш ќе ги забележите сличностите:
![Структура на еден формален `R` пакет (библиотека)](www/package-structure.png)
Сиот програмски код е изолиран во посебен фолдер (`skripti` или `R`), податоците се во фолдери посветени на податоци (`podatoci` или `data` или `data-raw`), главниот директориум во двата случаи има `README` фајл кој ја објаснува намената на пакетот и укажува како може да се користат спакувануте компоненти, итн.
Како работиме во оваа средина? Без разлика колку скрипти имаме, со колку функции во нив, и колку комплексни се зависностите помеѓу нив:
1. сите тие скрипти ги вчитуваме во нашата `R` средина (global environment, `.GlobalEnv`), така што кога една фунција повикува втора функција не мора да се сетиме во која скрипта живее таа функција
2. сите тие скрипти вчитуваат податоци кои се наоѓаат во `podatoci` и никаде на друго место
3. сите тие скрипти зачувуваат продукти (табели, графици, HTML/PDF извештаи) во `izvestai`
На ваков начин, ја елиминараме потрабата од копирање на податоци или код, и секоја компонента, дали е тоа изворен код или податоци, се јавува само еднаш во нашиот пакет.
### Поставување
Поставувањето на ваков повторлив проект може повторно да оди рачно, едноставно правиме фолдери за податоци, изборен код, итн. Но исто таке можеме да искористиме постоечки функции и пакети кои го прават овој чекор полесен. Иако има многу решенија достапни решенија, ќе тука ќе споменеме две опции. Прво, можеме да ја искористиме функционалноста за креирање формални `R` пакети, дури немаме намера да го објавуваме нашиот пакет. Кодот користи два пакети кои до сега не сме ги спомнале ([`devtools`](https://www.rdocumentation.org/packages/devtools/versions/2.3.2) [@R-devtools] и [`usethis`](https://www.rdocumentation.org/packages/usethis/versions/2.0.0) [@R-usethis]), но е едноставен:
```{r, eval = FALSE}
library(devtools)
library(usethis)
devtoos::create(path = "mojpaket")
podatoci1 <- read.csv("~/Desktop/moi-podatoci.csv")
usethis::use_data("podatoci1")
```
Извршувањето на горните линии ќе креира структура налик на следната:
```
mojpaket/
├── DESCRIPTION
├── NAMESPACE
├── data
└── R
```
Понатаму, како додавате документација, вињети, или тестови, можете да генерирате соодветни фолдери полу-автоматски. За далеку повеќе детали околу овие процедури, ве повикуваме да ја погледнете книгата за `R` пакети од Хадли Викам ([@wickham2015r]).
Втората опција за креирање на ваква структура за повторлив проект е преку користење на пакети посветени кон токму тоа: да олеснат повторливи анализи/истражувања/извештаи преку автоматизирање на некој од компонентите на повторливост кои ги дискутиравме до сега. Има повеќе вакви пакети: [`prodigenr`](https://www.rdocumentation.org/packages/prodigenr/versions/0.5.0), [`makeProject`](https://www.rdocumentation.org/packages/makeProject/versions/1.0), [`ProjectTemplate`](https://www.rdocumentation.org/packages/ProjectTemplate/versions/0.7), [`fertile`](https://github.com/baumer-lab/fertile), [`goodpractice`](https://www.rdocumentation.org/packages/goodpractice/versions/1.0.2), итн кои специјализираат кон различни аспекти (дали структура на проектот, проверки на кодот, итн). Овде, накратко, ќе се запознаеме со основната функционалност на пакетот `prodigenr` [@R-prodigenr] кој овозможува креирање на структура налик на формален `R` пакет но без (понекогаш непотребниот) багаж кој доаѓа со развивањето и користењето на еден формален `R` пакет.
```{r, eval = FALSE}
library(prodigenr)
setup_project("data/mojproekt2")
```
Потоа, доколку го погледнеме направениот проект ја имаме следната структура:
```
mojproekt2/
├── data
│ └── README.md
├── DESCRIPTION
├── doc
│ └── README.md
├── mojproekt2.Rproj
├── R
│ ├── fetch_data.R
│ ├── README.md
│ └── setup.R
├── README.md
└── TODO.md
```
На пример, скриптата `setup.R` доаѓа со објаснување како да ги организираме нашите скрипти/функции со цел да бидат достапни за време на интерактивната сесија. Додека пак скриптата `fetch_data.R` не насочува кон тоа како да ги собираме и подготвиме табелите со податоци за зачувување во `data/` и понатамошно користење. Како и во еден формален пакет, фајлот `DESCRIPTION` дава инструкции за зависностите на нашиот код (не-стандардни `R` пакети). Како и секогаш, ве покануваме да ја прочитате документацијата на пакетот (на пример `library(help="prodigenr")`) за повеќе детали.
### Предности
Очигледно има добри причини зошто би сакале нашиот повторлив проект да има структура слична на формален `R` пакет. Еден `R` пакет може да се инсталира и користи без проблем на било кој компјутер, има ефективна документација со вградени упатства за употреба и подолги вињети за специјални случаи, има екплицитно менаџирање на зависностите, и единични тестови (unit tests) кои што проверуваат дали секоја фунцкија го дава очекуваниот резултат во различни контексти.
### Недостатоци
Може да се најдат недостатоци во било кој систем, и секако оваа структура може сеуште да се подобрува, но за робустна организација на вашите повторливи проекти веројатно треба да се држите до некоја од алтернативите дискутирани тука. Она што може да се спомене, не како недостаток, туку предупредување, е дека навинкувањето и целосно искористување на ова структура ќе бара малку повеќе време и учење, како за оној кој го подготвува проектот така и за корисниците.
## Резиме
Без разлика која од овие опции ви ваќа повеќе око, или е поадекватна за вашиот проект, она што навистина прави разлика е внимание, трпение, и дисциплина при поставувањето на структурата на проектот и следењето на најдобрите пракси за повторливо програмирање. Дури и да имате идеална структура за вашиот проект, тоа нема да значи ништо доколку на вториот работен ден почнете да пишувате скрипти без документација или почнете да зачувувате фајлови во главниот фолдер наместо во посветените фолдери. Не се изненадувајте доколку следејќи ги праксите кои ги дискутираме тука текстот кој го пишувате за да документирате како работи одредена скрипта е подолг од самата скрипта што ја објаснувате.