Verifique se o número é inteiro

104

Fiquei surpreso ao saber que R não vem com uma função útil para verificar se o número é inteiro.

is.integer(66) # FALSE

Os arquivos de ajuda avisam :

is.integer(x)não testa se x contém números inteiros! Para isso, use round, como na função is.wholenumber(x)dos exemplos.

O exemplo tem esta função personalizada como uma "solução alternativa"

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

Se eu tivesse que escrever uma função para verificar se há inteiros, presumindo que não li os comentários acima, eu escreveria uma função que teria algo como

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

Onde minha abordagem falharia? Qual seria o seu trabalho se você estivesse no meu lugar hipotético?

Roman Luštrik
fonte
Espero que, se round(x)for implementado corretamente, o resultado de aplicá-lo a um número inteiro será sempre esse número inteiro ...
Stephen
Dê uma olhada no FAQ em R cran.r-project.org/doc/FAQ/…
Richie Cotton
5
> check.integer (9.0) [1] TRUE não é.
Peng Peng
@PengPeng, VitoshKa corrigiu isso na resposta aceita.
Roman Luštrik
4
Eu acho que há uma confusão sobre os conceitos matemáticos e computacionais de inteiro. A função is.integerverifica o conceito computacional, a check.integerfunção de usuário verifica o ponto de vista matemático.
João Daniel

Respostas:

126

Outra alternativa é verificar a parte fracionária:

x%%1==0

ou, se você quiser verificar dentro de uma certa tolerância:

min(abs(c(x%%1, x%%1-1))) < tol
James
fonte
1
a sugestão de verificação de tolerância realmente funciona ?? x <- 5-1e-8; x%%1dá 0,9999999 (o que implicaria se, tol==1e-5por exemplo) que nãox é um inteiro.
Ben Bolker
@BenBolker Boa captura, ele funciona para perturbações positivas, eu acho. Eu mudei para uma solução alternativa deve funcionar.
James
2
@James, acho que deveria ser ao min(abs(c(x%%1, x%%1-1))) < tolinvés do abs(min(x%%1, x%%1-1)) < tolcontrário, você obterá FALSEpara qualquer número inteiro ...
Cath
3
O que há de errado as.integer(x) == x? Ele não rejeitará 3 ou 3.0 como is.integer(x)faria e pegará 3.1.
Gabi,
34

Esta é uma solução usando funções mais simples e sem hacks:

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

Além do mais, você pode testar um vetor inteiro de uma vez, se desejar. Aqui está uma função:

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

Você pode alterá-lo para usar *applyno caso de vetores, matrizes, etc.

Iterator
fonte
11
O último if elsepoderia ser feito de forma simples isTRUE(test). Na verdade, isso é tudo de que você precisa para substituir a if elsecláusula e as returndeclarações, pois R retorna automaticamente o resultado da última avaliação.
Gavin Simpson
7
testInteger(1.0000001)[1] FALSE testInteger(1.00000001)[1] TRUE
PatrickT
3
all(a == as.integer(a))contorna este problema! '
Alex
Isso não está funcionando corretamente! Verifique o seguinte contra-exemplo: frac_test <- 1 / (1-0.98), all.equal (frac_test, as.integer (frac_test)), isTRUE (all.equal (frac_test, as.integer (frac_test)))
tstudio
11

Ler a documentação da linguagem R as.integertem mais a ver com a forma como o número é armazenado do que se ele é praticamente equivalente a um inteiro. is.integertesta se o número é declarado como um inteiro. Você pode declarar um número inteiro colocando um Ldepois dele.

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

Também funções como roundretornarão um inteiro declarado, que é o que você está fazendo x==round(x). O problema com essa abordagem é o que você considera ser praticamente um número inteiro. O exemplo usa menos precisão para testar a equivalência.

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

Portanto, dependendo de sua aplicação, você pode ter problemas dessa forma.

Andrew Redd
fonte
1
A segunda linha diz "as.integer testa se o número é declarado como um inteiro." mas tenho certeza de que você quis dizer "is.integer". É apenas uma edição de um personagem, então eu não poderia mudá-la facilmente.
PeterVermont
10

Aqui está uma maneira aparentemente confiável:

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

Esta solução também permite números inteiros em notação científica:

> check.integer(222e3)
[1] TRUE
VitoshKa
fonte
1
Isso não parece muito confiável para mim: check.integer(1e4)é VERDADEIRO, enquanto check.integer(1e5)é FALSO.
semana
5
-1 Isso é pior do que is.wholenumberqualquer uma das outras soluções fornecidas em outras respostas. Estes não devem ser diferentes: check.integer(1e22); check.integer(1e23). Obviamente, você pode alterar a regex para corrigir isso, mas essa abordagem é terrível. (O comentário vem da atribuição no pacote de instalação.)
Joshua Ulrich
1
@PatrickT, entendo. É o argumento do dígito padrão. use em seu format(40, scientific = FALSE, digits = 20)lugar. Eu atualizei a resposta. Obrigado por perceber isso.
VitoshKa
1
@PatrickT Você está no reino dos erros de arredondamento dependentes da máquina. Nesse aspecto, minha solução é a mesma aceita 1.0000000000000001 == 1L [1] TRUE. Mas minha solução é melhor se você já tiver um número na forma de stringcheck.integer("1000000000000000000000000000000000001") [1] TRUE
VitoshKa 01 de
4
@VitoshKa amou sua resposta! Embora haja um ponto que você perdeu, números negativos sem pontos decimais também são inteiros;) Modifiquei seu código de acordo.
Mehrad Mahmoudian
8

Parece que você não vê a necessidade de incorporar alguma tolerância de erro. Não seria necessário se todos os inteiros viessem inseridos como inteiros, no entanto, às vezes eles vêm como resultado de operações aritméticas que perdem alguma precisão. Por exemplo:

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

Observe que essa não é a fraqueza de R, todos os softwares de computador têm alguns limites de precisão.

Aniko
fonte
3
apenas no caso de algumas pessoas não entenderem bem o que aconteceu aqui ... se você inserir as.integer (2/49 * 49), obterá 1 !! [BTW, é sempre tão frustrante que R não apresenta o resultado do cálculo inicial como 2,0 para representar que o valor tem algum componente decimal) consulte ... stackoverflow.com/questions/1535021/…
John
6

De Hmisc::spss.get:

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

opção muito mais segura, IMHO, uma vez que "contorna" o problema de precisão da máquina. Se você tentar is.integer(floor(1)), você conseguirá FALSE. BTW, seu número inteiro não será salvo como número inteiro se for maior que o .Machine$integer.maxvalor, que é, por padrão, 2147483647, então altere o integer.maxvalor ou faça as verificações alternativas ...

aL3xa
fonte
1
se x <- sqrt(2)^2, então all(floor(x) == x, na.rm = TRUE)volteFALSE
Corrado
3

você pode usar uma condição simples como:

if(round(var) != var)­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Meru Patil
fonte
1

Em R, se um número é numérico ou inteiro pode ser determinado pela função de classe. Geralmente todos os números são armazenados como numéricos e para definir explicitamente um número como inteiro, precisamos especificar 'L' após o número.

Exemplo:

x <- 1

classe (x)

[1] "numérico"

x <- 1L

classe (x)

[1] "inteiro"

Espero que isso seja o que era necessário. Obrigado :)

Meenansha Sachdeva
fonte
0

[ATUALIZAÇÃO] =================================================== ===============

Com respeito à resposta [ANTIGA] aqui abaixo, descobri que funcionou porque coloquei todos os números em um único vetor atômico; um deles era um personagem, então todos se tornam personagens.

Se usarmos uma lista (portanto, a coerção não ocorre), todos os testes passam corretamente, exceto um 1/(1 - 0.98):, que permanece a numeric. Isso porque o tolparâmetro é por padrão 100 * .Machine$double.epse esse número está longe de ser 50pouco menos do que o dobro disso. Então, basicamente, para este tipo de números, temos que decidir nossa tolerância!

Então, se você quiser que todos os testes se tornem TRUE, você podeassertive::is_whole_number(x, tol = 200 * .Machine$double.eps)

De qualquer forma, confirmo que a assertiva IMO continua sendo a melhor solução.

Aqui abaixo uma reprex para este [UPDATE].

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

Criado em 23/07/2019 pelo pacote reprex (v0.3.0)

[ANTIGO] ===================================================== ==================

IMO a melhor solução vem do assertivepacote (que, por enquanto, resolve todos os exemplos positivos e negativos neste tópico):

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

Criado em 23/07/2019 pelo pacote reprex (v0.3.0)

Corrado
fonte
0

Se você preferir não escrever sua própria função, tente check.integerno instalador do pacote . Atualmente ele usa a resposta de VitoshKa.

Experimente também o check.numeric(v, only.integer=TRUE)pacote varhandle , que tem a vantagem de ser vetorizado.

qwr
fonte
0

Uma vez também pode usar dplyr::near:

library(dplyr)

near(a, as.integer(a))

Ele se aplica a qualquer vetor ae tem um parâmetro de tolerância opcional.

James Hirschorn
fonte
-3

Não tenho certeza do que você está tentando realizar. Mas aqui estão algumas idéias:
1. Converta para inteiro:
num = as.integer(123.2342)
2. Verifique se uma variável é um inteiro:
is.integer(num)
typeof(num)=="integer"

Bernardw
fonte
Estou apenas verificando se os usuários inserem um número apropriado - estamos falando sobre o número de "assuntos", que pode ser apenas um número inteiro.
Roman Luštrik