-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdata_frames.Rmd
143 lines (92 loc) · 6.24 KB
/
data_frames.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
---
title: "Data frames"
date: "2015-06-15"
output: html_document
---
### Перевод
### https://cran.r-project.org/web/packages/dplyr/vignettes/data_frames.html
```{r, echo=FALSE, warning=FALSE, message=FALSE}
setwd("D:/GITHUB/dplyr_doc_ru")
library(dplyr)
library(microbenchmark)
```
`data_frame()` представляет собой привлекательный способ создания таблиц данных (data frames). Эта функция объединяет лучшие практики создания таблиц данных:
* Никогда не меняет тип подаваемых на вход данных (т.е. больше никаких `stringsAsFactors = FALSE`!)
```{r}
data.frame(x = letters) %>% sapply(class)
data_frame(x = letters) %>% sapply(class)
```
Это упрощает её использование со списками-колонками (list-columns; *вспоминаем, что в R таблица данных тоже является списком - прим. пер.*):
```{r}
data_frame(x = 1:3, y = list(1:5, 1:10, 1:20))
```
Списки-колонки чаще всего создаются при помощи `do()`, но могут быть полезны и при создании вручную.
* Никогда не исправляет имена переменных:
```{r}
data.frame(`crazy name` = 1) %>% names()
data_frame(`crazy name` = 1) %>% names()
```
* Оценивает аргументы "лениво" и по порядку:
```{r}
data_frame(x = 1:5, y = x ^ 2)
```
* Добавляет класс `tbl_df()` для результата, так что если вы случайно напечатаете большую таблицу, то получите только первые несколько строк.
```{r}
data_frame(x = 1:5) %>% class()
```
* Никогда не использует `row.names()`, потому что весь смысл "чистых" данных состоит в согласованном хранении переменных, поэтому мы не должны помещать одну переменную в специальный атрибут.
* Повторяет только векторы единичной длины. Повторение (recycling) векторов другой длины является частым источником багов.
## Превращение
В дополнение к `data_frame()` dplyr предлагает функцию `as_data_frame()` для приведения списков в формат таблиц данных. Она делает две вещи:
* Проверяет, что исходный список является пригодным для таблицы данных, т.е. что каждый элемент имеет имя, является одномерным атомарным вектором или списком, и все элементы имеют одинаковую длину.
* Устанавливает класс и атрибуты списка, чтобы заставить его вести себя как таблицу данных. Эта модификация не требует полной копии исходного списка и поэтому работает очень быстро.
Это гораздо проще, чем `as.data.frame()`. Тяжело в точности объяснить, что именно делает `as.data.frame()`, но это действие аналогично `do.call(cbind, lapply(x, data.frame))` - т.е. происходит превращение каждого компонента в таблицу данных и объединение их при помощи `cbinds()`. Следовательно,`as_data_frame()` работает гораздо быстрее `as.data.frame()`:
```{r}
l2 <- replicate(26, sample(100), simplify = FALSE)
names(l2) <- letters
microbenchmark::microbenchmark(
as_data_frame(l2),
as.data.frame(l2)
)
```
Скорость `as.data.frame()` обычно не является "бутылочным горлышком" при интерактивной работе, но может быть проблемой при комбинировании тысяч беспорядочных источников в одну "чистую" таблицу.
## Память
Одной из причин быстроты dplyr является осторожность при создании копий столбцов. Этот раздел описывает, как это работает, и предоставляет вам несколько полезных средств для понимания использования памяти таблицами данных в R.
Первым средством, которое мы используем, является функция `dplyr::location()`. Она сообщает нам три вещи о таблице данных:
* где сам объект находится в памяти
* где расположен каждый столбец
* где расположен каждый атрибут
```{r}
location(iris)
```
Полезно знать адрес памяти, поскольку, если адрес изменился, то вы знаете, что R создал копию. Копии - это плохо, поскольку копирование вектора требует времени. Это обычно не является "бутылочным горлышком", если вы имеете несколько тысяч значений, но если их миллионы или десятки миллионов, то требуется значительное количество времени. Ненужные копии также плохи тем, что расходуют память.
R старается избегать создания копий, когда это возможно. Например, если вы присвоите `iris` другой переменной, она по-прежнему будет ссылаться на тот же
адрес:
```{r}
iris2 <- iris
location(iris2)
```
Вместо тщательного сравнения длинных ячеек памяти мы можем использовать функцию `dplyr::changes()`для описания изменений между двумя версиями таблицы данных. Она покажет нам, что `iris` и `iris2` идентичны - они ссылаются на одно и то же место в памяти.
```{r}
changes(iris2, iris)
```
Как вы думаете, что случится, если вы измените отдельный столбец в `iris2`? R 3.1.0 умеет изменять только один столбец, оставляя остальные ссылающимися на существующее расположение:
```{r}
iris2$Sepal.Length <- iris2$Sepal.Length * 2
changes(iris, iris2)
```
(Этого не было до R 3.1.0: R создавал полную копию целой таблицы данных.)
dplyr также умен:
```{r}
iris3 <- mutate(iris, Sepal.Length = Sepal.Length * 2)
changes(iris3, iris)
```
Он достаточно умен для создания только одного столца: остальные столбцы продолжают ссылаться на их старые расположения. Вы могли заметить, что атрибуты по-прежнему были скопированы. Это слабо влияет на производительность, посольку атрибуты обычно являются короткими векторами и их копирования делает внутренний код dplyr значительно проще.
dplyr никогда не делает копии, до тех пор, пока:
* `tbl_df()` и `group_by()` не создадут копию столбца
* `select()` никогда не копируют столбцы, даже когда вы их переименовываете
* `mutate()` никогда не копируют столбцы, даже когда вы изменяете существующие столбцы
* `arrange()` должна создавать копию, потому что вы меняете порядок каждого столбца. Это дорогостоящая операция для больших данных, но вы можете избегать её, используя порядковый аргумент "оконных функций" (https://cran.r-project.org/web/packages/dplyr/vignettes/window-functions.html).
* `summarise()` создает новые данные, но они обычно как минимум на порядок меньше исходных.
Это означает, что dplyr позволяет вам работать с таблицами данных с очень маленькими накладными расходами памяти.
data.table развивает эту идею на один шаг дальше, чем dplyr, и предоставляет функции для модификации таблицы данных на месте. Это позволяет избежать необходимости копировать указатели на существующие столбцы и атрибуты и опеспечивает ускорение, когда у вас много столбцов. dplyr не делает этого с таблицами данных (хотя и может), потому что я считаю, что безопаснее сохранять данные неизменяемыми: все методы dplyr для таблицы данных возвращают новую таблицу данных, даже когда они объединяют так много данных, насколько это возможно.