2016-12-19 2 views
3

У меня есть очень интересная проблема, хотя я бы предпочел не иметь ее. я должен округлить число до закрытия множественным я последовал за решение here Он используется для работы в порядке, пока я не обнаружить ошибку с data.tableКруглый и множественный фильтр в data.table

library(data.table) 
options(digits = 20) # to see number representation 
mround <- function (number, multiple) { 
    return(multiple * round(number/multiple)) 
} 
DT = data.table(a = mround(112.3, 0.1), b = "B") 
DT[a == 112.3,] # works as expected, i.e returns one row 
DT[a == 112.3 & b == 'B', ] # doesn't work 

Чтобы быть справедливым, с data.frame даже первый фильтр не работает. Любые идеи, как это исправить?

+0

You проблема четко не определена. Вы должны добавить то, что ожидаете от вывода. Обе строки возвращают мне пустые data.tables. – lmo

+0

Первый оператор не работает для меня в 'data.table', и он не связан с' data.table' или 'data.frame', но это проблема [точности с плавающей точкой] (https: // cran. r-project.org/doc/FAQ/R-FAQ.html#Why-doesn_0027t-R-think-these-numbers-are-equal_003f). См. «DT [(a - 112.3) <1.e-6 & b == 'B',] '(с использованием поля ошибки 0,000001, если вам нужна более высокая точность, см. ссылку выше или используйте' .Machine $ double.eps^0.5', как и 'all.equal') – Tensibai

+0

FWIW, round принимает параметр 'digits',' round (112.3,1) 'должно быть достаточно и должно избегать вашей реальной проблемы) – Tensibai

ответ

3

Это проблема floating point precision. См. DT[abs(a - 112.3)<1.e-6 & b == 'B',] с использованием допустимой погрешности в 0.000001.

Если вы хотите больше точности, вы можете использовать .Machine$double.eps^0.5, равно как all.equal.

Генеральный совет никогда не сравнивать равенства поплавков, но сравнить разницу со значением достаточно близко к точности станка, чтобы обойти точности дрейфа между 0 и 1), более подробно here

Один из способов исправить ваш проблема может быть реорганизовать функцию:

mround <- function (number, multiple, digits=nchar(strsplit(as.character(multiple),".",fixed=TRUE)[[1]][2])) { 

    round(multiple * round(number/multiple),digits) 
} 

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

Таким образом, результат должен быть достаточно точным, но вы все еще будете иметь угловые случаи:

> mround(112.37,0.2) 
[1] 112.40000000000001 

Чтобы использовать поплавки в соединениях, вы можете использовать (любезность Дэвида Arenburg):

setNumericRounding(1) 
DT[.(112.3, 'B'), nomatch = 0L, on = .(a, b)] 
+0

Я согласен, но я хочу использовать этот столбец для соединения. Не могу представить, чтобы ни один из них не использовал float для соединения. – kismsu

+0

@kismsu Я не понимаю, что вы имеете в виду ... Как это проблема? (звучит другой вопрос) – Tensibai

+0

@kismsu Я имею в виду, что 'DT1 [DT2, on =. ((xa-ia <.Machine $ double.eps^0.5)]' должен делать (непроверенный) – Tensibai

4

Просто чтобы добавить к @Tens отличный ответ.

Что, кажется, происходит три вещи

  • У вас есть вопрос с плавающей точкой (как уже упоминалось)
  • Вы используете и старый data.table версия
  • Вторичные индексы пиная в то время вы не знаете об этом

Использование установки

library(data.table) 
options(digits = 20) # to see number representation 

mround <- function (number, multiple) { 
    return(multiple * round(number/multiple)) 
} 

DT = data.table(a = mround(112.3, 0.1), b = "B") 

Так что давайте рассмотрим указанные выше пункты. Поскольку у вас есть с плавающей точкой и со ссылкой на ?setNumericRounding

Компьютеры не может представлять некоторые числа с плавающей точкой (например, 0.6) точно, используя основание 2. Это приводит к неожиданному поведению при соединении или группирования столбцов типа «числовой»; то есть«Двойной

Это привело data.table УБС реализовать setNumericRounding, которая автоматически округляется поплавки поэтому алгоритм радикс будет вести себя, как ожидалось.

До v1.9.8, setNumericRounding(2) был по умолчанию (следовательно, ваш первый пример работает), но после нескольких жалоб от пользователей на непоследовательность GH (IIRC), так как v1.9.8 по умолчанию было установлено обратно setNumericRounding(0) для того, чтобы должны соответствовать поведению data.frame (см. here). Поэтому, если вы обновите свою таблицу data.table до последней версии, вы увидите, что оба data.table и data.frame будут вести себя одинаково для обоих ваших примеров (и оба ваших примера не удастся).

Сравнить

setNumericRounding(0) 
DT[a == 112.3] 
## Empty data.table (0 rows) of 2 cols: a,b 

Для

setNumericRounding(1) 
DT[a == 112.3] 
#      a b 
# 1: 112.30000000000001 B 

Так вы спросите, ", что на земле алгоритм Radix имеет отношение к чему здесь". Итак, мы достигли третьего пункта выше вторичные индексы (пожалуйста, прочтите this). Давайте посмотрим, что на самом деле происходит, когда вы работаете вам код, указанный выше

options(datatable.verbose = TRUE) 
DT[a == 112.3] # works as expected, i.e returns one row 
# Creating new index 'a' <~~~~ 
# forder took 0 sec 
# Starting bmerge ...done in 0 secs 
#      a b 
# 1: 112.30000000000001 B 

Позволяет проверяет новые вторичные индексов

indices(DT) 
#[1] "a" 

, когда вы бежали ==, data.table набор a в качестве вторичного индекса в порядке для выполнения будущих операций намного эффективнее (это было введено в версии 1.9.4, см. here). Другими словами, вы выполнили двоичное соединение на a вместо обычного векторного сканирования, как это было раньше v1.9.4. (В качестве побочной заметки это можно отключить, выполнив options(datatable.auto.index = FALSE), в этом случае ни один из ваших примеров не будет работать даже с setNumericRounding(1) если не будет явно указать ключ, используя setkey или on аргумент)

Это, вероятно, будет также объяснить, почему

DT[a == 112.30000 & b == 'B'] 

не работает. Вы устанавливаете здесь два столбца, и ни вторичные индексы, ни двоичные соединения не выполняются (автоматически) для таких выражений, как == & == (пока), поэтому вы выполнили обычное векторное сканирование, а setNumericRounding(1) не ударили в

Хотя, вы можете установить ключи вручную и заставить его работать, например (как я заметил под @Tens ответ), вы можете сделать

setNumericRounding(1) # make sure autoroundings turned on 
DT[.(112.3, 'B'), nomatch = 0L, on = .(a, b)] 
# Calculated ad hoc index in 0 secs 
# Starting bmerge ...done in 0 secs 
#  a b 
# 1: 112.3 B 

Или используя старый способ

setkey(DT, a, b) 
DT[.(112.3, 'B'), nomatch = 0L] 
# Starting bmerge ...done in 0 secs 
#  a b 
# 1: 112.3 B 
+0

Спасибо за резюме. На самом деле, я действительно не знал об индексе ad hoc, забыл об этой функции. – kismsu