Как эффективно суммировать по уровням, определенным в другой переменной?

#r #apply

#r #применить

Вопрос:

Я новичок в R. Теперь у меня есть функция следующим образом:

 funItemAverRating = function()
{
    itemRatingNum = array(0, itemNum);
    print("begin");
    apply(input, 1, function(x)
        {
            itemId = x[2] 1;
            itemAverRating[itemId] <<- itemAverRating[itemId]   x[3];
            itemRatingNum[itemId] <<- itemRatingNum[itemId]   1;
        }
    );
}
  

В этой функции вводом является n*3 фрейм данных, n является ~6*(10e 7) , itemRatingNum является вектором размера ~3*(10e 5) .
Мой вопрос в том, почему apply функция работает так медленно (для завершения работы потребовался бы почти час)? Кроме того, по мере выполнения функции она использует все больше и больше памяти. Но, как вы можете видеть, все переменные определены вне apply функции. Кто-нибудь может мне помочь?

ченг

Комментарии:

1. Трудно сказать, не видя, как выглядят данные (что такое itemAverRating , из каких столбцов input состоят), но я полагаю, вы могли бы сделать это без apply использования векторизации. Например.: itemRatingNum[input[[2]] 1] <- itemRatingNum[input[[2]] 1] 1

2. Спасибо за ваш ответ. Есть ли какая-либо разница в эффективности между этим и функцией apply?

3. ДА. Работа с векторами, если намного, намного быстрее (может занять от 1 часа до <1 м)

4. @пользователь572138, пожалуйста, измените название вашего вопроса. Apply в целом работает не медленно, только по вашей конкретной причине, главным образом потому, что вы неправильно его используете.

5. @mpiktas Ок. Можете ли вы предоставить сценарий, в котором apply эффективно обрабатывает крупномасштабные данные (например, в моем случае)? Спасибо.

Ответ №1:

Это медленно, потому что вы вызываете высокоуровневые R-функции много раз.

Вы должны векторизовать свою функцию, что означает, что большинство операций (таких как <- или 1 ) должны вычисляться по всем векторам данных.

Например, мне кажется, что itemRatingNum содержит частоты input[[2]] (второй столбец input data.frame ), которые можно было бы заменить на:

 tb <- table(input[[2]] 1)
itemRatingNum[as.integer(names(tb))] <- tb
  

Комментарии:

1. Спасибо за ваш ответ. Но если я хочу сделать что-то вроде: itemPopu = tapply (ввод [,3], ввод [, 2], сумма); Есть ли какие-либо эффективные решения. Я обнаружил, что tapply выполняется очень медленно.

2. Попробуйте rowsum(input[[3]],input[[2]])

Ответ №2:

Не делайте этого. Вы следуете логике, которая совершенно не похожа на R. Если я правильно понимаю, вы хотите добавить к определенному itemAverRating вектору значение из третьего столбца в некотором входном фрейме данных.

То, что itemRatingNum делается, довольно неясно. Это не попадает в глобальную среду, а просто становится вектором, заполненным частотами в конце цикла. Поскольку вы определяете itemRatingNum внутри функции, <<- присваивание также назначит его в локальной среде функции, и оно будет уничтожено, когда функция завершится.

Далее вы должны ввести данные вашей функции и получить некоторый результат. Никогда не назначайте глобальную среду, если в этом нет необходимости. Ваша функция эквивалентна — скорее, намного быстрее — следующей функции, которая принимает входные данные и выдает выходные :

 funItemAverRating = function(x,input){
    sums <- rowsum(input[,3],input[,2])
    sumid <- as.numeric(rownames(sums)) 1
    x[sumid] c(sums)
}
  

ФУНКЦИЯ ОТРЕДАКТИРОВАНА СОГЛАСНО КОММЕНТАРИЮ МАРЕКСА

Который работает как :

 # make data
itemNum <- 10
set.seed(12)
input <- data.frame(
    a1 = rep(1:10,itemNum),
    a2 = sample(9:0,itemNum*10,TRUE),
    a3 = rep(10:1,itemNum)
)
itemAverRating <- array(0, itemNum)
itemAverRating <- funItemAverRating(itemAverRating,input)
itemAverRating
 0  1  2  3  4  5  6  7  8  9 
39 65 57 36 62 33 98 62 60 38 
  

Если я попробую ваш код, я получу :

 > funItemAverRating()
[1] "begin"
...
> itemAverRating
 [1] 39 65 57 36 62 33 98 62 60 38
  

Что является тем же самым. Если вам нужен itemRatingNum, то просто сделайте :

 > itemRatingNum <- table(input[,2])
 0  1  2  3  4  5  6  7  8  9 
 6 11 11  8 10  6 18  9 13  8 
  

Комментарии:

1. Я попробовал tapply, но обнаружил, что эта функция работает очень медленно, itemPopu = tapply(ввод[,3], ввод[,2], сумма); этот код отнял бы много времени. Есть ли какие-либо лучшие решения?

2. @user572138 : Это примерно в 13 раз быстрее, чем ваш код на моем компьютере, и он делает точно то же самое. Что вы подразумеваете под «медленным»?

3. В моих данных длина (входные данные) очень велика (~ 6 * 10e 7), но во входных данных много повторяющихся элементов [, 2]. Уникальный номер ввода [,2] равен ~ 3 * 10e5. Когда я запускаю tapply (ввод [,3], ввод [, 2], сумма), мне нужно долго ждать (по крайней мере, 5 минут). В C это, конечно, не будет стоить так долго.

4. Под «по крайней мере, 5 минут» я подразумеваю, что через 5 минут код все еще выполняется.

5. Номер строки ввода равен примерно (~ 6 * 10e 7).