Как понять, какой контент можно поместить внутри или снаружи фигурных скобок? - PullRequest
0 голосов
/ 04 апреля 2020

Как новичок в R, я боролся с чтением сложного кода (по крайней мере, для меня это сложно). Когда я читаю простой код, структура очень ясна. Я могу сказать, какие аргументы, какие переменные и что-то еще, просто так. Например:

x <- c(1, 2, 3)

Но когда я читаю какой-то сложный код, я запутываюсь. Например:

output <- vector("double", ncol(df))  
for (i in seq_along(df)) {            
  output[[i]] <- median(df[[i]]) 
}
output

Я не знаю, почему это «в» перед seq_along (df), а не другие (я использую? Seq_along, но ответа нет). Я не знаю, почему "(я в seq_along (df))" не в фигурных скобках. Это состояние или? если в языке программирования есть основы c logi c, через которые я могу сказать, что можно поместить куда? Как читать сложный код в R, я имею в виду, если есть какой-либо лог c я могу следовать?

1 Ответ

2 голосов
/ 04 апреля 2020

Базовая 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 
> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...