Vérifiez si le nombre est un entier

106

J'ai été surpris d'apprendre que R ne vient pas avec une fonction pratique pour vérifier si le nombre est entier.

is.integer(66) # FALSE

Les fichiers d'aide avertissent :

is.integer(x)ne teste pas si x contient des nombres entiers! Pour cela, utilisez round, comme dans la fonction is.wholenumber(x)dans les exemples.

L'exemple a cette fonction personnalisée comme "solution de contournement"

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

Si je devais écrire une fonction pour vérifier les entiers, en supposant que je n'ai pas lu les commentaires ci-dessus, j'écrirais une fonction qui irait dans le sens de

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

Où mon approche échouerait-elle? Quel serait votre travail si vous étiez à ma place hypothétique?

Roman Luštrik
la source
J'espère que si round(x)est mis en œuvre correctement, le résultat de son application à un entier serait toujours cet entier ...
Stephen
Jetez un œil à la FAQ sur R cran.r-project.org/doc/FAQ/…
Richie Cotton
5
> check.integer (9.0) [1] TRUE ce n'est pas le cas.
Peng Peng
@PengPeng, VitoshKa a corrigé ce problème dans la réponse acceptée.
Roman Luštrik
4
Je pense qu'il y a une confusion entre les concepts mathématiques et informatiques des nombres entiers. La fonction is.integervérifie le concept de calcul, la check.integerfonction utilisateur vérifie le point de vue mathématique.
João Daniel

Réponses:

126

Une autre alternative consiste à vérifier la partie fractionnaire:

x%%1==0

ou, si vous souhaitez vérifier dans une certaine tolérance:

min(abs(c(x%%1, x%%1-1))) < tol
James
la source
1
la suggestion de contrôle de tolérance fonctionne-t-elle vraiment? x <- 5-1e-8; x%%1donne 0,9999999 (ce qui impliquerait si tol==1e-5par exemple) qui xn'est pas un entier.
Ben Bolker
@BenBolker Bonne prise, ça marche pour les perturbations positives je pense. Je l'ai changé pour une solution alternative devrait fonctionner.
James
2
@James, je pense que ça devrait être min(abs(c(x%%1, x%%1-1))) < tolplutôt que abs(min(x%%1, x%%1-1)) < tolsinon, vous obtiendrez FALSEpour n'importe quel entier ...
Cath
3
Quel est le problème avec as.integer(x) == x? Il ne rejettera pas 3 ou 3.0 comme le is.integer(x)ferait, et il attrapera 3.1.
Gabi
34

Voici une solution utilisant des fonctions plus simples et pas de hacks:

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

De plus, vous pouvez tester un vecteur entier à la fois, si vous le souhaitez. Voici une fonction:

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

Vous pouvez le modifier pour l'utiliser *applydans le cas de vecteurs, matrices, etc.

Itérateur
la source
11
Le dernier if elsepourrait être fait simplement isTRUE(test). En effet, c'est tout ce dont vous avez besoin pour remplacer la if elseclause et les returninstructions car R renvoie automatiquement le résultat de la dernière évaluation.
Gavin Simpson
7
testInteger(1.0000001)[1] FALSE testInteger(1.00000001)[1] TRUE
PatrickT
3
all(a == as.integer(a))contourne ce problème!
Alex
Cela ne fonctionne pas correctement! Consultez le contre-exemple suivant: 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

La lecture de la documentation du langage R as.integera plus à voir avec la façon dont le nombre est stocké que s'il est pratiquement équivalent à un entier. is.integerteste si le nombre est déclaré comme un entier. Vous pouvez déclarer un entier en mettant un Laprès.

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

Des fonctions comme roundrenverront également un entier déclaré, ce que vous faites avec x==round(x). Le problème avec cette approche est ce que vous considérez être pratiquement un entier. L'exemple utilise moins de précision pour tester l'équivalence.

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

Donc, en fonction de votre application, vous pourriez avoir des problèmes de cette façon.

Andrew Redd
la source
1
La deuxième ligne dit "as.integer teste si le nombre est déclaré comme un entier". mais je suis sûr que vous vouliez dire "is.integer". Ce n'est qu'une modification d'un seul caractère, donc je ne pouvais pas le changer facilement.
PeterVermont
10

Voici un moyen apparemment fiable:

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

Cette solution permet également d'utiliser des nombres entiers en notation scientifique:

> check.integer(222e3)
[1] TRUE
VitoshKa
la source
1
Cela ne me semble pas très fiable: check.integer(1e4)c'est VRAI, alors que check.integer(1e5)c'est FAUX.
wch
5
-1 C'est pire que is.wholenumber, ou l'une des autres solutions fournies dans d'autres réponses. Ceux - ci ne devraient pas être différentes: check.integer(1e22); check.integer(1e23). Vous pouvez évidemment changer l'expression régulière pour résoudre ce problème, mais cette approche est affreuse. (Le commentaire provient de l'attribution dans le package installr.)
Joshua Ulrich
1
@PatrickT, je vois. C'est l'argument du chiffre par défaut. utiliser à la format(40, scientific = FALSE, digits = 20)place. J'ai mis à jour la réponse. Merci de l'avoir repéré.
VitoshKa
1
@PatrickT Vous êtes dans le domaine des erreurs d'arrondi dépendant de la machine. À cet égard, ma solution est la même que celle acceptée 1.0000000000000001 == 1L [1] TRUE. Mais ma solution est meilleure si vous obtenez déjà un nombre sous forme de chaînecheck.integer("1000000000000000000000000000000000001") [1] TRUE
VitoshKa
4
@VitoshKa a adoré votre réponse! Bien qu'il y ait un point que vous avez manqué, les nombres négatifs sans décimales sont également des nombres entiers;) J'ai modifié votre code en conséquence.
Mehrad Mahmoudian
8

Il semble que vous ne voyiez pas la nécessité d'incorporer une certaine tolérance d'erreur. Cela ne serait pas nécessaire si tous les entiers étaient entrés sous forme d'entiers, mais parfois ils résultent d'opérations arithmétiques qui perdent une certaine précision. Par exemple:

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

Notez que ce n'est pas la faiblesse de R, tous les logiciels informatiques ont des limites de précision.

Aniko
la source
3
juste au cas où certaines personnes ne comprennent pas tout à fait ce qui s'est passé ici ... si vous entrez as.integer (2/49 * 49) vous obtenez 1 !! [BTW, il est tellement frustrant que R ne présente pas le résultat du calcul initial comme 2.0 pour représenter que la valeur a une composante décimale) voir ... stackoverflow.com/questions/1535021/…
John
6

De Hmisc::spss.get:

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

option beaucoup plus sûre, à mon humble avis, car elle «contourne» le problème de précision de la machine. Si vous essayez is.integer(floor(1)), vous obtiendrez FALSE. BTW, votre entier ne sera pas enregistré en tant qu'entier s'il est supérieur à la .Machine$integer.maxvaleur, qui est, par défaut, 2147483647, alors changez la integer.maxvaleur ou effectuez les vérifications alternatives ...

aL3xa
la source
1
si x <- sqrt(2)^2, puis all(floor(x) == x, na.rm = TRUE)retourFALSE
Corrado
3

vous pouvez utiliser une condition if simple comme:

if(round(var) != var)­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Meru Patil
la source
1

Dans R, si un nombre est numérique ou entier peut être déterminé par la fonction de classe. Généralement, tous les nombres sont stockés sous forme numérique et pour définir explicitement un nombre comme entier, nous devons spécifier «L» après le nombre.

Exemple:

x <- 1

classe (x)

[1] "numérique"

x <- 1 L

classe (x)

[1] "entier"

J'espère que c'est ce dont nous avions besoin. Merci :)

Meenansha Sachdeva
la source
0

[MISE À JOUR] =============================================== ===============

En ce qui concerne la réponse [ANCIENNE] ci-dessous, j'ai découvert que cela fonctionnait parce que j'ai mis tous les nombres dans un seul vecteur atomique; l'un d'eux était un personnage, donc chacun devient des personnages.

Si nous utilisons une liste (par conséquent, la coercition ne se produit pas) tout le test passe correctement sauf un:, 1/(1 - 0.98)qui reste un numeric. En effet, le tolparamètre est par défaut 100 * .Machine$double.epset ce nombre est loin d'être un 50peu inférieur au double de cela. Donc, en gros, pour ce genre de chiffres, nous avons à décider de notre tolérance!

Donc si vous voulez que tous les tests deviennent TRUE, vous pouvezassertive::is_whole_number(x, tol = 200 * .Machine$double.eps)

Quoi qu'il en soit, je confirme que l'affirmation de l'OMI reste la meilleure solution.

Ci-dessous un reprex pour cette [MISE À JOUR].

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

Créé le 23/07/2019 par le package reprex (v0.3.0)

[ANCIEN] =============================================== ===================

IMO la meilleure solution vient du assertivepackage (qui, pour le moment, résout tous les exemples positifs et négatifs de ce fil):

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

Créé le 23/07/2019 par le package reprex (v0.3.0)

Corrado
la source
0

Si vous préférez ne pas écrire votre propre fonction, essayez à check.integerpartir du package installr . Actuellement, il utilise la réponse de VitoshKa.

Essayez également le check.numeric(v, only.integer=TRUE)package varhandle , qui a l'avantage d'être vectorisé.

qwr
la source
0

Une fois peut également utiliser dplyr::near:

library(dplyr)

near(a, as.integer(a))

Il s'applique à n'importe quel vecteur aet possède un paramètre de tolérance facultatif.

James Hirschorn
la source
-3

Je ne suis pas sûr de ce que vous essayez d'accomplir. Mais voici quelques réflexions:
1. Convertir en entier:
num = as.integer(123.2342)
2. Vérifier si une variable est un entier:
is.integer(num)
typeof(num)=="integer"

bernardw
la source
Je m'assure simplement que les utilisateurs saisissent un nombre approprié - nous parlons du nombre de "sujets", qui ne peut être qu'un entier.
Roman Luštrik