Автор: Неха Анвер, Статистика без границ.
Эта статья содержит
- Краткий обзор концепции
apply()
и общего варианта использования apply()
противsapply()
с примерами
На этой неделе в Статистика без границ мы рассмотрим концепцию apply()
и обсудим, когда ее лучше всего использовать. Во время программирования вы можете столкнуться с ситуациями, требующими перебора набора значений и применения какой-либо функции или вычисления к каждому значению. Традиционно для этого используется конструкция петля.
Обзор apply()
Функцию apply()
можно использовать вместо циклов, чтобы ускорить код. apply()
может принимать матричные структуры в качестве входных данных и предоставлять выходные результаты в виде вектора, массива или списка. Поскольку она имеет встроенную возможность принимать многогранные входные данные и создавать аналогичные выходные структуры, использование этой функции устраняет необходимость в написании длинных циклов.
В приведенном ниже примере кода мы выполним несколько простых операций со встроенным набором данных R под названием Orange
. Этот набор данных фиксирует рост апельсиновых деревьев.
- Сначала мы загрузим набор данных в нашу среду с помощью команды
data
.
## Load in data data('Orange') head(Orange)
Вывод
# Tree age circumference # 1 1 118 30 # 2 1 484 58 # 3 1 664 87 # 4 1 1004 115 # 5 1 1231 120 # 6 1 1372 142
- Далее мы разработаем новую функцию. Эта новая функция будет вычислять значение
circumference
каждого дерева, деленное на наибольшее значениеcircumference
в данных. - Сначала мы выполним это вычисление, используя цикл
for
, а затем вычислим те же значения, используяapply()
.
Давайте напишем цикл for
, чтобы выразить каждое значение окружности в процентах от наибольшего значения в столбце окружности.
max_circ <- max(Orange$circumference) # maximum circumference pct_circ <- list() ## create an empty list to store values for(i in 1:nrow(Orange)) { # loop over each row # Divide each value by the max and multiply by 100 pct_circ[i] <- (Orange$circumference[i] / max_circ) * 100 } # End for loop pct_circ
Как видите, нам удалось перебрать каждую строку и вычислить процент от общего числа. Однако мы можем сделать это в одну строку, используя функцию apply()
. Общий синтаксис функции следующий:
apply(data, Margin, Function)
Первый аргумент ожидает массив или матрицу. Второй аргумент может быть 1
, 2
или вектором индексов. Этот аргумент сообщает функции, должна ли операция применяться к строкам (1
), вниз по столбцу (2
) или к определенной ячейке. Последний аргумент ожидает имя функции, которая будет применена. В нашем случае, поскольку нет встроенной функции, которая может вычислить данное число как процент от другого числа, мы напишем встроенную функцию:
pct_func <- function(x) { result <- x / max_circ # x is a vector/matrix result <- result * 100 # convert from decimal to % return(result) }
Теперь, когда у нас есть готовая функция, нам просто нужно указать правильные входные данные для функции apply()
. Помните, что он ожидает, что данные будут фреймом данных или матрицей. Мы предоставим столбец окружности (столбец 3) в качестве нашей входной матрицы. При индексировании столбца 3 кадра данных Orange
нам нужно будет указать дополнительный аргумент drop=F
, чтобы наша матрица сохраняла свои размеры.
apply(Orange[,3,drop=F], 1, pct_func)
Вывод
# output from console # 1 2 3 4 5 6 7 8 9 10 # 14.01869 27.10280 40.65421 53.73832 56.07477 66.35514 67.75701 15.42056 32.24299 51.86916 # 11 12 13 14 15 16 17 18 19 20 # 72.89720 80.37383 94.85981 94.85981 14.01869 23.83178 35.04673 50.46729 53.73832 64.95327 # 21 22 23 24 25 26 27 28 29 30 # 65.42056 14.95327 28.97196 52.33645 78.03738 83.64486 97.66355 100.00000 14.01869 22.89720 # 31 32 33 34 35 # 37.85047 58.41121 66.35514 81.30841 82.71028
Как видите, мы смогли добиться таких же результатов в одной строке. Используя метод apply()
, мы можем быстро применить любую функцию к набору данных. Хотя в данном случае мы написали собственную определяемую пользователем функцию, сочетание анонимной функции (обычно называемой лямбда-выражением) с apply()
позволяет пользователям писать свои собственные функции внутри функции apply()
. Хотя мы не будем обсуждать лямбда-выражения в этой статье, эта запись в блоге хорошо показывает, как они работают как в R, так и в Python.
применить () против sapply ()
Созданная на основе функции apply()
, sapply()
использует более гибкие типы ввода и идеально подходит для векторных операций. sapply()
принимает список, вектор или DataFrame в качестве входных данных и возвращает матрицу или вектор той же длины в качестве выходных данных. Общий синтаксис функции sapply()
:
sapply(data, function)
Обратите внимание, что здесь нет аргумента margin
. Это связано с тем, что функция sapply()
по умолчанию применяет функцию к каждому элементу данных. Из-за такого поведения по умолчанию аргумент margin
не нужен.
Следуя тому же примеру, что и выше, давайте вычислим длину окружности в процентах от наибольшей окружности в нашем наборе данных, используя sapply()
. Обратите внимание, что, поскольку входные данные не должны обязательно быть матрицей с пригодными для использования размерами, мы можем просто указать имя столбца «окружность» и получить объект списка той же длины, что и на выходе.
sapply(Orange$circumference, pct_func) #Output: # [1] 14.01869 27.10280 40.65421 53.73832 56.07477 66.35514 67.75701 15.42056 32.24299 # [10] 51.86916 72.89720 80.37383 94.85981 94.85981 14.01869 23.83178 35.04673 50.46729 # [19] 53.73832 64.95327 65.42056 14.95327 28.97196 52.33645 78.03738 83.64486 97.66355 # [28] 100.00000 14.01869 22.89720 37.85047 58.41121 66.35514 81.30841 82.71028
Если бы мы захотели, мы могли бы применить pct_func
к столбцу age
в нашем наборе данных, мы могли бы так:
sapply(Orange[,c('circumference', 'age')], pct_func) #Output: # circumference age # [1,] 14.01869 55.14019 # [2,] 27.10280 226.16822 # [3,] 40.65421 310.28037 # [4,] 53.73832 469.15888 # [5,] 56.07477 575.23364 # [6,] 66.35514 641.12150 # [7,] 67.75701 739.25234 # [8,] 15.42056 55.14019 # [9,] 32.24299 226.16822 # [10,] 51.86916 310.28037 # [11,] 72.89720 469.15888 # [12,] 80.37383 575.23364 # [13,] 94.85981 641.12150 # [14,] 94.85981 739.25234 # [15,] 14.01869 55.14019 # [16,] 23.83178 226.16822 # [17,] 35.04673 310.28037 # [18,] 50.46729 469.15888 # [19,] 53.73832 575.23364 # [20,] 64.95327 641.12150 # [21,] 65.42056 739.25234 # [22,] 14.95327 55.14019 # [23,] 28.97196 226.16822 # [24,] 52.33645 310.28037 # [25,] 78.03738 469.15888 # [26,] 83.64486 575.23364 # [27,] 97.66355 641.12150 # [28,] 100.00000 739.25234 # [29,] 14.01869 55.14019 # [30,] 22.89720 226.16822 # [31,] 37.85047 310.28037 # [32,] 58.41121 469.15888 # [33,] 66.35514 575.23364 # [34,] 81.30841 641.12150 # [35,] 82.71028 739.25234
В результате мы получаем DataFrame той же длины, что и наш входной DataFrame. Супер удобно! Я обычно использую эту функцию ежедневно для выполнения манипуляций с данными. И sapply()
, и apply()
являются двумя функциями из большого семейства функций apply. Я считаю, что они, как правило, охватывают большинство случаев использования, с которыми я сталкиваюсь изо дня в день, но если вы хотите узнать больше, этот пост охватывает все семейство приложений.
- Хотите узнать больше о статистике без границ? Подпишитесь на нас в Twitter и LinkedIn и посетите наш веб-сайт.
- Хотите стать волонтером в проектах или внести свой вклад в этот блог? Отправьте нам электронное письмо по адресу [email protected].
Познакомьтесь с автором
Неха — специалист по науке о данных с опытом работы в сфере финансового консультирования. Она работает волонтером в «Статистика без границ» уже более года, а совсем недавно присоединилась к команде SWB по маркетингу и коммуникациям. В свободное время Неха любит путешествовать, читать художественную литературу и проводить время на свежем воздухе.