Проверить, является ли число целым

107

Я был удивлен, узнав, что в R нет удобной функции для проверки целого числа.

is.integer(66) # FALSE

Эти файлы справки предупреждает :

is.integer(x)не проверяет, x содержит ли целые числа! Для этого используйте round, как в функции is.wholenumber(x)в примерах.

В примере эта настраиваемая функция используется в качестве «обходного пути».

is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol
is.wholenumber(1) # is TRUE

Если бы мне пришлось написать функцию для проверки целых чисел, предполагая, что я не читал приведенные выше комментарии, я бы написал функцию, которая выполняла бы что-то вроде

check.integer <- function(x) {
    x == round(x)
}

Где мой подход потерпит неудачу? Что бы вы сделали, если бы были на моем гипотетическом месте?

Роман Луштрик
источник
Я надеюсь, что если все round(x)будет реализовано правильно, результатом его применения к целому числу всегда будет это целое число ...
Стивен
Взгляните на FAQ на сайте R cran.r-project.org/doc/FAQ/…
Ричи Коттон,
5
> check.integer (9.0) [1] ИСТИНА, это не так.
Peng Peng
@PengPeng, VitoshKa исправил это в принятом ответе.
Roman Luštrik
4
Я думаю, что существует путаница в математических и вычислительных концепциях целого числа. Функция is.integerпроверяет вычислительную концепцию, check.integerпользовательская функция проверяет математическую точку зрения.
João Daniel

Ответы:

127

Другой вариант - проверить дробную часть:

x%%1==0

или, если вы хотите проверить в пределах определенного допуска:

min(abs(c(x%%1, x%%1-1))) < tol
Джеймс
источник
1
предложение проверки допуска действительно работает ?? x <- 5-1e-8; x%%1дает 0.9999999 (что означало бы , если tol==1e-5, например) , что xэто не является целым числом.
Бен Болкер
@BenBolker Хороший улов, я думаю, он работает при положительных возмущениях. Я изменил его на альтернативное решение, которое должно работать.
Джеймс
2
@ Джеймс, я думаю, это должно быть, min(abs(c(x%%1, x%%1-1))) < tolа abs(min(x%%1, x%%1-1)) < tolне иначе, вы получите FALSEза любое целое число ...
Кэт,
3
Что не так as.integer(x) == x? Он не отклонит 3 или 3,0, как is.integer(x)хотел бы, а поймает 3,1.
Габи
34

Вот решение, использующее более простые функции и никаких хаков:

all.equal(a, as.integer(a))

Более того, вы можете протестировать сразу весь вектор, если хотите. Вот функция:

testInteger <- function(x){
  test <- all.equal(x, as.integer(x), check.attributes = FALSE)
  if(test == TRUE){ return(TRUE) }
  else { return(FALSE) }
}

Вы можете изменить его для использования *applyв случае векторов, матриц и т. Д.

Итератор
источник
11
Последнее if elseможно было сделать просто isTRUE(test). В самом деле, это все, что вам нужно для замены if elseпредложения и returnоператоров, поскольку R автоматически возвращает результат последней оценки.
Гэвин Симпсон
7
testInteger(1.0000001)[1] ЛОЖЬ testInteger(1.00000001)[1] ИСТИНА
PatrickT
3
all(a == as.integer(a))обходит эту проблему! '
Alex
Это не работает должным образом! Посмотрите следующий контрпример: frac_test <- 1 / (1-0.98), all.equal (frac_test, as.integer (frac_test)), isTRUE (all.equal (frac_test, as.integer (frac_test)))
tstudio 06
11

Чтение документации на языке R as.integerбольше связано с тем, как хранится число, чем если оно практически эквивалентно целому числу. is.integerпроверяет, объявлено ли число как целое. Вы можете объявить целое число, поставив Lпосле него.

> is.integer(66L)
[1] TRUE
> is.integer(66)
[1] FALSE

Также такие функции, как roundвозвращают объявленное целое число, что вы и делаете x==round(x). Проблема с этим подходом заключается в том, что вы считаете практически целым числом. В примере используется меньшая точность для проверки эквивалентности.

> is.wholenumber(1+2^-50)
[1] TRUE
> check.integer(1+2^-50)
[1] FALSE

Так что, в зависимости от вашего приложения, у вас могут возникнуть проблемы.

Эндрю Редд
источник
1
Во второй строке написано: «as.integer проверяет, объявлено ли число как целое». но я почти уверен, что вы имели в виду "is.integer". Это редактирование только одного символа, поэтому я не мог легко его изменить.
PeterVermont
10

Вот один, по-видимому, надежный способ:

check.integer <- function(N){
    !grepl("[^[:digit:]]", format(N,  digits = 20, scientific = FALSE))
}

check.integer(3243)
#TRUE
check.integer(3243.34)
#FALSE
check.integer("sdfds")
#FALSE

Это решение также позволяет использовать целые числа в экспоненциальной нотации:

> check.integer(222e3)
[1] TRUE
ВитошКа
источник
1
Мне это кажется не очень надежным: check.integer(1e4)ИСТИНА, а check.integer(1e5)ЛОЖЬ.
wch
5
-1 Это хуже, чем is.wholenumberлюбое другое решение, представленное в других ответах. Они не должны быть разными: check.integer(1e22); check.integer(1e23). Очевидно, вы можете изменить регулярное выражение, чтобы исправить это, но этот подход ужасен. (Комментарий предоставлен авторством в пакете установщика.)
Джошуа Ульрих
1
@PatrickT, понятно. Это аргумент цифры по умолчанию. используйте format(40, scientific = FALSE, digits = 20)вместо этого. Я обновил ответ. Спасибо, что заметили это.
VitoshKa
1
@PatrickT Вы попали в область машинно-зависимых ошибок округления. В этом отношении мое решение совпадает с принятым 1.0000000000000001 == 1L [1] TRUE. Но мое решение лучше, если вы уже получили число в строковой формеcheck.integer("1000000000000000000000000000000000001") [1] TRUE
VitoshKa 01
4
@VitoshKa понравился твой ответ! Хотя есть один момент, который вы упустили, отрицательные числа без десятичных знаков также являются целыми;) Я соответствующим образом изменил ваш код.
Мехрад Махмудиан
8

Похоже, что вы не видите необходимости включать некоторую устойчивость к ошибкам. В этом не было бы необходимости, если бы все целые числа вводились как целые числа, однако иногда они появляются в результате арифметических операций, которые теряют некоторую точность. Например:

> 2/49*49
[1] 2
> check.integer(2/49*49)
[1] FALSE 
> is.wholenumber(2/49*49)
[1] TRUE

Обратите внимание, что это не слабость R, все компьютерные программы имеют некоторые пределы точности.

Анико
источник
3
на всякий случай некоторые люди не совсем понимают, что здесь произошло ... если вы введете as.integer (2/49 * 49), вы получите 1 !! [Кстати, очень неприятно, что R не представляет результат начального вычисления как 2.0, чтобы представить, что значение имеет некоторый десятичный компонент) см. ... stackoverflow.com/questions/1535021/…
John
6

Откуда Hmisc::spss.get:

all(floor(x) == x, na.rm = TRUE)

ИМХО гораздо более безопасный вариант, поскольку он "обходит" проблему точности станка. Если попробуешь is.integer(floor(1)), получишь FALSE. Кстати, ваше целое число не будет сохранено как целое, если оно больше .Machine$integer.maxзначения, которое по умолчанию составляет 2147483647, поэтому либо измените integer.maxзначение, либо выполните альтернативные проверки ...

aL3xa
источник
1
если x <- sqrt(2)^2, то all(floor(x) == x, na.rm = TRUE)вернемсяFALSE
Corrado
3

вы можете использовать простое условие if, например:

if(round(var) != var)­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Меру Патил
источник
1

В R, является ли число числовым или целым, можно определить функцией класса. Как правило, все числа хранятся как числовые, и чтобы явно определить число как целое, нам нужно указать «L» после числа.

Пример:

х <- 1

класс (x)

[1] "числовой"

х <- 1л

класс (x)

[1] "целое число"

Надеюсь, это то, что было нужно. Спасибо :)

Минанша Сачдева
источник
0

[ОБНОВЛЕНИЕ] ================================================= ===============

Что касается [СТАРОГО] ответа здесь ниже, я обнаружил, что это сработало, потому что я поместил все числа в один атомарный вектор; один из них был персонажем, поэтому каждый стал персонажем.

Если мы используем список (следовательно, принуждение не происходит), все тесты проходят правильно, кроме одного:, 1/(1 - 0.98)который остается numeric. Это потому, что tolпараметр установлен по умолчанию, 100 * .Machine$double.epsи это число далеко 50не меньше его удвоения. Итак, в основном для таких чисел у нас есть определить нашу терпимость!

Так что если вы хотите, чтобы все испытания стали TRUE, вы можетеassertive::is_whole_number(x, tol = 200 * .Machine$double.eps)

В любом случае, я подтверждаю, что напористый IMO остается лучшим решением.

Ниже представлен пример этого [ОБНОВЛЕНИЕ].

expect_trues_c <- c(
  cl = sqrt(2)^2,
  pp = 9.0,
  t = 1 / (1 - 0.98),
  ar0 = 66L,
  ar1 = 66,
  ar2 = 1 + 2^-50,
  v = 222e3,
  w1 = 1e4,
  w2 = 1e5,
  v2 = "1000000000000000000000000000000000001",
  an = 2 / 49 * 49,
  ju1 = 1e22,
  ju2 = 1e24,
  al = floor(1),
  v5 = 1.0000000000000001 # this is under machine precision!
)

str(expect_trues_c)
#>  Named chr [1:15] "2" "9" "50" "66" "66" "1" "222000" "10000" "1e+05" ...
#>  - attr(*, "names")= chr [1:15] "cl" "pp" "t" "ar0" ...
assertive::is_whole_number(expect_trues_c)
#> Warning: Coercing expect_trues_c to class 'numeric'.
#>                      2                      9                     50 
#>                   TRUE                   TRUE                   TRUE 
#>                     66                     66                      1 
#>                   TRUE                   TRUE                   TRUE 
#>                 222000                  10000                 100000 
#>                   TRUE                   TRUE                   TRUE 
#>                  1e+36                      2                  1e+22 
#>                   TRUE                   TRUE                   TRUE 
#> 9.9999999999999998e+23                      1                      1 
#>                   TRUE                   TRUE                   TRUE



expect_trues_l <- list(
  cl = sqrt(2)^2,
  pp = 9.0,
  t = 1 / (1 - 0.98),
  ar0 = 66L,
  ar1 = 66,
  ar2 = 1 + 2^-50,
  v = 222e3,
  w1 = 1e4,
  w2 = 1e5,
  v2 = "1000000000000000000000000000000000001",
  an = 2 / 49 * 49,
  ju1 = 1e22,
  ju2 = 1e24,
  al = floor(1),
  v5 = 1.0000000000000001 # this is under machine precision!
)

str(expect_trues_l)
#> List of 15
#>  $ cl : num 2
#>  $ pp : num 9
#>  $ t  : num 50
#>  $ ar0: int 66
#>  $ ar1: num 66
#>  $ ar2: num 1
#>  $ v  : num 222000
#>  $ w1 : num 10000
#>  $ w2 : num 1e+05
#>  $ v2 : chr "1000000000000000000000000000000000001"
#>  $ an : num 2
#>  $ ju1: num 1e+22
#>  $ ju2: num 1e+24
#>  $ al : num 1
#>  $ v5 : num 1
assertive::is_whole_number(expect_trues_l)
#> Warning: Coercing expect_trues_l to class 'numeric'.
#> There was 1 failure:
#>   Position              Value      Cause
#> 1        3 49.999999999999957 fractional
assertive::is_whole_number(expect_trues_l, tol = 200 * .Machine$double.eps)
#> Warning: Coercing expect_trues_l to class 'numeric'.
#>     2.0000000000000004                      9     49.999999999999957 
#>                   TRUE                   TRUE                   TRUE 
#>                     66                     66     1.0000000000000009 
#>                   TRUE                   TRUE                   TRUE 
#>                 222000                  10000                 100000 
#>                   TRUE                   TRUE                   TRUE 
#>                  1e+36     1.9999999999999998                  1e+22 
#>                   TRUE                   TRUE                   TRUE 
#> 9.9999999999999998e+23                      1                      1 
#>                   TRUE                   TRUE                   TRUE



expect_falses <- list(
  bb = 5 - 1e-8,
  pt1 = 1.0000001,
  pt2 = 1.00000001,
  v3 = 3243.34,
  v4 = "sdfds"
)

str(expect_falses)
#> List of 5
#>  $ bb : num 5
#>  $ pt1: num 1
#>  $ pt2: num 1
#>  $ v3 : num 3243
#>  $ v4 : chr "sdfds"
assertive::is_whole_number(expect_falses)
#> Warning: Coercing expect_falses to class 'numeric'.
#> Warning in as.this_class(x): NAs introduced by coercion
#> There were 5 failures:
#>   Position              Value      Cause
#> 1        1 4.9999999900000001 fractional
#> 2        2 1.0000001000000001 fractional
#> 3        3 1.0000000099999999 fractional
#> 4        4 3243.3400000000001 fractional
#> 5        5               <NA>    missing
assertive::is_whole_number(expect_falses, tol = 200 * .Machine$double.eps)
#> Warning: Coercing expect_falses to class 'numeric'.

#> Warning: NAs introduced by coercion
#> There were 5 failures:
#>   Position              Value      Cause
#> 1        1 4.9999999900000001 fractional
#> 2        2 1.0000001000000001 fractional
#> 3        3 1.0000000099999999 fractional
#> 4        4 3243.3400000000001 fractional
#> 5        5               <NA>    missing

Создано 23.07.2019 пакетом REPEX (v0.3.0)

[СТАРЫЙ] =============================================== ==================

IMO лучшее решение исходит из assertiveпакета (который на данный момент решает все положительные и отрицательные примеры в этом потоке):

are_all_whole_numbers <- function(x) {
  all(assertive::is_whole_number(x), na.rm = TRUE)
}

are_all_whole_numbers(c(
  cl = sqrt(2)^2,
  pp = 9.0,
  t = 1 / (1 - 0.98),
  ar0 = 66L,
  ar1 = 66,
  ar2 = 1 + 2^-50,
  v = 222e3,
  w1 = 1e4,
  w2 = 1e5,
  v2 = "1000000000000000000000000000000000001",
  an = 2 / 49 * 49,
  ju1 = 1e22,
  ju2 = 1e24,
  al = floor(1),
  v5 = 1.0000000000000001 # difference is under machine precision!
))
#> Warning: Coercing x to class 'numeric'.
#> [1] TRUE

are_all_not_whole_numbers <- function(x) {
  all(!assertive::is_whole_number(x), na.rm = TRUE)
}

are_all_not_whole_numbers(c(
  bb = 5 - 1e-8,
  pt1 = 1.0000001,
  pt2 = 1.00000001,
  v3 = 3243.34,
  v4 = "sdfds"
))
#> Warning: Coercing x to class 'numeric'.
#> Warning in as.this_class(x): NAs introduced by coercion
#> [1] TRUE

Создано 23.07.2019 пакетом REPEX (v0.3.0)

Коррадо
источник
0

Once также можно использовать dplyr::near:

library(dplyr)

near(a, as.integer(a))

Он применяется к любому вектору aи имеет необязательный параметр допуска.

Джеймс Хиршорн
источник
-3

Я не уверен, чего вы пытаетесь достичь. Но вот некоторые мысли:
1. Преобразовать в целое число:
num = as.integer(123.2342)
2. Проверить, является ли переменная целым числом:
is.integer(num)
typeof(num)=="integer"

Bernardw
источник
Я просто убеждаюсь, что пользователи вводят соответствующее число - мы говорим о количестве «субъектов», которое может быть только целым числом.
Роман Луштрик,