Какая разница между as.integer() и + 0L, используемой в булеанах?
Я видел +0L
, который использовался в ответе на вопрос, и выяснил, что он хорошо работает с матрицами/кадрами данных/таблицами данных, где as.integer()
не сможет сохранить начальные классы данных.
> a <- matrix(TRUE, nrow=3, ncol=3)
> a
[,1] [,2] [,3]
[1,] TRUE TRUE TRUE
[2,] TRUE TRUE TRUE
[3,] TRUE TRUE TRUE
> as.integer(a)
[1] 1 1 1 1 1 1 1 1 1
> a+0L
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
- Существуют ли другие различия между этими подходами?
- Каковы плюсы и минусы и оговорки при использовании одного или другого?
[edit:] много мудрости в комментариях! По-видимому, существует много разных способов достижения такого же результата, некоторые из которых я понятия не имел, поэтому:
- Каковы другие способы достижения того, что делает
a+0L
?
Ответы
Ответ 1
x + 0L
является элементарной операцией на x
; как таковой, он часто сохраняет форму данных. as.integer
isnt: он принимает всю структуру - здесь, матрицу - и преобразует ее в одномерный целочисленный вектор.
Тем не менее, в общем случае Id настоятельно предлагает использовать as.integer
и препятствовать + 0L
умному взлому (помните: часто, умный ≠ хороший). Если вы хотите сохранить форму данных, я предлагаю использовать метод Davids из комментариев, а не + 0L
hack:
a[] = as.integer(a)
Это использует обычный смысл as.integer
, но результат присваивается отдельным элементам a
, а не самому a
. Другими словами, форма a
s остается нетронутой.
Ответ 2
Добавление 0L
повышает a
до целого числа, как описано в ?Arithmetic
:
Логические векторы будут принуждаться к целым или числовым векторам, FALSE с нулевым значением и ИСТИНА, имеющим значение 1.
Как следствие, любая арифметическая операция, использующая a
и элемент идентификации для этой операции (но не обязательно, чтобы перейти к числовому в какой-либо точке, например /
и ^
), будет работать:
a+0L
a-0L
a*1L
a%/%1
Унарные операции также будут работать, поэтому, возможно, "наилучшая" версия для гольфа для кодов:
--a
Это имеет параллель с общим трюком использования !!a
для преобразования числового объекта в логический.
identical(a+0L, a-0L, a*1L, a%/%1L, --a)
[1] TRUE
Возврат к логическому:
identical(a, !!--a)
[1] TRUE
Альтернативный и, возможно, более ясный подход - напрямую изменить storage.mode
of a
:
storage.mode(a) <- "integer"
a
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
Ответ 3
(Этот ответ не добавляет другой альтернативы уже существующим, но я публикую только для того, чтобы убирать комментарии в этом потоке.)
as.integer
, по определению, ведет себя как as.vector
, т.е. разбивает все атрибуты (включая "dim" ) для создания R-вектора. Он просто не вернет тот же объект с измененным typeof
. Чтобы восстановить атрибуты после принуждения, "dim<-"
, "names<-"
, "class<-"
и т.д. Необходимо вызвать явно или через функцию, которая хранит атрибуты своих аргументов (например, "[<-"
). Например. "dim<-"(as.integer(a), dim(a))
или array(as.integer(a), dim(a))
или a[] <- as.integer(a)
. Тест:
x = matrix(T, 1e3, 1e3)
microbenchmark::microbenchmark("dim<-"(as.integer(x), dim(x)),
array(as.integer(x), dim(x)),
{ x[] = as.integer(x) }, times = 25)
#Unit: milliseconds
# expr min lq median uq max neval
# `dim<-`(as.integer(x), dim(x)) 1.650232 1.691296 2.492748 4.237985 5.67872 25
# array(as.integer(x), dim(x)) 6.226130 6.638513 8.526779 8.973268 47.50351 25
# { x[] = as.integer(x) } 7.822421 8.071243 9.658487 10.408435 11.90798 25
В приведенном выше примере "dim<-"
justs добавляет атрибут к созданному as.integer(x)
, array
выделяет новый вектор для хранения созданных as.integer(x)
и "[<-"
изменений "x", чтобы он мог принимать значения созданного as.integer(x)
и, следовательно, итерации через "x" для вставки его новых значений.
Однако метод "[<-"
имеет недостаток:
x = as.character(1:5)
x
#[1] "1" "2" "3" "4" "5"
x[] = as.integer(x)
x
#[1] "1" "2" "3" "4" "5"
Или:
x = 1:5
x
#[1] 1 2 3 4 5
x[] = as.logical(x)
x
#[1] 1 1 1 1 1
Но:
x = round(runif(5), 2)
x
#[1] 0.68 0.54 0.02 0.14 0.08
x[] = as.character(x)
x
#[1] "0.68" "0.54" "0.02" "0.14" "0.08"
т.е. "[<-"
не изменит typeof
сменяемого объекта, если typeof
объекта замены выше. Subassignment (т.е. "[<-"
) принуждает либо объект, подлежащий замене, либо заменяющий объект, либо ни один в зависимости от их typeof
(это выполняется SubassignTypeFix
). @Josh O'Brien отмечает возможность существования разницы в поведении "[<-"
, если индексы отсутствуют. Честно говоря, я не мог найти конкретного лечения в таком случае, как например, do_subset_dflt
("["
), который косвенно обрабатывает missingness.
Как уже упоминалось, существует также "storage.mode<-"
для изменения typeof
объекта:
"storage.mode<-"(as.character(1:5), "integer")
#[1] 1 2 3 4 5
"storage.mode<-"(1:5, "logical")
#[1] TRUE TRUE TRUE TRUE TRUE
"storage.mode<-"(round(runif(5), 2), "character")
#[1] "0.09" "0.38" "0.98" "0.73" "0.81"
x = matrix(T, 1e3, 1e3)
microbenchmark::microbenchmark("storage.mode<-"(x, "integer"),
"dim<-"(as.integer(x), dim(x)), times = 25)
#Unit: milliseconds
# expr min lq median uq max neval
# `storage.mode<-`(x, "integer") 1.986055 2.01842 2.147181 2.406096 6.019415 25
# `dim<-`(as.integer(x), dim(x)) 1.984664 2.02016 2.111684 2.613854 6.174973 25
Аналогично с эффективностью "dim<-"
, поскольку они одновременно принудительно и сохраняют атрибут.
Двоичные операции (как упоминалось Джеймсом и Конрадом Рудольфом) сводят аргументы к подходящим typeof
и сохраняют атрибуты ( "dim", "names", "class" и т.д.) в зависимости от правил, относящихся к двум аргументам. (Раздел "Значение" в ?Arithmetic
)