Bagaimana cara memeriksa apakah string adalah angka (float)?

1609

Apa cara terbaik untuk memeriksa apakah string dapat direpresentasikan sebagai angka dengan Python?

Fungsi yang saya miliki saat ini adalah:

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

Yang, tidak hanya jelek dan lambat, tampaknya kikuk. Namun saya belum menemukan metode yang lebih baik karena memanggil floatfungsi utama bahkan lebih buruk.

Daniel Goldberg
sumber
61
Apa yang salah dengan apa solusi Anda saat ini? Ini pendek, cepat dan mudah dibaca.
Kolonel Panic
5
Dan Anda tidak hanya harus mengembalikan Benar atau Salah. Anda dapat mengembalikan nilai yang dimodifikasi secara tepat - misalnya Anda dapat menggunakan ini untuk memberikan tanda kutip non-angka.
Thruston
7
Bukankah lebih baik mengembalikan hasil float (s) jika konversi berhasil? Anda masih memiliki pemeriksaan untuk keberhasilan (hasilnya False) dan Anda sebenarnya MEMILIKI konversi, yang kemungkinan besar Anda inginkan.
Jiminion
8
Meskipun pertanyaan ini lebih lama, saya hanya ingin mengatakan bahwa ini adalah cara yang elegan yang didokumentasikan sebagai EAFP . Jadi mungkin solusi terbaik untuk masalah seperti ini.
thiruvenkadam
7
Jangan kembalikan hasil float (s) atau Tidak ada pada gagal. jika Anda menggunakannya seperti x = float('0.00'); if x: use_float(x);sekarang Anda punya bug dalam kode Anda. Nilai-nilai kebenaran adalah alasan mengapa fungsi-fungsi ini memunculkan pengecualian daripada mengembalikannya None. Solusi yang lebih baik adalah dengan menghindari fungsi utilitas dan mengelilingi panggilan untuk mengapung di try catchsaat Anda ingin menggunakannya.
Ovangle

Jawaban:

699

Yang, tidak hanya jelek dan lambat

Saya akan membantah keduanya.

Regex atau metode penguraian string lainnya akan lebih jelek dan lebih lambat.

Saya tidak yakin bahwa banyak hal bisa lebih cepat daripada yang di atas. Ini memanggil fungsi dan kembali. Try / Catch tidak memperkenalkan banyak overhead karena pengecualian yang paling umum ditangkap tanpa pencarian yang luas dari stack frame.

Masalahnya adalah fungsi konversi numerik apa pun memiliki dua jenis hasil

  • Nomor, jika nomor itu valid
  • Kode status (mis., Via errno) atau pengecualian untuk menunjukkan bahwa tidak ada nomor yang valid yang dapat diuraikan.

C (sebagai contoh) meretas ini dengan beberapa cara. Python menjabarkannya dengan jelas dan eksplisit.

Saya pikir kode Anda untuk melakukan ini sempurna.

S.Lott
sumber
21
Saya tidak berpikir bahwa kodenya sempurna (tapi saya pikir itu sangat dekat): itu lebih biasa untuk menempatkan hanya bagian yang "diuji" dalam tryklausa, jadi saya akan memasukkannya ke return Truedalam elseklausa try. Salah satu alasannya adalah bahwa dengan kode dalam pertanyaan, jika saya harus memeriksanya, saya harus memeriksa bahwa pernyataan kedua dalam tryklausa tidak dapat meningkatkan ValueError: begitu saja, ini tidak memerlukan terlalu banyak waktu atau kekuatan otak, tetapi mengapa menggunakan apa pun saat tidak diperlukan?
Eric O Lebigot
4
Jawabannya sepertinya menarik, tetapi membuat saya bertanya-tanya mengapa itu tidak disediakan ... Saya akan menyalin ini dan menggunakannya dalam hal apa pun.
bijak
9
Sangat buruk. Bagaimana jika saya tidak peduli apa nomor adalah hanya saja angka (yang adalah apa yang membawa saya di sini)? Alih-alih 1-line IsNumeric()saya berakhir dengan mencoba / menangkap atau membungkus mencoba / menangkap. Ugh
Dasar
6
Itu tidak disediakan 'di luar kotak' karena if is_number(s): x = float(x) else: // failjumlah baris kode yang sama dengan try: x = float(x) catch TypeError: # fail. Fungsi utilitas ini adalah abstraksi yang sama sekali tidak perlu.
Ovangle
12
Tetapi abstraksi adalah inti dari perpustakaan. Memiliki fungsi 'isNumber' (dalam bahasa apa pun) membantu jumlah yang sangat besar karena Anda dapat membangunnya langsung ke pernyataan if dan memiliki kode yang jauh lebih mudah dibaca dan dipelihara yang mengandalkan blok try-catch. Juga, jika Anda perlu menggunakan kode lebih dari sekali di lebih dari satu kelas / modul, Anda telah menggunakan lebih banyak baris kode daripada fungsi bawaan.
JamEngulfer
1612

Jika Anda mencari parsing (positif, tidak bertanda) bilangan alih-alih mengapung, Anda dapat menggunakan isdigit()fungsi untuk objek string.

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

Metode String - isdigit(): Python2 , Python3

Ada juga sesuatu pada string Unicode, yang saya tidak terlalu kenal dengan Unicode - Desimal / desimal

Zoomulator
sumber
232
Itu negatif pada negatif juga
intrepion
22
Gagal dengan eksponensial juga: '1e3'.isdigit () -> False
ssc
35
Sementara Angka! = Digit, orang-orang yang mencari cara untuk menguji apakah suatu string berisi bilangan bulat mungkin sangat tersandung di pertanyaan ini, dan pendekatan isDigit mungkin sangat cocok untuk aplikasi mereka.
Adam Parkin
8
@AdamParkin: isdigit()dan int()memiliki pendapat yang berbeda tentang apa yang adalah bilangan bulat misalnya, untuk karakter Unicode u'\u00b9': u'¹'.isdigit()adalah Truetetapi int(u'¹')menimbulkan ValueError.
jfs
6
+1: isdigit () mungkin bukan yang dicari OP, tapi itulah yang saya inginkan. Mungkin bukan karena jawaban dan metode ini tidak mencakup semua jenis angka, tetapi masih sangat relevan, bertentangan dengan argumen tentang keakuratannya. Sementara "Angka! = Digit," digit masih merupakan bagian dari angka, terutama angka yang positif, non-negatif, dan menggunakan basis 1-10. Lebih lanjut, metode ini sangat berguna dan singkat untuk kasus-kasus di mana Anda ingin memeriksa apakah string adalah ID numerik atau tidak, yang sering jatuh ke dalam subset angka yang baru saja saya jelaskan.
Justin Johnson
161

TL; DR Solusi terbaik adalahs.replace('.','',1).isdigit()

Saya melakukan beberapa tolok ukur membandingkan berbagai pendekatan

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

import re    
def is_number_regex(s):
    """ Returns True is string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

Jika string bukan angka, blok kecuali-cukup lambat. Tetapi yang lebih penting, metode coba-kecuali adalah satu-satunya pendekatan yang menangani notasi ilmiah dengan benar.

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

Notasi float ".1234" tidak didukung oleh:
- is_number_regex

scientific1 = '1.000000e+50'
scientific2 = '1e50'


print('Scientific notation "1.000000e+50" is not supported by:')
for f in funcs:
    if not f(scientific1):
        print('\t -', f.__name__)




print('Scientific notation "1e50" is not supported by:')
for f in funcs:
    if not f(scientific2):
        print('\t -', f.__name__)

Notasi ilmiah "1,000000e + 50" tidak didukung oleh:
- is_number_regex
- is_number_repl_isdigit
Notasi ilmiah "1e50" tidak didukung oleh:
- is_number_regex
- is_number_repl_isdigit

EDIT: Hasil benchmark

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

di mana fungsi-fungsi berikut diuji

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True is string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True is string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

masukkan deskripsi gambar di sini

Idok
sumber
15
untuk grafik bagus +1. Saya melihat benchmark dan melihat grafik, semua TL; DR menjadi jelas dan intuitif.
jcchuks
Saya setuju dengan @JCChuks: grafik sangat membantu untuk mendapatkan semua TL; DR dengan cepat. Tapi saya pikir TL; DR (seperti: TL; DR : solusi terbaik s.replace('.','',1).isdigit()) harus muncul di awal anwser ini. Bagaimanapun, itu harus yang diterima. Terima kasih!
Simon C.
10
Metode ini tidak menangani angka negatif (tanda hubung). Saya akan menganjurkan untuk hanya menggunakan metode float karena kurang rentan terhadap kesalahan dan akan bekerja setiap saat.
Urchin
3
Yang penting untuk dicatat, adalah bahwa bahkan dengan asumsi tidak ada tanda hubung, metode ganti-isdigit hanya lebih cepat untuk bukan angka (hasil salah), sedangkan metode coba-kecuali lebih cepat untuk angka (hasil sebenarnya). Jika sebagian besar input Anda adalah input yang valid, Anda lebih baik dengan solusi coba-kecuali!
Markus von Broady
1
Tidak berfungsi pada notasi eksponensial suka '1.5e-9'atau negatif.
EL_DON
68

Ada satu pengecualian yang mungkin ingin Anda perhitungkan: string 'NaN'

Jika Anda ingin is_number mengembalikan FALSE untuk 'NaN' kode ini tidak akan berfungsi karena Python mengonversinya menjadi representasi nomor yang bukan angka (bicara tentang masalah identitas):

>>> float('NaN')
nan

Kalau tidak, saya harus benar-benar berterima kasih atas potongan kode yang sekarang saya gunakan secara luas. :)

G.

gvrocha
sumber
2
Sebenarnya, NaNmungkin nilai yang baik untuk dikembalikan (bukan False) jika teks yang dikirimkan sebenarnya bukan representasi dari angka. Memeriksanya adalah jenis rasa sakit ( floattipe Python benar-benar membutuhkan metode untuk itu) tetapi Anda dapat menggunakannya dalam perhitungan tanpa menghasilkan kesalahan, dan hanya perlu memeriksa hasilnya.
kindall
7
Pengecualian lain adalah string 'inf'. Entah infatau NaNbisa juga diawali dengan +atau -masih diterima.
agf
4
Jika Anda ingin mengembalikan False untuk NaN dan Inf, ubah baris ke x = float (s); return (x == x) dan (x - 1! = x). Ini harus mengembalikan True untuk semua pelampung kecuali Inf dan NaN
RyanN
5
x-1 == xbenar untuk mengapung besar lebih kecil dari inf. Dari Python 3.2 Anda dapat menggunakan math.isfiniteuntuk menguji angka yang bukan NaN atau tidak terbatas, atau periksa keduanya math.isnandan math.isinfsebelum itu.
Steve Jessop
56

bagaimana dengan ini:

'3.14'.replace('.','',1).isdigit()

yang akan mengembalikan true hanya jika ada satu atau tidak ada '.' dalam deretan digit.

'3.14.5'.replace('.','',1).isdigit()

akan kembali salah

sunting: hanya melihat komentar lain ... menambahkan .replace(badstuff,'',maxnum_badstuff)untuk kasus lain dapat dilakukan. jika Anda mengeluarkan garam dan bukan bumbu sembarang (ref: xkcd # 974 ) ini akan baik-baik saja: P

haxwithaxe
sumber
7
Namun ini tidak memperhitungkan angka negatif.
Michael Barton
5
Atau angka dengan eksponen seperti 1.234e56(yang mungkin juga ditulis sebagai +1.234E+56dan beberapa varian lainnya).
Alfe
re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')harus melakukan pekerjaan yang lebih baik dalam menentukan nomor (tetapi tidak semua, saya tidak mengklaim itu). Saya tidak menyarankan menggunakan ini, lebih baik menggunakan kode asli si Penanya.
Baldrickk
jika Anda tidak menyukai solusi ini, baca ini sebelum downvoting!
aloisdg pindah ke codidact.com
Orang ini adalah solusi paling cerdas yang pernah saya lihat di situs web ini!
Karam Qusai
41

Yang, tidak hanya jelek dan lambat, tampaknya kikuk.

Mungkin perlu beberapa waktu untuk membiasakan diri, tetapi ini adalah cara pythonic untuk melakukannya. Seperti yang telah ditunjukkan, alternatifnya lebih buruk. Tetapi ada satu keuntungan lain dari melakukan hal-hal seperti ini: polimorfisme.

Gagasan utama di balik mengetik bebek adalah bahwa "jika berjalan dan berbicara seperti bebek, maka itu bebek." Bagaimana jika Anda memutuskan bahwa Anda perlu subkelas string sehingga Anda dapat mengubah cara Anda menentukan apakah sesuatu dapat dikonversi menjadi float? Atau bagaimana jika Anda memutuskan untuk menguji beberapa objek lain seluruhnya? Anda dapat melakukan hal-hal ini tanpa harus mengubah kode di atas.

Bahasa lain menyelesaikan masalah ini dengan menggunakan antarmuka. Saya akan menyimpan analisis solusi mana yang lebih baik untuk utas lainnya. Intinya, bagaimanapun, adalah bahwa python sudah pasti di sisi mengetik bebek persamaan, dan Anda mungkin harus membiasakan diri dengan sintaksis seperti ini jika Anda berencana untuk melakukan banyak pemrograman dalam Python (tapi itu tidak berarti Anda harus menyukainya tentu saja).

Satu hal lagi yang perlu Anda pertimbangkan: Python cukup cepat dalam melempar dan menangkap pengecualian dibandingkan dengan banyak bahasa lain (30x lebih cepat dari .Net misalnya). Heck, bahasa itu sendiri bahkan melempar pengecualian untuk mengkomunikasikan kondisi program normal yang tidak biasa (setiap kali Anda menggunakan for for loop). Jadi, saya tidak akan terlalu khawatir tentang aspek kinerja kode ini sampai Anda melihat masalah yang signifikan.

Jason Baker
sumber
1
Tempat umum lain di mana Python menggunakan pengecualian untuk fungsi dasar adalah di hasattr()mana hanya sebuah getattr()panggilan yang dibungkus dengan a try/except. Namun, penanganan pengecualian lebih lambat daripada kontrol aliran normal, sehingga menggunakannya untuk sesuatu yang benar sebagian besar waktu dapat menghasilkan penalti kinerja.
kindall
Tampaknya jika Anda menginginkan one-liner, Anda SOL
Basic
Juga pythonic adalah gagasan bahwa "lebih baik untuk meminta maaf daripada izin", mengenai dampak dari memiliki pengecualian murah.
heltonbiker
40

Diperbarui setelah Alfe menunjukkan Anda tidak perlu memeriksa float secara terpisah karena kompleks menangani keduanya:

def is_number(s):
    try:
        complex(s) # for int, long, float and complex
    except ValueError:
        return False

    return True

Dikatakan sebelumnya: Apakah beberapa kasus langka Anda mungkin perlu memeriksa bilangan kompleks (misalnya 1 + 2i), yang tidak dapat diwakili oleh pelampung:

def is_number(s):
    try:
        float(s) # for int, long and float
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False

    return True
Matthew Wilcoxson
sumber
14
Saya tidak setuju. Itu SANGAT tidak mungkin dalam penggunaan normal, dan Anda akan lebih baik membangun panggilan is_complex_number () ketika Anda menggunakannya, daripada membebani panggilan dengan operasi ekstra untuk 0,0001% peluang salah operasi.
Jiminion
3
Anda dapat menghapus float()barang sepenuhnya dan cukup memeriksa complex()panggilan untuk berhasil. Segala sesuatu yang diuraikan float()dapat diuraikan complex().
Alfe
Fungsi ini akan mengembalikan nilai NaNs dan Inf Pandas sebagai nilai numerik.
fixxxer
complex('(01989)')akan kembali (1989+0j). Tetapi float('(01989)')akan gagal. Jadi saya pikir menggunakan complexitu bukan ide yang baik.
plhn
26

Untuk intmenggunakan ini:

>>> "1221323".isdigit()
True

Tetapi untuk floatkita memerlukan beberapa trik ;-). Setiap angka float memiliki satu titik ...

>>> "12.34".isdigit()
False
>>> "12.34".replace('.','',1).isdigit()
True
>>> "12.3.4".replace('.','',1).isdigit()
False

Juga untuk angka negatif tambahkan saja lstrip():

>>> '-12'.lstrip('-')
'12'

Dan sekarang kita mendapatkan cara universal:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False
Sdwdaw
sumber
2
Tidak menangani hal-hal seperti 1.234e56dan sejenisnya. Juga, saya akan tertarik bagaimana Anda mengetahui bahwa 99999999999999999999e99999999999999999999itu bukan angka. Mencoba menguraikannya dengan cepat.
Alfe
Ini berjalan ~ 30% lebih cepat dari solusi yang diterima pada daftar string 50m, dan 150% lebih cepat pada daftar string 5k. 👏
Zev Averbach
15

Just Mimic C #

Di C # ada dua fungsi berbeda yang menangani parsing nilai skalar:

  • Float.Parse ()
  • Float.TryParse ()

float.parse ():

def parse(string):
    try:
        return float(string)
    except Exception:
        throw TypeError

Catatan: Jika Anda bertanya-tanya mengapa saya mengubah pengecualian menjadi TypeError, inilah dokumentasinya .

float.try_parse ():

def try_parse(string, fail=None):
    try:
        return float(string)
    except Exception:
        return fail;

Catatan: Anda tidak ingin mengembalikan boolean 'False' karena itu masih merupakan tipe nilai. Tidak ada yang lebih baik karena ini menunjukkan kegagalan. Tentu saja, jika Anda menginginkan sesuatu yang berbeda, Anda dapat mengubah parameter gagal menjadi apa pun yang Anda inginkan.

Untuk memperpanjang float untuk memasukkan 'parse ()' dan 'try_parse ()' Anda harus memonetipe kelas 'float' untuk menambahkan metode ini.

Jika Anda ingin menghormati fungsi yang sudah ada sebelumnya, kode harus berupa:

def monkey_patch():
    if(!hasattr(float, 'parse')):
        float.parse = parse
    if(!hasattr(float, 'try_parse')):
        float.try_parse = try_parse

SideNote: Saya pribadi lebih suka menyebutnya Monkey Punching karena rasanya saya menyalahgunakan bahasa ketika saya melakukan ini tetapi YMMV.

Pemakaian:

float.parse('giggity') // throws TypeException
float.parse('54.3') // returns the scalar value 54.3
float.tryParse('twank') // returns None
float.tryParse('32.2') // returns the scalar value 32.2

Dan Sage Pythonas yang agung berkata kepada Takhta Suci, "Apa pun yang dapat Anda lakukan, saya bisa berbuat lebih baik; saya bisa melakukan sesuatu yang lebih baik daripada Anda."

Evan Plaice
sumber
Saya telah mengkode sebagian besar JS belakangan ini dan tidak benar-benar menguji ini sehingga mungkin ada beberapa kesalahan kecil. Jika Anda melihat ada, jangan ragu untuk memperbaiki kesalahan saya.
Evan Plaice
Untuk menambahkan dukungan untuk bilangan kompleks, lihat jawabannya oleh @Matthew Wilcoxson. stackoverflow.com/a/3335060/290340 .
Evan Plaice
1
Menggunakan !bukannya notmungkin kesalahan kecil, tetapi Anda pasti tidak dapat menetapkan atribut ke built-in floatdi CPython.
BlackJack
15

Untuk string bukan angka, try: except:sebenarnya lebih lambat dari ekspresi reguler. Untuk string angka yang valid, regex lebih lambat. Jadi, metode yang tepat tergantung pada input Anda.

Jika Anda menemukan bahwa Anda berada dalam ikatan kinerja, Anda dapat menggunakan modul pihak ketiga baru yang disebut fastnumber yang menyediakan fungsi yang disebut isfloat . Pengungkapan penuh, akulah penulisnya. Saya telah memasukkan hasilnya dalam pengaturan waktu di bawah ini.


from __future__ import print_function
import timeit

prep_base = '''\
x = 'invalid'
y = '5402'
z = '4.754e3'
'''

prep_try_method = '''\
def is_number_try(val):
    try:
        float(val)
        return True
    except ValueError:
        return False

'''

prep_re_method = '''\
import re
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def is_number_re(val):
    return bool(float_match(val))

'''

fn_method = '''\
from fastnumbers import isfloat

'''

print('Try with non-number strings', timeit.timeit('is_number_try(x)',
    prep_base + prep_try_method), 'seconds')
print('Try with integer strings', timeit.timeit('is_number_try(y)',
    prep_base + prep_try_method), 'seconds')
print('Try with float strings', timeit.timeit('is_number_try(z)',
    prep_base + prep_try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('is_number_re(x)',
    prep_base + prep_re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('is_number_re(y)',
    prep_base + prep_re_method), 'seconds')
print('Regex with float strings', timeit.timeit('is_number_re(z)',
    prep_base + prep_re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('isfloat(y)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with float strings', timeit.timeit('isfloat(z)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print()

Try with non-number strings 2.39108395576 seconds
Try with integer strings 0.375686168671 seconds
Try with float strings 0.369210958481 seconds

Regex with non-number strings 0.748660802841 seconds
Regex with integer strings 1.02021503448 seconds
Regex with float strings 1.08564686775 seconds

fastnumbers with non-number strings 0.174362897873 seconds
fastnumbers with integer strings 0.179651021957 seconds
fastnumbers with float strings 0.20222902298 seconds

Seperti yang Anda lihat

  • try: except: cepat untuk input numerik tetapi sangat lambat untuk input yang tidak valid
  • regex sangat efisien ketika input tidak valid
  • fastnumbers menang dalam kedua kasus
SethMMorton
sumber
Saya berdiri dikoreksi: -} sepertinya tidak melakukan hal ini. Mungkin menggunakan nama seperti prep_code_basisdan prep_code_re_methodakan mencegah kesalahan saya.
Alfe
Apakah Anda keberatan menjelaskan cara kerja modul Anda, setidaknya untuk isfloatfungsinya?
Solomon Ucko
@SolomonUcko Ini adalah tautan ke kode sumber untuk bagian pengecekan string: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/… . Pada dasarnya, ia berjalan melintasi setiap karakter dalam string secara berurutan dan memvalidasi bahwa ia mengikuti pola float yang valid. Jika input sudah berupa angka, itu hanya menggunakan PyFloat_Check cepat .
SethMMorton
1
Diuji terhadap alternatif terbaik di thread ini saya mengkonfirmasi solusi ini adalah jauh tercepat. Metode tercepat kedua adalahstr(s).strip('-').replace('.','',1).isdigit() adalah sekitar 10x lebih lambat!
Alexander McFarlane
14

Saya tahu ini sudah sangat lama, tetapi saya akan menambahkan jawaban. Saya yakin ini mencakup informasi yang hilang dari jawaban pilihan tertinggi yang bisa sangat berharga bagi siapa pun yang menemukan ini:

Untuk masing-masing metode berikut sambungkan dengan hitungan jika Anda memerlukan input apa pun untuk diterima. (Dengan asumsi kita menggunakan definisi vokal bilangan bulat daripada 0-255, dll.)

x.isdigit() berfungsi dengan baik untuk memeriksa apakah x adalah bilangan bulat.

x.replace('-','').isdigit() berfungsi dengan baik untuk memeriksa apakah x adalah negatif. (Periksa - di posisi pertama)

x.replace('.','').isdigit() bekerja dengan baik untuk memeriksa apakah x adalah desimal.

x.replace(':','').isdigit() berfungsi dengan baik untuk memeriksa apakah x adalah rasio.

x.replace('/','',1).isdigit() bekerja dengan baik untuk memeriksa apakah x adalah pecahan.

Aruthawolf
sumber
1
Meskipun untuk pecahan, Anda mungkin perlu melakukan x.replace('/','',1).isdigit()atau tanggal seperti 4/7/2017 akan disalahartikan sebagai angka.
Yuxuan Chen
Untuk cara terbaik untuk menghubungkan kondisi: stackoverflow.com/q/3411771/5922329
Daniel Braun
13

Jawaban ini memberikan panduan langkah demi langkah yang berfungsi dengan contoh untuk menemukan string adalah:

  • Bilangan bulat positif
  • Positif / negatif - bilangan bulat / mengambang
  • Bagaimana cara membuang string "NaN" (bukan angka) saat memeriksa nomor?

Periksa apakah string bilangan bulat positif

Anda dapat menggunakan str.isdigit()untuk memeriksa apakah string yang diberikan adalah bilangan bulat positif .

Hasil sampel:

# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False

Periksa string sebagai positif / negatif - integer / float

str.isdigit()kembali Falsejika string adalah angka negatif atau angka float. Sebagai contoh:

# returns `False` for float
>>> '123.3'.isdigit()
False
# returns `False` for negative number
>>> '-123'.isdigit()
False

Jika Anda ingin juga memeriksa bilangan bulat negatif danfloat , maka Anda dapat menulis fungsi khusus untuk memeriksanya sebagai:

def is_number(n):
    try:
        float(n)   # Type-casting the string to `float`.
                   # If string is not a valid `float`, 
                   # it'll raise `ValueError` exception
    except ValueError:
        return False
    return True

Contoh Run:

>>> is_number('123')    # positive integer number
True

>>> is_number('123.4')  # positive float number
True

>>> is_number('-123')   # negative integer number
True

>>> is_number('-123.4') # negative `float` number
True

>>> is_number('abc')    # `False` for "some random" string
False

Buang string "NaN" (bukan angka) sambil memeriksa nomor

Fungsi-fungsi di atas akan kembali Trueuntuk string "NAN" (Bukan angka) karena untuk Python valid float yang mewakili itu bukan angka. Sebagai contoh:

>>> is_number('NaN')
True

Untuk memeriksa apakah nomornya "NaN", Anda dapat menggunakan math.isnan()sebagai:

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

Atau jika Anda tidak ingin mengimpor perpustakaan tambahan untuk memeriksanya, maka Anda cukup memeriksanya dengan membandingkannya dengan menggunakan sendiri ==. Python kembali Falseketika nanfloat dibandingkan dengan dirinya sendiri. Sebagai contoh:

# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False

Oleh karena itu, di atas fungsi is_numberdapat diperbarui kembali Falseuntuk"NaN" sebagai:

def is_number(n):
    is_number = True
    try:
        num = float(n)
        # check for "nan" floats
        is_number = num == num   # or use `math.isnan(num)`
    except ValueError:
        is_number = False
    return is_number

Contoh Run:

>>> is_number('Nan')   # not a number "Nan" string
False

>>> is_number('nan')   # not a number string "nan" with all lower cased
False

>>> is_number('123')   # positive integer
True

>>> is_number('-123')  # negative integer
True

>>> is_number('-1.12') # negative `float`
True

>>> is_number('abc')   # "some random" string
False

PS: Setiap operasi untuk setiap cek tergantung pada jenis nomor dilengkapi dengan overhead tambahan. Pilih versi is_numberfungsi yang sesuai dengan kebutuhan Anda.

Moinuddin Quadri
sumber
12

Casting untuk mengapung dan menangkap ValueError mungkin adalah cara tercepat, karena float () secara khusus dimaksudkan untuk hal itu. Hal lain yang membutuhkan penguraian string (regex, dll) kemungkinan akan lebih lambat karena fakta bahwa itu tidak disetel untuk operasi ini. $ 0,02 saya

codelogic
sumber
11
Dolar "2e-2" Anda juga merupakan float (argumen tambahan untuk menggunakan float :)
tzot
8
@tzot TIDAK PERNAH menggunakan float untuk mewakili nilai moneter.
Luke
6
@ Lukas: Saya sepenuhnya setuju dengan Anda, meskipun saya tidak pernah menyarankan menggunakan pelampung untuk mewakili nilai moneter; Saya baru saja mengatakan bahwa nilai moneter dapat direpresentasikan sebagai mengapung :)
tzot
11

Anda dapat menggunakan string Unicode, mereka memiliki metode untuk melakukan apa yang Anda inginkan:

>>> s = u"345"
>>> s.isnumeric()
True

Atau:

>>> s = "345"
>>> u = unicode(s)
>>> u.isnumeric()
True

http://www.tutorialspoint.com/python/string_isnumeric.htm

http://docs.python.org/2/howto/unicode.html

Blackzafiro
sumber
2
untuk ints non-negatif tidak apa-apa ;-)
andilabs
1
s.isdecimal()memeriksa apakah sstring bilangan bulat non-negatif. s.isnumeric()termasuk karakter yang int()menolak.
jfs
9

Saya ingin melihat metode mana yang tercepat. Secara keseluruhan hasil terbaik dan paling konsisten diberikan oleh check_replacefungsi. Hasil tercepat diberikan oleh check_exceptionfungsi, tetapi hanya jika tidak ada pengecualian yang dipecat - yang berarti kodenya adalah yang paling efisien, tetapi biaya tambahan untuk melempar pengecualian cukup besar.

Harap perhatikan bahwa memeriksa cast yang sukses adalah satu-satunya metode yang akurat, misalnya, ini berfungsi dengan check_exceptiontetapi dua fungsi tes lainnya akan mengembalikan False untuk float yang valid:

huge_number = float('1e+100')

Berikut ini adalah kode benchmark:

import time, re, random, string

ITERATIONS = 10000000

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self
    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

def check_regexp(x):
    return re.compile("^\d*\.?\d*$").match(x) is not None

def check_replace(x):
    return x.replace('.','',1).isdigit()

def check_exception(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

to_check = [check_regexp, check_replace, check_exception]

print('preparing data...')
good_numbers = [
    str(random.random() / random.random()) 
    for x in range(ITERATIONS)]

bad_numbers = ['.' + x for x in good_numbers]

strings = [
    ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
    for x in range(ITERATIONS)]

print('running test...')
for func in to_check:
    with Timer() as t:
        for x in good_numbers:
            res = func(x)
    print('%s with good floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in bad_numbers:
            res = func(x)
    print('%s with bad floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in strings:
            res = func(x)
    print('%s with strings: %s' % (func.__name__, t.interval))

Berikut adalah hasil dengan Python 2.7.10 pada 2017 MacBook Pro 13:

check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169

Berikut adalah hasil dengan Python 3.6.5 pada 2017 MacBook Pro 13:

check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002

Berikut adalah hasil dengan PyPy 2.7.13 pada 2017 MacBook Pro 13:

check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056
Ron Reiter
sumber
10
Anda juga harus menguji kinerja untuk kasus yang tidak valid. Tidak terkecuali dimunculkan dengan angka-angka ini, yang merupakan bagian "lambat".
Ugo Méda
1
@ UgoMéda saya menerima saran Anda dari 2013 dan melakukannya :)
Ron Reiter
"Harap dicatat bahwa memeriksa para pemeran yang sukses adalah satu-satunya metode yang akurat" <- ini sebenarnya tidak benar. Saya telah menjalankan tes Anda menggunakan regexp dalam jawaban saya di atas, dan sebenarnya berjalan lebih cepat daripada regexp. Saya akan menambahkan hasilnya ke jawaban saya di atas.
David Ljung Madison Stellar
Kebetulan, sebagai poin yang lucu, pembuat angka buruk Anda sebenarnya dapat membuat beberapa nomor hukum, meskipun itu akan sangat jarang. :)
David Ljung Madison Stellar
8

Jadi untuk menyatukan semuanya, memeriksa Nan, angka tak terbatas dan bilangan kompleks (tampaknya mereka ditentukan dengan j, bukan i, yaitu 1 + 2j) menghasilkan:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True
a1an
sumber
Sejauh ini jawaban terbaik. Terima kasih
anish
6

Inputnya mungkin sebagai berikut:

a="50" b=50 c=50.1 d="50.1"


Input 1-Umum:

Input dari fungsi ini bisa menjadi segalanya!

Menemukan apakah variabel yang diberikan adalah numerik. String numerik terdiri dari tanda opsional, sejumlah digit, bagian desimal opsional, dan bagian eksponensial opsional. Jadi + 0123.45e6 adalah nilai numerik yang valid. Notasi heksadesimal (mis. 0xf4c3b00c) dan biner (mis. 0b10100111001) tidak diizinkan.

fungsi is_numeric

import ast
import numbers              
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

uji:

>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True

is_floatfungsi

Menemukan apakah variabel yang diberikan adalah float. string float terdiri dari tanda opsional, sejumlah digit, ...

import ast

def is_float(obj):
    if isinstance(obj, float):
        return True
    if isinstance(obj, int):
        return False
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        if not isinstance(nodes[-1].n, float):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

uji:

>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True

apa itu ast ?


2- Jika Anda yakin bahwa konten variabel adalah String :

gunakan metode str.isdigit ()

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

Input 3-Angka:

mendeteksi nilai int:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

mendeteksi float:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True
Bastian
sumber
apa itu " ast"?
4

Saya melakukan tes kecepatan. Mari kita katakan bahwa jika string cenderung menjadi bilangan, cobalah / kecuali strategi adalah yang tercepat yang mungkin. Jika string tidak mungkin menjadi bilangan dan Anda tertarik pada bilangan bulat Integer , layak untuk melakukan beberapa tes (isdigit plus heading '-'). Jika Anda tertarik untuk memeriksa nomor float, Anda harus menggunakan coba / kecuali kode whitout escape.

FxIII
sumber
4

Saya perlu menentukan apakah string dilemparkan ke tipe dasar (float, int, str, bool). Setelah tidak menemukan apa pun di internet saya membuat ini:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

Contoh

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

Anda dapat menangkap tipe dan menggunakannya

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 
astrodsg
sumber
3

RyanN menyarankan

Jika Anda ingin mengembalikan False untuk NaN dan Inf, ubah baris ke x = float (s); return (x == x) dan (x - 1! = x). Ini harus mengembalikan True untuk semua pelampung kecuali Inf dan NaN

Tetapi ini tidak cukup berhasil, karena untuk pelampung yang cukup besar, x-1 == xmengembalikan true. Sebagai contoh,2.0**54 - 1 == 2.0**54

philh
sumber
3

Saya pikir solusi Anda baik-baik saja, tapi ada adalah implementasi regexp yang benar.

Tampaknya ada banyak kebencian regexp terhadap jawaban-jawaban ini yang saya pikir tidak bisa dibenarkan, regexps bisa cukup bersih dan benar dan cepat. Itu benar-benar tergantung pada apa yang Anda coba lakukan. Pertanyaan aslinya adalah bagaimana Anda bisa "memeriksa apakah string dapat direpresentasikan sebagai angka (float)" (sesuai judul Anda). Agaknya Anda ingin menggunakan nilai numerik / float setelah Anda memeriksa apakah itu valid, dalam hal ini coba / kecuali Anda masuk akal. Tetapi jika, karena alasan tertentu, Anda hanya ingin memvalidasi bahwa string adalah a angkakemudian regex juga berfungsi dengan baik, tetapi sulit untuk mendapatkan yang benar. Saya pikir sebagian besar jawaban regex sejauh ini, misalnya, tidak mengurai string dengan benar tanpa bagian integer (seperti ".7") yang merupakan float sejauh menyangkut python. Dan itu agak sulit untuk diperiksa dalam satu regex di mana bagian fraksional tidak diperlukan. Saya sudah menyertakan dua regex untuk menunjukkan ini.

Itu memang memunculkan pertanyaan menarik tentang apa "angka" itu. Apakah Anda memasukkan "inf" yang valid sebagai float in python? Atau apakah Anda memasukkan angka yang merupakan "angka" tetapi mungkin tidak dapat direpresentasikan dengan python (seperti angka yang lebih besar dari float max).

Ada juga ambiguitas dalam cara Anda menguraikan angka. Misalnya, bagaimana dengan "--20"? Apakah ini "angka"? Apakah ini cara hukum untuk mewakili "20"? Python akan membiarkan Anda melakukan "var = --20" dan mengaturnya menjadi 20 (meskipun sebenarnya ini karena ia memperlakukannya sebagai ekspresi), tetapi float ("- 20") tidak berfungsi.

Bagaimanapun, tanpa info lebih lanjut, inilah regex yang saya percayai mencakup semua int dan mengapung seperti python mem-parsing mereka .

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

Beberapa contoh nilai pengujian:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

Menjalankan kode pembandingan dalam jawaban @ ron-reiter menunjukkan bahwa regex ini sebenarnya lebih cepat dari regex normal dan jauh lebih cepat dalam menangani nilai-nilai buruk daripada pengecualian, yang masuk akal. Hasil:

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481
David Ljung Madison Stellar
sumber
Harapan itu benar - akan senang mendengar tentang contoh balasan. :)
David Ljung Madison Stellar
2
import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False
xin.chen
sumber
2
Apakah Anda tidak mempertimbangkan 1e6untuk mewakili angka?
Mark Dickinson
1

Inilah cara sederhana saya untuk melakukannya. Katakanlah saya mengulang-ulang beberapa string dan saya ingin menambahkannya ke sebuah array jika mereka berubah menjadi angka.

try:
    myvar.append( float(string_to_check) )
except:
    continue

Ganti myvar.apppend dengan operasi apa pun yang ingin Anda lakukan dengan string jika ternyata berupa angka. Idenya adalah untuk mencoba menggunakan operasi float () dan menggunakan kesalahan yang dikembalikan untuk menentukan apakah string adalah angka atau tidak.


sumber
Anda harus memindahkan bagian append dari fungsi itu ke pernyataan lain untuk menghindari memicu pengecualian secara tidak sengaja jika ada sesuatu yang salah dengan array.
DarwinSurvivor
1

Saya juga menggunakan fungsi yang Anda sebutkan, tetapi segera saya perhatikan bahwa string sebagai "Nan", "Inf" dan variasi itu dianggap sebagai angka. Jadi saya usulkan versi perbaikan fungsi Anda, yang akan mengembalikan false pada jenis input tersebut dan tidak akan gagal varian "1e3":

def is_float(text):
    try:
        float(text)
        # check for nan/infinity etc.
        if text.isalpha():
            return False
        return True
    except ValueError:
        return False
mathfac
sumber
1

Kode ini menangani eksponen, mengapung, dan bilangan bulat, tanpa menggunakan regex.

return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False
ravi tanwar
sumber
1

Fungsi pembantu pengguna:

def if_ok(fn, string):
  try:
    return fn(string)
  except Exception as e:
    return None

kemudian

if_ok(int, my_str) or if_ok(float, my_str) or if_ok(complex, my_str)
is_number = lambda s: any([if_ok(fn, s) for fn in (int, float, complex)])
Samantha Atkins
sumber
0

Anda dapat menggeneralisasi teknik pengecualian dengan cara yang bermanfaat dengan mengembalikan nilai yang lebih berguna daripada Benar dan Salah. Misalnya fungsi ini menempatkan tanda kutip putaran tetapi meninggalkan angka sendiri. Itulah yang saya butuhkan untuk filter cepat dan kotor untuk membuat beberapa definisi variabel untuk R.

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'
Thruston
sumber
0

Saya sedang mengerjakan masalah yang mengarahkan saya ke utas ini, yaitu bagaimana mengubah kumpulan data menjadi string dan angka dengan cara yang paling intuitif. Saya menyadari setelah membaca kode asli bahwa apa yang saya butuhkan berbeda dalam dua cara:

1 - Saya ingin hasil integer jika string mewakili integer

2 - Saya ingin hasil angka atau string tetap pada struktur data

jadi saya mengadaptasi kode asli untuk menghasilkan turunan ini:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s
pengguna1508746
sumber
0

Coba ini.

 def is_number(var):
    try:
       if var == int(var):
            return True
    except Exception:
        return False
TheRedstoneLemon
sumber
Gagal merespons denganis_number('10')
geote
@geotheory, apa maksudmu "gagal merespons"?
Solomon Ucko
0
def is_float(s):
    if s is None:
        return False

    if len(s) == 0:
        return False

    digits_count = 0
    dots_count = 0
    signs_count = 0

    for c in s:
        if '0' <= c <= '9':
            digits_count += 1
        elif c == '.':
            dots_count += 1
        elif c == '-' or c == '+':
            signs_count += 1
        else:
            return False

    if digits_count == 0:
        return False

    if dots_count > 1:
        return False

    if signs_count > 1:
        return False

    return True
Amir Saniyan
sumber