Иногда мне нужно получить только первую строку набора данных, сгруппированную по идентификатору, как при получении возраста и пола, когда существует несколько наблюдений на человека. Какой быстрый (или самый быстрый) способ сделать это в R? Я использовал агрегат () ниже и подозреваю, что есть лучшие способы. Перед публикацией этого вопроса я немного искал в Google, нашел и попробовал ddply, и был удивлен, что он был очень медленным и дал мне ошибки памяти в моем наборе данных (400 000 строк x 16 столбцов, 7 000 уникальных идентификаторов), тогда как версия aggregate () было достаточно быстро.
(dx <- data.frame(ID = factor(c(1,1,2,2,3,3)), AGE = c(30,30,40,40,35,35), FEM = factor(c(1,1,0,0,1,1))))
# ID AGE FEM
# 1 30 1
# 1 30 1
# 2 40 0
# 2 40 0
# 3 35 1
# 3 35 1
ag <- data.frame(ID=levels(dx$ID))
ag <- merge(ag, aggregate(AGE ~ ID, data=dx, function(x) x[1]), "ID")
ag <- merge(ag, aggregate(FEM ~ ID, data=dx, function(x) x[1]), "ID")
ag
# ID AGE FEM
# 1 30 1
# 2 40 0
# 3 35 1
#same result:
library(plyr)
ddply(.data = dx, .var = c("ID"), .fun = function(x) x[1,])
ОБНОВЛЕНИЕ: См. Ответ Чейза и комментарий Мэтта Паркера о том, что я считаю самым элегантным подходом. Посмотрите ответ @Matthew Dowle для быстрого решения, которое использует data.table
пакет.
источник
diff()
чтобы вы могли получить первый идентификаторdx
.Ответы:
Ваш столбец идентификаторов действительно является фактором? Если это на самом деле числовой, я думаю, вы можете использовать
diff
функцию в ваших интересах. Вы также можете привести его к числовомуas.numeric()
.источник
dx[c(TRUE, dx$ID[-1] != dx$ID[-length(dx$ID)], ]
для нечисловых данных - я получаю 0,03 для символа, 0,05 для факторов. PS: есть дополнительная функция)
в вашей первойsystem.time()
функции, после второго нуля.data.table
решение должно оказаться самым быстрым, поэтому я бы проверил это на вашем месте (вероятно, здесь должен быть принятый ответ).Следуя ответу Стива, в data.table есть гораздо более быстрый путь:
Если вам просто нужен первый ряд каждой группы, гораздо быстрее присоединиться к этому ряду напрямую. Зачем создавать объект .SD каждый раз, только чтобы использовать его первый ряд?
Сравните 0,064 data.table с «альтернативой Мэтта Паркера решению Чейза» (которая пока казалась самой быстрой):
Так что ~ в 5 раз быстрее, но это крошечная таблица с количеством строк менее 1 миллиона. С увеличением размера увеличивается и разница.
источник
[.data.table
может быть функция ... Думаю, я не осознавал, что вы не создали.SD
объект, если он вам действительно не нужен. Хороший!dxt <- data.table(dx, key='ID')
в вызов system.time (), это быстрее, чем решение @ Matt.SD[1L]
были полностью оптимизированы, а ответ @SteveLianoglou будет в два раза быстрее для строк 5e7.Вам не нужно много
merge()
шагов, простоaggregate()
обе переменные, представляющие интерес:Сроки сравнения:
1) Решение Мэтта:
2) Решение Zach's reshape2:
3) Решение для data.table Стива:
4) Быстрое решение Чейза с использованием числового, а не факторного
ID
:и 5) альтернатива Мэтта Паркера решению Чейза, для характера или фактора
ID
, которая немного быстрее, чем числовое решение ЧейзаID
:источник
dx$ID <- sample(as.numeric(dx$ID)) #assuming IDs arent presorted system.time(replicate(1000, { dy <- dx[order(dx$ID),] dy[ diff(c(0,dy$ID)) != 0, ] })) user system elapsed 0.58 0.00 0.58
ID
s, поэтому результат был сопоставим с другими решениями.Вы можете попробовать использовать пакет data.table .
Для вашего конкретного случая, плюс в том, что это (безумно) быстро. Когда я впервые познакомился с ним, я работал с объектами data.frame с сотнями тысяч строк. "Нормальный"
aggregate
илиddply
методы были приняты ~ 1-2 минуты, чтобы завершить (это было до того, как Хэдли ввелidata.frame
моджо вddply
). Используяdata.table
, операция была буквально за несколько секунд.Недостатком является то, что это так быстро, потому что он преобразует ваш data.table (он как data.frame) в «ключевые столбцы» и использует стратегию интеллектуального поиска для поиска подмножеств ваших данных. Это приведет к изменению порядка ваших данных, прежде чем вы соберете статистику по ним.
Учитывая, что вы просто захотите первый ряд каждой группы - возможно, при переупорядочении будет запутано, какой ряд является первым, поэтому он может не подходить для вашей ситуации.
В любом случае, вам придется судить, подходит ли
data.table
это здесь или нет , но вот как вы будете использовать его с данными, которые вы представили:Обновление: Мэтью Доул (главный разработчик пакета data.table) предоставил лучший / более умный / (чрезвычайно) более эффективный способ использования data.table для решения этой проблемы в качестве одного из ответов здесь ... определенно проверьте это ,
источник
Попробуй reshape2
источник
Вы могли бы попробовать
Я понятия не имею, будет ли это быстрее, чем
plyr
, хотя.источник