2016-11-14 2 views
1

У меня есть dataframe с 1,8 миллионами строк, и каждая строка содержит один или несколько из 30 000 тегов. Я пытаюсь найти, сколько строк содержит экземпляр каждого тега. Некоторые элементы содержат до 25 тегов, каждый из которых находится в своем столбце. Ни в одной строке не содержит более одного экземпляра любого тега:Оптимизация запроса типа sumif в R

ItemNo <- c(1, 2, 3, 4) 
Tag1 <- c("ZZZ", "AAA", "BBB", "YYY") 
Tag2 <- c("YYY2", "ZZZ", "AAA", "XXX") 
Tag3 <- c("", "YYY2", "AAA2", "XXX3") 
Tag4 <- c("", "", "", "AAA") 
Tag5 <- c("", "", "", "ZZZ") 
Tag6 <- c("", "", "", "YYY2") 
Items <- data.frame(ItemNo, Tag1, Tag2, Tag3, Tag4, Tag5, Tag6) 
Items 

    ItemNo Tag1 Tag2 Tag3 Tag4 Tag5 Tag6 
1  1 ZZZ YYY2  
2  2 AAA ZZZ YYY2 
3  3 BBB AAA AAA2 
4  4 YYY XXX XXX3 AAA ZZZ YYY2 

Вселенная тегов находится в отдельном наборе данных:

Code <- c("AAA", "BBB", "XXX", "ZZZ", "AAAA", "XXX3", "YYY2") 
COUNT <- c(0, 0, 0, 0, 0, 0, 0) 
tags <- data.frame(Code, COUNT) 
tags 

    Code COUNT 
1 AAA  0 
2 BBB  0 
3 XXX  0 
4 ZZZ  0 
5 AAAA  0 
6 XXX3  0 
7 YYY2  0 

Я хочу, чтобы в конечном итоге с чем-то вроде этого:

Code COUNT 
1 AAA  3 
2 BBB  1 
3 XXX  1 
4 ZZZ  3 
5 AAAA  0 
6 XXX3  1 
7 YYY2  3 

Я могу получить хорошие результаты с помощью петли для обработки, которая занимает около 3 часов для прохождения через набор данных:

for (i in 1:nrow(tags)) {tags[i,2] <- sum(Items[,2:7] == 
as.character(tags[i,1]), na.rm = TRUE)} 

Есть ли более эффективный или элегантный способ подсчета экземпляров каждого тега в этом наборе данных?

+1

'table (unlist (tags [-1]))'. И конвертируйте все свои столбцы в 'character' * перед тем, как * запустить цикл. (или что-нибудь еще). Лучше всего это сделать, когда создается кадр данных ('stringsAsFactors = F'). Простое перемещение 'as.character()' вне цикла, вероятно, сбрит 30 минут вашего 3-часового времени работы. – Gregor

ответ

1

Давайте сделаем пример на что-то близко к вашей шкале:

n = 1e6 
ncol = 25 
ItemNo <- 1:n 
tags = c("", do.call(paste0, expand.grid(LETTERS, LETTERS, LETTERS, stringsAsFactors = FALSE))) 
item_tags = sample(tags, size = n * ncol, replace = T) 

Items <- cbind.data.frame(ItemNo, matrix(item_tags, ncol = ncol)) 

выше имеет 25 меток столбцов, 1 миллион строк, с 26^3 + 1 = 17577 уникальных тегов.

system.time(table(unlist(Items[-1]))) 
# user system elapsed 
# 15.077 1.001 16.277 

Запуск table на все, но первый столбец, у кого есть ссылка, занимает около 15 секунд на моем ноутбуке. Вывод должен быть пригодным для использования:

tt = table(unlist(Items[-1])) 
head(tt) 
#  AAA AAB AAC AAD AAE 
# 1421 1451 1456 1479 1440 1449 

Преобразовать в кадр данных, чтобы соответствовать вашему желаемому результату:

dd = data.frame(tt) 
head(dd) 
# Var1 Freq 
# 1  1421 
# 2 AAA 1451 
# 3 AAB 1456 
# 4 AAC 1479 
# 5 AAD 1440 
# 6 AAE 1449 

Обратите внимание, что он также подсчитывает пробела - вы можете подмножество тех из апостериорных.


Edit: Для большей скорости, вы можете использовать tabulate вместо table. table имеет варианты, которые нам не нужны в этом случае - tabulate - урезанная версия. Использование tabulate вместо table работает на моем компьютере менее чем через 10 секунд.

+0

Использование таблицы/списка получило время работы от 2 часов 55 минут до 9 секунд. Огромное спасибо! –

0

Я не могу проверить скорость на большом наборе данных, но я предполагаю, что это будет быстрее, так как он использует apply вместо for цикла:

Sums <- data.frame(Code = Code, 
        COUNT = unlist(lapply(Code, function(i) 
          sum(apply(Items, 1, function(x) any(i %in% x)))))) 

Разработка от центра COUNT = линии, такой подход:

  • Использует apply вдоль строк, чтобы получить логическое, указывающий, отображается ли или нет данный элемент в векторе возможных кодов в этой строке.
  • Обертывания, вызывающие apply в вызове lapply, который применяет этот процесс к каждому элементу в векторе кодов (Codes) и суммирует результаты.
  • Unlists the result list, чтобы он стал вектором.
  • Помещает этот вектор в рамку данных вместе со столбцом, отображающим соответствующие коды.

Результат:

> Sums 
    Code COUNT 
1 AAA  3 
2 BBB  1 
3 XXX  1 
4 ZZZ  3 
5 AAAA  0 
6 XXX3  1 
7 YYY2  3 
+0

Это более элегантный, чем мой цикл. Интересно, что это фактически увеличило время выполнения примерно в три раза от моего оригинала. Я не могу понять, почему это было бы так. Одна из причин может заключаться в том, что я читаю из столбца в «тегах», а не в исходном массиве. . Этот метод имеет дополнительное преимущество при восстановлении исходного порядка «тегов». –

+0

Ну, очевидно, мой подход не помогает. Сожалею. Я бы удалил ответ, но я думаю, что он может быть информативным, поэтому я его оставлю. – ulfelder

Смежные вопросы