Подтвердить что ты не робот

R data.table Усечение битов?

Итак, я огромный фанат data.table в R. Я использую его почти все время, но столкнулся с ситуацией, в которой он не будет работать для меня вообще. У меня есть пакет (внутренний для моей компании), который использует R double для хранения значения беззнакового 64-битного целого числа, битовая последовательность которого соответствует некоторой фантазии кодирования. Этот пакет работает очень хорошо везде, кроме data.table. Я обнаружил, что если я агрегирую по столбцу этих данных, я потеряю большое количество моих уникальных значений. Мое единственное догадывание здесь в том, что data.table - усекающие биты в какой-то странной double оптимизации.

Может ли кто-нибудь подтвердить, что это так? Это просто ошибка?

Ниже показано воспроизведение проблемы и сравнение с пакетом, который я сейчас должен использовать, но вы хотите избежать страсти (dplyr).

temp <- structure(list(obscure_math = c(6.95476896592629e-309, 6.95476863436446e-309, 
6.95476743245288e-309, 6.95476942182375e-309, 6.95477149408563e-309, 
6.95477132830476e-309, 6.95477132830476e-309, 6.95477149408562e-309, 
6.95477174275702e-309, 6.95476880014538e-309, 6.95476896592647e-309, 
6.95476896592647e-309, 6.95476900737172e-309, 6.95476900737172e-309, 
6.95476946326899e-309, 6.95476958760468e-309, 6.95476958760468e-309, 
6.95477020928318e-309, 6.95477124541406e-309, 6.95476859291965e-309, 
6.95476875870014e-309, 6.95476904881676e-309, 6.95476904881676e-309, 
6.95476904881676e-309, 6.95476909026199e-309, 6.95476909026199e-309, 
6.95476909026199e-309, 6.95476909026199e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.95477211576406e-309, 
6.95476880014538e-309, 6.95476880014538e-309, 6.95476880014538e-309, 
6.95476892448104e-309, 6.95476880014538e-309, 6.95476892448105e-309, 
6.9547689659263e-309, 6.95476913170719e-309, 6.95476933893334e-309
)), .Names = "obscure_math", class = c("data.table", "data.frame"), row.names = c(NA, 
-50L))

dt_collapsed <- temp[, .(count=.N), by=obscure_math]
nrow(dt_collapsed) == length(unique(temp$obscure_math))

setDF(temp)
dplyr_collapsed <- temp %>% group_by(obscure_math) %>% summarise(count=n())
nrow(dplyr_collapsed) == length(unique(temp$obscure_math))
4b9b3361

Ответ 1

Обновление: функция округления по умолчанию была удалена в текущей версии версии data.table(v1.9.7). См. Инструкции по установке для версии devel здесь.

Это также означает, что вы несете ответственность за понимание ограничений в представлении чисел с плавающей запятой и решении этой проблемы.


data.table существует уже давно. Мы использовали дело с ограничениями в представлениях с плавающей запятой, используя порог (например, base R, например, all.equal). Однако он просто не работает, поскольку он должен быть адаптивным в зависимости от того, насколько велики сравниваемые числа. Эта серия статей является отличным чтением по этой теме и другим потенциальным проблемам.

Это повторяющаяся проблема, потому что: а) люди не понимают ограничений, или б) пороговое значение действительно не помогло их проблеме, означало, что люди все спрашивали здесь или размещали на странице проекта.

В то время как мы повторно реализовали заказ data.table для быстрого упорядочивания радиуса, мы воспользовались возможностью, чтобы предоставить альтернативный способ устранения проблемы и предоставить выход, если это окажется нежелательным (экспорт setNumericRounding). С проблемой # 1642 заказ, вероятно, не требует округления удвоений (но это не так просто, так как порядок напрямую влияет на подмножества, основанные на бинарном поиске).

Фактическая проблема здесь заключается в группировке чисел с плавающей запятой, еще хуже, таких чисел, как в вашем случае. Это просто плохой выбор ИМХО.

Я могу думать о двух направлениях:

  • При группировке по столбцам, которые действительно удваиваются (в R, 1 является двойным, а не 1L, и эти случаи не имеют проблем), мы предоставляем предупреждение о том, что последние 2 байта округлены, и что люди должны читать ?setNumericRounding. А также предложите использовать bit64::integer64.

  • Удалите функциональность, позволяющую группировать операции по действительно двойным значениям или заставлять их фиксировать точность до определенных цифр, прежде чем продолжить. Я не могу думать о допустимой причине, по которой нужно было бы группировать числа с плавающей точкой действительно (хотелось бы услышать от людей, которые делают).

Что вряд ли произойдет, это возврат к проверкам на основе пороговых значений для определения того, какие двойники должны принадлежать к одной и той же группе.

Просто чтобы Q остался отвеченным, используйте setNumericRounding(0L).