Базовая c структура функции for()
в R:
for(condition that iterates) {
# do something
}
Оператор фигурных скобок {
и }
принимает все, что произошло в предыдущей функции for()
и применяет его к операторам программирования в фигурных скобках.
[[
в вашем вопросе является одной из четырех форм оператора извлечения . [[
специально позволяет извлечь один элемент из списка, поддерживая вычисляемый индекс. В моей статье приведены иллюстрации трех форм оператора извлечения Формы оператора извлечения . Четвертая форма, @
(также известная как оператор слота), уникальна для объектов, созданных с помощью объектной системы S4 в R, и обычно не используется начинающими программистами R.
Что касается обучения чтению кода R, ссылка, которую я разместил в комментариях, Программирование R Роджера Пенга, является разумным "бесплатным" ресурсом, поскольку вы можете решить заплатить 0 долларов за Это.
Объяснение кода в оригинальном сообщении
Здесь мы пройдемся по оригинальному примеру и объясним, что происходит. Поскольку df
не было определено в исходном сообщении, мы будем использовать фрейм данных mtcars
.
df <- mtcars
# instantiate a vector object with length equal to number of columns in
# df, which in this example should be 11
output <- vector("double",ncol(df))
length(output)
> length(output)
[1] 11
>
На этом этапе мы определили выходной вектор длиной 11, который соответствует количеству столбцов в df
.
Затем функция for()
устанавливает значение i
для уникальных последовательных чисел от 1 до количества столбцов в df
.
Это можно увидеть, напечатав результат seq_along()
.
# illustrate what seq_along() does
seq_along(df)
> seq_along(df)
[1] 1 2 3 4 5 6 7 8 9 10 11
>
Для каждого значения i
он вычисляет медиану соответствующего столбца в df
и сохраняет его в соответствующем элементе в пределах output
.
# iterate across columns in df, from 1:11
for (i in seq_along(df)){
output[[i]] <- median(df[[i]])
}
R поддерживает присвоение имен элементам в векторе. Чтобы было очевидно, что произошло в for()
l oop, мы установим имена элементов в output
на имена столбцов в df
и напечатаем вектор.
# add names to output vector and print
names(output) <- colnames(df)
output
> output
mpg cyl disp hp drat wt qsec vs am gear carb
19.200 6.000 196.300 123.000 3.695 3.325 17.710 0.000 0.000 4.000 2.000
>
Важность [[
оператора извлечения
На этом этапе начинающий пользователь R может спросить: «Почему этот код использует [[
форму оператора извлечения вместо [
?»
Ключ в том, что для кода в исходном вопросе форма [
возвращает объект типа data.frame()
, тогда как [[
оценивает фрейм данных и возвращает числовой вектор c. Мы можем проиллюстрировать это с помощью функции str()
, которая возвращает структуру объекта R.
str(df[1]) # returns a data frame
str(df[[1]]) # returns a numeric vector
> str(df[1]) # returns a data frame
'data.frame': 32 obs. of 1 variable:
$ mpg: num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
> str(df[[1]]) # returns a numeric vector
num [1:32] 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
>
Функция median()
ожидает числовой c вектор на входе. Передача ему фрейма данных вместо вектора приводит к ошибке «Необходимые числа c данные».
> median(df[1])
Error in median.default(df[1]) : need numeric data
>
Еще один способ объяснить, что R делает в этой ситуации, состоит в том, что df[1]
поднаправляет фрейм данных для возврата фрейма данных с одним столбцом, тогда как df[[1]]
не только поднабор фрейма данных в один столбец, но также рекурсивно подставляет его, чтобы вернуть значения в первом столбце как вектор. Подмножество более двух столбцов может привести к сбою рекурсивного подмножества.
# illustrate recursive subsetting with a case that will fail
head(df[1:3]) # print 6 rows of first 3 columns
head(df[[1:3]]) # fails with recursive subset error
> head(df[1:3]) # print 6 rows of first 3 columns
mpg cyl disp
Mazda RX4 21.0 6 160
Mazda RX4 Wag 21.0 6 160
Datsun 710 22.8 4 108
Hornet 4 Drive 21.4 6 258
Hornet Sportabout 18.7 8 360
Valiant 18.1 6 225
> head(df[[1:3]]) # fails with recursive subset error
Error in .subset2(x, i, exact = exact) :
recursive indexing failed at level 2
>
Множество способов решения проблемы в R
Объяснив код в исходном вопросе, важно отметить, что существует множество способов сделать что-то в R. Здесь мы будем используйте версию for()
, которая выполняет итерацию на основе имен столбцов в df
, и использует форму [[
оператора извлечения, чтобы извлечь правильный столбец в df
для вычисления его медианы.
# now illustrate same code by iterating over column names
output2 <- vector("double",ncol(df))
names(output2) <- colnames(df)
columnNames <- colnames(df)
for (i in columnNames){
output2[[i]] <- median(df[[i]])
}
output2
> output2
mpg cyl disp hp drat wt qsec vs am gear carb
19.200 6.000 196.300 123.000 3.695 3.325 17.710 0.000 0.000 4.000 2.000
>
Важной тонкостью в этом решении является то, что, присваивая имена элементам в output2
, мы можем использовать имена для индексации выходного вектора, а также фрейма входных данных. Фактически, если бы мы не присвоили имена, l oop приведет к неточным результатам, добавив именованные значения к элементам 12:22 в векторе.
Наконец, по мере продвижения в изучении R вы узнаете о семействе функций apply()
, которые позволяют избегать циклов for()
. Решение той же проблемы с использованием lapply()
или «list apply» иллюстрируется следующим блоком кода. Блок кода также реализует анонимную функцию , которая похожа на любую другую функцию R, за исключением того, что ей не дано имя. Так как lapply()
возвращает список, и мы знаем, что каждый элемент в списке является одним числом, мы используем unlist()
, чтобы преобразовать список, возвращаемый lapply()
, в вектор.
# produce same output with a different technique: lapply()
columnNames <- colnames(df)
output3 <- unlist(lapply(columnNames,function(x){
median(df[[x]])
}))
names(output3) <- colnames(df)
output3
> output3
mpg cyl disp hp drat wt qsec vs am gear carb
19.200 6.000 196.300 123.000 3.695 3.325 17.710 0.000 0.000 4.000 2.000
>