Мне нужно найти индексы для числовых значений 1MM в векторе с примерно 10MM значениями. Я нашел пакет fastmatch
, но когда я использую функцию fmatch()
, я возвращаю только индекс первого совпадения.
Может кто-нибудь помочь мне использовать эту функцию, чтобы найти все значения, а не только первые? Я понимаю, что это основной вопрос, но онлайн-документация довольно скудная, а fmatch
значительно сократило вычислительное время.
Большое спасибо!
Вот некоторые примеры данных - для целей этого упражнения позвоните в этот фрейм данных A:
DateTime Address Type ID
1 2014-03-04 20:21:03 982076970 1 2752394
2 2014-03-04 20:21:07 98174238211 1 2752394
3 2014-03-04 20:21:08 76126162197 1 2752394
4 2014-03-04 20:21:16 6718053253 1 2752394
5 2014-03-04 20:21:17 98210219176 1 2752510
6 2014-03-04 20:21:20 7622877100 1 2752510
7 2014-03-04 20:21:23 2425126157 1 2752510
8 2014-03-04 20:21:23 2425126157 1 2752510
9 2014-03-04 20:21:25 701838650 1 2752394
10 2014-03-04 20:21:27 98210219176 1 2752394
Что я хочу сделать, так это найти количество уникальных значений Type
для каждого Address
. Есть несколько миллионов строк данных с примерно 1MM уникальными значениями адреса... в среднем каждый адрес появляется примерно в 6 раз в наборе данных. И, хотя значения Type
, перечисленные выше, равны 1, они могут принимать любое значение от 0: 5. Я также понимаю, что значения Address
довольно длинные, что добавляет времени, необходимого для сопоставления.
Я пробовал следующее:
uvals <- unique(A$Address)
utypes <- matrix(0,length(uvals),2)
utypes[,1] <- uvals
for (i in 1:length(unique(Address))) {
b <- which(uvals[i] %in% A$Address)
c <- length(unique(A$Type[b]))
utypes[i,2] <- c
}
Однако приведенный выше код не очень эффективен - если я перебираю значения 1MM, я считаю, что это займет 10-15 часов.
Я тоже пробовал это в цикле... но это не намного быстрее.
b <- which(A$Address == uvals[i])
Я знаю, что есть более элегантный/более быстрый способ, я довольно новичок в R и буду признателен за любую помощь.
Ответ 1
Это можно сделать с помощью функции unique
в data.table
, за которой следует агрегация. Я проиллюстрирую это, используя более или менее выборочные данные, сгенерированные @Chinmay:
Создание выборочных данных:
set.seed(100L)
dat = data.frame(
address = sample(1e6L, 1e7L, TRUE),
value = sample(1:5, 1e7L, TRUE, prob=c(0.5, 0.3, 0.1, 0.07, 0.03))
)
data.table решение:
require(data.table) ## >= 1.9.2
dat.u = unique(setDT(dat), by=c("address", "value"))
ans = dat.u[, .N, by=address]
Объяснение:
- Функция
setDT
преобразует a data.frame
в data.table
по ссылке (что очень быстро).Функция -
unique
, работающая на data.table, вызывает метод unique.data.table
, который невероятно быстрый по сравнению с base:::unique
. Теперь у нас есть только уникальные значения type
для каждого address
. - Все, что осталось сделать, это объединить или сгруппировать по
address
и получить количество наблюдений, которые есть в каждой группе. Группы частей by=address
by address
и .N
являются встроенной переменной data.table
, которая предоставляет количество наблюдений для этой группы.
Ориентиры:
Я создам функции для генерации данных как data.table
и data.frame
для сравнения data.table
с ответом на dplyr
решение (a), предложенное @beginneR, хотя я не вижу необходимости в arrange(.)
там и, следовательно, пропустит эту часть.
## function to create data
foo <- function(type = "df") {
set.seed(100L)
dat = data.frame(
address = sample(1e6L, 1e7L, TRUE),
value = sample(1:5, 1e7L, TRUE, prob=c(0.5, 0.3, 0.1, 0.07, 0.03))
)
if (type == "dt") setDT(dat)
dat
}
## DT function
dt_sol <- function(x) {
unique(x, by=c("address", "value"))[, .N, by=address]
}
## dplyr function
dplyr_sol <- function(x) {
distinct(x) %>% group_by(address) %>% summarise(N = n_distinct(value))
}
Тайминги, представленные здесь, представляют собой три последовательных прогона system.time(.)
для каждой функции.
## benchmark timings in seconds
## pkg run-01 run-02 run-03 command
## data.table 2.4 2.3 2.4 system.time(ans1 <- dt_sol(foo("dt")))
## dplyr 15.3 16.3 15.7 system.time(ans2 <- dplyr_sol(foo()))
По какой-то причине dplyr
автоматически упорядочивает результат по переменной группировки. Поэтому, чтобы сравнить результаты, я также закажу их в результате от data.table
:
system.time(setkey(ans1, address)) ## 0.102 seconds
identical(as.data.frame(ans1), as.data.frame(ans2)) ## TRUE
Итак, data.table
здесь ~ 6x быстрее.
Обратите внимание, что bit64:::integer64
также поддерживается в data.table
- поскольку вы указываете, что значения адреса слишком велики, вы также можете сохранить их как integer64
.
Ответ 2
Вы можете попытаться создать индекс ваших значений 10MM и отсортировать их. Затем поиск ваших значений 1MM в этом индексированном векторе должен быть быстрее.
Например, используя пакет data.table
, вы можете сделать это с помощью функции setkey
, которая индексирует данный столбец data.table.
require(data.table)
set.seed(100)
dat <- sample(1:1e+07, size = 1e+07, replace = T)
searchval <- sample(dat, size = 1e+06)
DT <- data.table(dat, index = seq_along(dat))
setkey(DT, dat)
DT
## dat index
## 1: 1 169458
## 2: 1 4604823
## 3: 1 7793446
## 4: 2 5372388
## 5: 3 2036622
## ---
## 9999996: 9999996 1271426
## 9999997: 9999998 530029
## 9999998: 10000000 556672
## 9999999: 10000000 6776063
## 10000000: 10000000 6949665
lookup <- data.table(val = searchval)
setkey(lookup, val)
lookup
## val
## 1: 2
## 2: 16
## 3: 24
## 4: 33
## 5: 36
## ---
## 999996: 9999970
## 999997: 9999973
## 999998: 9999988
## 999999: 9999996
## 1000000: 9999998
Теперь вы можете найти все значения из lookup
в DT
, просто используя
DT[lookup]
## dat index
## 1: 2 5372388
## 2: 16 537927
## 3: 16 1721233
## 4: 24 7286522
## 5: 33 7448516
## ---
## 2000298: 9999973 8008610
## 2000299: 9999988 3099060
## 2000300: 9999988 7996302
## 2000301: 9999996 1271426
## 2000302: 9999998 530029
Ответ 3
fmatch
, похоже, ясно указывает, что он находит только первое совпадение. И учитывая, что он использует базовую стратегию хеширования, я полагаю, что маловероятно, что он хранит несколько элементов на ключ, что является одним из способов, которым он остается таким быстрым (и он работает так же, как работает match
).
У вас много повторяющихся значений? Возможно, вы можете сохранить их в отдельном месте/таблице и создать быстрый индекс для списка возможных совпадений. Было бы более полезно, если бы вы предоставили образцы данных, представляющих то, что вы пытаетесь сделать, и код, который вы пытались увидеть, если его было бы легко расширить.
Ответ 4
Если я правильно понял ваш вопрос, вы также можете сделать это с помощью dplyr
:
Я буду включать два разных способа, так как я не совсем уверен, каков ваш желаемый результат.
Сначала создайте некоторые данные образца:
Address <- rep(letters, 5)
Type <- sample(1:5, size=5*26, replace=T)
A <- data.frame(Address, Type)
Затем установите и загрузите dplyr
require(dplyr)
a) Чтобы найти количество различных значений Type
для каждого значения Address
:
A %.% arrange(Address, Type) %.% group_by(Address) %.% summarize(NoOfTypes = length(unique(Type)))
b) Чтобы найти все уникальные комбинации Address
и Type
:
A %.% arrange(Address, Type) %.% group_by(Address, Type) %.% filter( 1:n() == 1)