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

Какая разница между 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?
4b9b3361

Ответ 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)