Sprawdź, czy liczba jest liczbą całkowitą

107

Byłem zaskoczony, gdy dowiedziałem się, że R nie ma przydatnej funkcji do sprawdzenia, czy liczba jest liczbą całkowitą.

is.integer(66) # FALSE

Te pliki pomocy ostrzega :

is.integer(x)nie sprawdza, czy x zawiera liczby całkowite! W tym celu użyj round, jak w funkcji is.wholenumber(x)w przykładach.

W przykładzie zastosowano tę funkcję niestandardową jako „obejście”

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

Gdybym musiał napisać funkcję do sprawdzania liczb całkowitych, zakładając, że nie przeczytałem powyższych komentarzy, napisałbym funkcję, która poszłaby podobnie

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

Gdzie moje podejście zawiodłoby? Co byś obejrzał, gdybyś był w moich hipotetycznych butach?

Roman Luštrik
źródło
Mam nadzieję, że jeśli round(x)zostanie poprawnie zaimplementowany, wynikiem zastosowania go do liczby całkowitej będzie zawsze ta liczba całkowita ...
Stephen
Spójrz na FAQ na R cran.r-project.org/doc/FAQ/…
Richie Cotton
5
> check.integer (9.0) [1] TRUE to nie jest.
Peng Peng,
@PengPeng, VitoshKa naprawił to w zaakceptowanej odpowiedzi.
Roman Luštrik
4
Myślę, że istnieje niejasność dotycząca matematycznych i obliczeniowych koncepcji liczby całkowitej. Funkcja is.integersprawdza koncepcję obliczeniową, check.integerfunkcja użytkownika sprawdza matematyczny punkt widzenia.
João Daniel,

Odpowiedzi:

127

Inną alternatywą jest sprawdzenie części ułamkowej:

x%%1==0

lub, jeśli chcesz sprawdzić w ramach określonej tolerancji:

min(abs(c(x%%1, x%%1-1))) < tol
James
źródło
1
czy sugestia dotycząca sprawdzenia tolerancji naprawdę działa? x <- 5-1e-8; x%%1Daje 0.9999999 (co oznacza, jeśli tol==1e-5, na przykład), które xjest nie liczbą całkowitą.
Ben Bolker
@BenBolker Dobry chwyt, myślę, że działa na pozytywne perturbacje. Zmieniłem to na alternatywne rozwiązanie, które powinno działać.
James
2
@James, myślę, że powinno być min(abs(c(x%%1, x%%1-1))) < tolzamiast tego abs(min(x%%1, x%%1-1)) < tol, otrzymasz FALSEza dowolną liczbę całkowitą ...
Cath
3
Co jest nie tak as.integer(x) == x? Nie odrzuci 3 lub 3.0 tak jak is.integer(x)chciałbym i złapie 3.1.
Gabi
34

Oto rozwiązanie wykorzystujące prostsze funkcje i bez hacków:

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

Co więcej, jeśli chcesz, możesz przetestować cały wektor naraz. Oto funkcja:

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

Możesz go zmienić tak, aby był używany *applyw przypadku wektorów, macierzy itp.

Iterator
źródło
11
To ostatnie if elsemożna zrobić po prostu isTRUE(test). Rzeczywiście, to wszystko, czego potrzebujesz, aby zamienić if elseklauzulę i returninstrukcje, ponieważ R automatycznie zwraca wynik ostatniej oceny.
Gavin Simpson,
7
testInteger(1.0000001)[1] FALSE testInteger(1.00000001)[1] TRUE
PatrickT
3
all(a == as.integer(a))omija ten problem!
Alex
To nie działa poprawnie! Sprawdź następujący kontrprzykład: 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

Czytanie dokumentacji języka R as.integerma więcej wspólnego z tym, jak liczba jest przechowywana, niż jeśli jest praktycznie równoważna liczbie całkowitej. is.integersprawdza, czy liczba jest zadeklarowana jako liczba całkowita. Możesz zadeklarować liczbę całkowitą, umieszczając Lpo niej.

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

Również funkcje takie jak roundzwrócą zadeklarowaną liczbę całkowitą, czyli właśnie z nią korzystasz x==round(x). Problem z tym podejściem polega na tym, co uważamy za praktycznie liczbę całkowitą. Przykład używa mniejszej precyzji do testowania równoważności.

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

W zależności od aplikacji możesz w ten sposób wpaść w kłopoty.

Andrew Redd
źródło
1
W drugim wierszu jest napisane „as.integer” sprawdza, czy liczba jest zadeklarowana jako liczba całkowita. ale jestem pewien, że miałeś na myśli „is.integer”. To tylko jedna zmiana postaci, więc nie mogłem jej łatwo zmienić.
PeterVermont,
10

Oto jeden, pozornie niezawodny sposób:

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

To rozwiązanie dopuszcza również liczby całkowite w notacji naukowej:

> check.integer(222e3)
[1] TRUE
VitoshKa
źródło
1
Nie wygląda to na zbyt wiarygodne dla mnie: check.integer(1e4)jest PRAWDA, podczas gdy check.integer(1e5)jest FAŁSZ.
wch
5
-1 To jest gorsze niż is.wholenumberjakiekolwiek inne rozwiązanie podane w innych odpowiedziach. To nie powinno być inaczej: check.integer(1e22); check.integer(1e23). Oczywiście możesz zmienić wyrażenie regularne, aby to naprawić, ale takie podejście jest okropne. (Komentarz pochodzi z przypisania w pakiecie instalacyjnym.)
Joshua Ulrich
1
@PatrickT, rozumiem. To domyślny argument cyfry. użyj format(40, scientific = FALSE, digits = 20)zamiast tego. Zaktualizowałem odpowiedź. Dzięki, że to zauważyłeś.
VitoshKa
1
@PatrickT Znajdujesz się w sferze błędów zaokrąglania zależnych od maszyny. Pod tym względem moje rozwiązanie jest takie samo jak przyjęte 1.0000000000000001 == 1L [1] TRUE. Ale moje rozwiązanie jest lepsze, jeśli masz już liczbę w postaci ciągucheck.integer("1000000000000000000000000000000000001") [1] TRUE
VitoshKa
4
@VitoshKa uwielbiał twoją odpowiedź! Chociaż jest jeden punkt, który przegapiłeś, liczby ujemne bez kropek dziesiętnych również są liczbami całkowitymi;) Odpowiednio zmodyfikowałem twój kod.
Mehrad Mahmoudian
8

Wydaje się, że nie widzisz potrzeby uwzględnienia pewnej tolerancji na błędy. Nie byłoby konieczne, gdyby wszystkie liczby całkowite były wprowadzane jako liczby całkowite, jednak czasami pojawiają się one w wyniku operacji arytmetycznych, które tracą pewną precyzję. Na przykład:

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

Zauważ, że to nie jest słabość R, każde oprogramowanie komputerowe ma pewne ograniczenia dokładności.

Aniko
źródło
3
na wypadek, gdyby niektórzy ludzie nie rozumieli, co się tutaj stało ... jeśli wpiszesz as.integer (2/49 * 49), otrzymasz 1 !! [BTW, to zawsze tak frustrujące, że R nie przedstawia wyniku początkowych obliczeń jako 2.0, aby wskazać, że wartość ma jakąś część dziesiętną) patrz ... stackoverflow.com/questions/1535021/
Jan
6

Z Hmisc::spss.get :

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

znacznie bezpieczniejsza opcja, IMHO, ponieważ „omija” problem precyzji maszyny. Jeśli spróbujesz is.integer(floor(1)), dostaniesz FALSE. BTW, twoja liczba całkowita nie zostanie zapisana jako liczba całkowita, jeśli jest większa niż .Machine$integer.maxwartość, czyli domyślnie 2147483647, więc albo zmień integer.maxwartość, albo wykonaj alternatywne sprawdzenia ...

aL3xa
źródło
1
jeśli x <- sqrt(2)^2, to all(floor(x) == x, na.rm = TRUE)wróćFALSE
Corrado
3

możesz użyć prostego warunku if, takiego jak:

if(round(var) != var)­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Meru Patil
źródło
1

W R to, czy liczba jest liczbą, czy liczbą całkowitą, można określić za pomocą funkcji klasy. Ogólnie wszystkie liczby są przechowywane jako liczby i aby jawnie zdefiniować liczbę jako liczbę całkowitą, musimy określić „L” po liczbie.

Przykład:

x <- 1

klasa (x)

[1] „numeryczny”

x <- 1L

klasa (x)

[1] „liczba całkowita”

Mam nadzieję, że to było potrzebne. Dzięki :)

Meenansha Sachdeva
źródło
0

[AKTUALIZACJA] =============================================== ===============

Szanując [STARY] odpowiedź poniżej, odkryłem, że zadziałała, ponieważ umieściłem wszystkie liczby w jednym wektorze atomowym; jeden z nich był postacią, więc każdy stał się postacią.

Jeśli użyjemy listy (stąd przymus nie zachodzi), wszystkie testy przebiegną poprawnie, z wyjątkiem jednego 1/(1 - 0.98):, który pozostaje numeric. Dzieje się tak, ponieważ tolparametr jest domyślny, 100 * .Machine$double.epsa liczba ta jest daleka od 50podwojenia tej liczby. Tak więc, w zasadzie, do tego rodzaju liczb, możemy mieć do decydowania naszą tolerancję!

Więc jeśli chcesz, aby wszystkie testy stały się możliwe TRUE, możeszassertive::is_whole_number(x, tol = 200 * .Machine$double.eps)

W każdym razie potwierdzam, że asertywność IMO pozostaje najlepszym rozwiązaniem.

Poniżej reprex dla tej [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

Utworzony 2019-07-23 przez pakiet reprex (v0.3.0)

[STARY] =============================================== ==================

IMO najlepsze rozwiązanie pochodzi z assertivepakietu (który na razie rozwiązuje wszystkie pozytywne i negatywne przykłady w tym wątku):

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

Utworzony 2019-07-23 przez pakiet reprex (v0.3.0)

Corrado
źródło
0

Jeśli wolisz nie pisać własnej funkcji, spróbuj check.integerz instalatora pakietu . Obecnie używa odpowiedzi VitoshKa.

Spróbuj również check.numeric(v, only.integer=TRUE)z pakietu varhandle , który ma tę zaletę, że jest wektoryzowany.

qwr
źródło
0

Raz można też użyć dplyr::near:

library(dplyr)

near(a, as.integer(a))

Ma zastosowanie do dowolnego wektora ai ma opcjonalny parametr tolerancji.

James Hirschorn
źródło
-3

Nie jestem pewien, co próbujesz osiągnąć. Ale oto kilka myśli:
1. Zamień na liczbę całkowitą:
num = as.integer(123.2342)
2. Sprawdź, czy zmienna jest liczbą całkowitą:
is.integer(num)
typeof(num)=="integer"

bernardw
źródło
Po prostu upewniam się, że użytkownicy wprowadzają odpowiednią liczbę - mówimy o liczbie „tematów”, która może być tylko liczbą całkowitą.
Roman Luštrik