Поиск…


Операциями по строке

Ключ в векторизации кода R состоит в том, чтобы уменьшить или исключить «операции с помощью ряда» или метод отправки функций R.

Это означает, что при приближении к проблеме, которая на первый взгляд требует «операций по строке», например, вычисления средств каждой строки, нужно спросить себя:

  • Каковы классы наборов данных, с которыми я имею дело?
  • Существует ли существующий скомпилированный код, который может достичь этого без необходимости повторной оценки функций R?
  • Если нет, могу ли я выполнить эту операцию столбцами вместо строки?
  • Наконец, стоит ли тратить много времени на разработку сложного векторизованную кода вместо того , чтобы просто работает просто apply цикл? Другими словами, являются ли данные большими / сложными, что R не может эффективно обрабатывать его с помощью простого цикла?

Отложив проблему выделения памяти и увеличив объект в цикле, в этом примере мы сосредоточимся на том, как избежать apply циклов apply , отправки методов или переоценки R-функций в циклах.

Стандартный / простой способ вычисления среднего числа строк:

apply(mtcars, 1, mean)
          Mazda RX4       Mazda RX4 Wag          Datsun 710      Hornet 4 Drive   Hornet Sportabout             Valiant          Duster 360 
           29.90727            29.98136            23.59818            38.73955            53.66455            35.04909            59.72000 
          Merc 240D            Merc 230            Merc 280           Merc 280C          Merc 450SE          Merc 450SL         Merc 450SLC 
           24.63455            27.23364            31.86000            31.78727            46.43091            46.50000            46.35000 
 Cadillac Fleetwood Lincoln Continental   Chrysler Imperial            Fiat 128         Honda Civic      Toyota Corolla       Toyota Corona 
           66.23273            66.05855            65.97227            19.44091            17.74227            18.81409            24.88864 
   Dodge Challenger         AMC Javelin          Camaro Z28    Pontiac Firebird           Fiat X1-9       Porsche 914-2        Lotus Europa 
           47.24091            46.00773            58.75273            57.37955            18.92864            24.77909            24.88027 
     Ford Pantera L        Ferrari Dino       Maserati Bora          Volvo 142E 
           60.97182            34.50818            63.15545            26.26273 

Но можем ли мы сделать лучше? Давайте посмотрим, что здесь произошло:

  1. Сначала мы преобразовали data.frame в matrix . (Обратите внимание, что он происходит внутри функции apply .) Это неэффективно и опасно. matrix не может содержать несколько типов столбцов за раз. Следовательно, такое преобразование, вероятно, приведет к потере информации и несколько раз к вводящим в заблуждение результатам (сравнить apply(iris, 2, class) с str(iris) или с sapply(iris, class) ).
  2. Во-вторых, мы выполняли операцию повторно, один раз для каждой строки. Смысл, нам пришлось оценить некоторые R функции nrow(mtcars) раз. В этом конкретном случае mean не является дорогостоящей функцией, поэтому R может, вероятно, легко справиться с этим даже для большого набора данных, но что произойдет, если нам нужно вычислить стандартное отклонение по строке (что связано с дорогостоящей работой с квадратным корнем) ? Это подводит нас к следующему пункту:
  3. Мы много раз оценивали функцию R, но, возможно, уже есть скомпилированная версия этой операции?

Действительно, мы могли бы просто сделать:

rowMeans(mtcars)
          Mazda RX4       Mazda RX4 Wag          Datsun 710      Hornet 4 Drive   Hornet Sportabout             Valiant          Duster 360 
           29.90727            29.98136            23.59818            38.73955            53.66455            35.04909            59.72000 
          Merc 240D            Merc 230            Merc 280           Merc 280C          Merc 450SE          Merc 450SL         Merc 450SLC 
           24.63455            27.23364            31.86000            31.78727            46.43091            46.50000            46.35000 
 Cadillac Fleetwood Lincoln Continental   Chrysler Imperial            Fiat 128         Honda Civic      Toyota Corolla       Toyota Corona 
           66.23273            66.05855            65.97227            19.44091            17.74227            18.81409            24.88864 
   Dodge Challenger         AMC Javelin          Camaro Z28    Pontiac Firebird           Fiat X1-9       Porsche 914-2        Lotus Europa 
           47.24091            46.00773            58.75273            57.37955            18.92864            24.77909            24.88027 
     Ford Pantera L        Ferrari Dino       Maserati Bora          Volvo 142E 
           60.97182            34.50818            63.15545            26.26273 

Это не связано с операциями по строке и, следовательно, повторной оценкой R-функций. Тем не менее , мы по-прежнему преобразовали data.frame в matrix . Хотя rowMeans имеет механизм обработки ошибок, и он не будет работать в наборе данных, который он не может обрабатывать, он по-прежнему имеет эффективность.

rowMeans(iris)
Error in rowMeans(iris) : 'x' must be numeric

Но все-таки, можем ли мы сделать лучше? Мы могли бы попробовать вместо преобразования матрицы с обработкой ошибок, другой метод, который позволит нам использовать mtcars в качестве вектора (поскольку data.frame по существу является list а list - vector ).

Reduce(`+`, mtcars)/ncol(mtcars)
 [1] 29.90727 29.98136 23.59818 38.73955 53.66455 35.04909 59.72000 24.63455 27.23364 31.86000 31.78727 46.43091 46.50000 46.35000 66.23273 66.05855
[17] 65.97227 19.44091 17.74227 18.81409 24.88864 47.24091 46.00773 58.75273 57.37955 18.92864 24.77909 24.88027 60.97182 34.50818 63.15545 26.26273

Теперь для возможного увеличения скорости мы потеряли имена столбцов и обработку ошибок (включая обработку NA ).


Другим примером может быть вычисление среднего значения по группе, используя базу R, которую мы могли бы попробовать

aggregate(. ~ cyl, mtcars, mean)
cyl      mpg     disp        hp     drat       wt     qsec        vs        am     gear     carb
1   4 26.66364 105.1364  82.63636 4.070909 2.285727 19.13727 0.9090909 0.7272727 4.090909 1.545455
2   6 19.74286 183.3143 122.28571 3.585714 3.117143 17.97714 0.5714286 0.4285714 3.857143 3.428571
3   8 15.10000 353.1000 209.21429 3.229286 3.999214 16.77214 0.0000000 0.1428571 3.285714 3.500000

Тем не менее, мы в основном оцениваем R-функцию в цикле, но цикл теперь скрыт во внутренней C-функции (мало что имеет в виду, является ли это C или R-петлей).

Мы могли бы избежать этого? Ну есть скомпилированная функция в R, называемая rowsum , поэтому мы могли бы сделать:

rowsum(mtcars[-2], mtcars$cyl)/table(mtcars$cyl)
mpg     disp        hp     drat       wt     qsec        vs        am     gear     carb
4 26.66364 105.1364  82.63636 4.070909 2.285727 19.13727 0.9090909 0.7272727 4.090909 1.545455
6 19.74286 183.3143 122.28571 3.585714 3.117143 17.97714 0.5714286 0.4285714 3.857143 3.428571
8 15.10000 353.1000 209.21429 3.229286 3.999214 16.77214 0.0000000 0.1428571 3.285714 3.500000

Хотя нам пришлось сначала преобразовать в матрицу.

В этом вопросе мы можем задать вопрос о том, является ли наша нынешняя структура данных наиболее подходящей. Является ли data.frame лучшей практикой? Или нужно просто переключиться на matrix структуру данных, чтобы повысить эффективность?


Порядковые операции будут становиться все более и более дорогостоящими (даже в матрицах), когда мы начинаем оценивать дорогостоящие функции каждый раз. Давайте рассмотрим пример вычисления дисперсии по строке.

Допустим, мы имеем матрицу m :

set.seed(100)
m <- matrix(sample(1e2), 10)
m
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    8   33   39   86   71  100   81   68   89    84
 [2,]   12   16   57   80   32   82   69   11   41    92
 [3,]   62   91   53   13   42   31   60   70   98    79
 [4,]   66   94   29   67   45   59   20   96   64     1
 [5,]   36   63   76    6   10   48   85   75   99     2
 [6,]   18    4   27   19   44   56   37   95   26    40
 [7,]    3   24   21   25   52   51   83   28   49    17
 [8,]   46    5   22   43   47   74   35   97   77    65
 [9,]   55   54   78   34   50   90   30   61   14    58
[10,]   88   73   38   15    9   72    7   93   23    87

Можно просто сделать:

apply(m, 1, var)
[1]  871.6556  957.5111  699.2111  941.4333 1237.3333  641.8222  539.7889  759.4333  500.4889 1255.6111

С другой стороны, можно было бы полностью векторизовать эту операцию, следуя формуле дисперсии

RowVar <- function(x) {
  rowSums((x - rowMeans(x))^2)/(dim(x)[2] - 1)
}
RowVar(m)
[1]  871.6556  957.5111  699.2111  941.4333 1237.3333  641.8222  539.7889  759.4333  500.4889 1255.6111


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow