Pull to refresh

Comments 21

Очень часто использую data.table — особенно при больших объемах данных. Есть еще интересный «побочный эффект»:
data.table(x = seq(0, 6*pi, length.out = 1000)
)[, y := sin(x)
  ][, plot(y ~ x, type = "l", col = "blue")]

Т.е. в j можно практически все что угодно вычислять. И, если я не ошибаюсь, piping создает копии объектов, поэтому для DT правильнее использовать chaining.
Очень часто использую data.table — особенно при больших объемах данных. Есть еще интересный «побочный эффект»:


Интересное замечание! Графики внутри ДТ я не строил, но в остальном полное взаимодействие с окружением, да. Испольвал также set и get внутри ДТ в родительское окружение.

И, если я не ошибаюсь, piping создает копии объектов, поэтому для DT правильнее использовать chaining.


Спасибо, я срочно почитаю про это. Не совсем уверен в ответе.

Оставлю это здесь — ДТ делает всегда копию при фильтрации в i, неважно каким стилем оформления кода это делается. Далее наступает вопрос, как gc работает с этими копиями. Вопрос не тривиальный, возился с проектами, где копии таблиц отжирали 30+ Гб оперативки, после оптимизации снизил до 10 Гб.
Со сборкой мусора — особенно при фрагментации — у R не очень. Еще интересно, возможно ли технически в DT организовать «внутренние» типы данных вроде float8, float16, int8 и т.д. В pandas/numpy это сильно помогает экономить память.
Со сборкой мусора — особенно при фрагментации — у R не очень.


Не уверен. Есть обсуждение этого вопроса подетальнее, ссылка на форум или SE?

Еще интересно, возможно ли технически в DT организовать «внутренние» типы данных вроде float8, float16, int8 и т.д. В pandas/numpy это сильно помогает экономить память.


Так как DT это по сути list, она использует аровские типы. Для 64-разрядного интежера я вроде ставил библиотеку, чтобы повысить с 32. Есть и другие трюки, но опять же на аровских типах. В принципе, это наверное вопрос к создателям ДТ, про такие фичи я ничего не слышал, думаю, нет этого.

По сравнению с богатством типов данных в numpy здесь проигрыш явный, да.
Да по типам вопрос не к вам — просто размышления вслух. В base R single == double: «R has no single precision data type». Но зато есть пакет float:
> object.size(data.table(x=double(1000000)))
8001152 bytes
> object.size(data.table(x=float(1000000)))
4001768 bytes

Вроде бы то что надо.
А на счет памяти: неоднократно была ситуация, когда удаляешь большой объект или его часть, запускаешь сборщик мусора, а память не освобождается.

Интересно, интересно. Так не делал. Спасибо.


По памяти, да, понял. А вот так: попробуйте профайлер памяти (не времени) Rprof с записью памяти с очень дробным промежутком времени. Там видно, если конечно я ничего не перепутал, запуск gc очень часто.
А gc ведь очищает только точно не нужные участки памяти, например, дататейблы без ссылок на них. И вообще тог работает по своей логике, я ее не особо знаю… Может не удаляются объекты, которые он считает нужными? И может ошибается?

Там, кажется, проблема в фрагментации памяти, и gc плохо работает в этом случае. Пару лет назад на SO был топик на эту тему — не могу найти ссылку.

Пытался найти инфу по связке gc и DT, инфы маловато...

Для вытаскивания первой буквы можно ещё применить встроенную в data.table функцию разделения строк tstrsplit. По скорости будет примерно равно третьему варианту, но код гораздо проще :) Естественно это решение для конкретной задачи, а не для понимания, как делать быстро в целом.

dt[, first_l := NULL]
microbenchmark({
dt[, first_l := tstrsplit(w, split = ' ', fixed = T, keep = 1L)]
})
Да, согласен. ) Но я хотел показать, что когда есть, например, текст из разных слов или стринга с различными значениями, разделенными пробелом, как это дело спарсить. Но для буквы ваш вариант должен быть идеальным. )
Добавил в раздел про векторизацию более реальный пример. Там тоже делается векторизация через strsplit, и она реально нужна.
По моему с новым примером что-то не то. Функция first_l_f3 не используется в коде, а только создаётся.

И выдаётся такая ошибка
dt[
, (paste0('w_', 1:3)) := strsplit(w, split = ' ', fixed = T)
]

Error in `[.data.table`(dt, , `:=`((paste0("w_", 1:3)), strsplit(w, split = " ", :
Supplied 3 columns to be assigned 100000 items. Please see NEWS for v1.12.2.

Сори, Код не обновил… Скоро обновлю, посмотрите.

Спасибо, что заметили. Обновил код в статье.

microbenchmark({
dt[
, (paste0('w_', 1:3)) := lapply(1:3, function(x) first_l_f3(w, x))
]
})

Мне кажется Вы опять усложнили решение не используя встроенные функции.
В Вашем варианте получается, что одно и тоже разделение на слова происходит три раза?
Я бы сделал функцией, которую предлагал выше
dt[, (paste0('w_', 1:3)) := NULL]
microbenchmark({
dt[, (paste0('w_', 1:3)) := tstrsplit(w, split = ' ', fixed = T, keep = 1L:3L)]
})

у меня это работает в 5 раз быстрее.

Кстати функция str_split из stringr позволяет получать на выходе матрицу и можно её использовать

library(stringr)
dt[, (paste0('w_', 1:3)) := NULL]
microbenchmark({
cbind(dt, str_split(dt$w, " ", simplify = TRUE)[, 1:3])
})

Вы верно ухватили смысл. Все так и есть. Рад, что нашли более крутое решение! Завтра попробую его. tstrsplit? Это из библиотеки?

Не пользовался. Ну, если сделать сплит один раз и записать его в колонку, то мое решение на основе r base будет быстрее опубликованного. Но из за превращения в матрицу проигрывает. У вас без этого шага. Вижу отличное решение...

Заслуженные лайки. Код обновил.
Sign up to leave a comment.

Articles