[![PyPI version](https://badge.fury.io/py/is-numeric.svg)](https://badge.fury.io/py/is-numeric)
[![Python package](https://github.com/jasonray/is-numeric/actions/workflows/python-package.yml/badge.svg)](https://github.com/jasonray/is-numeric/actions/workflows/python-package.yml)
# is-numeric
Really simple. A method to test if a value `is_numeric`.
``` python
from is_numeric import is_numeric
print( is_numeric(1) ) # True
print( is_numeric(-1) ) # True
print( is_numeric(123) ) # True
print( is_numeric(123.456) ) # True
print( is_numeric("123.456") ) # True
print( is_numeric("x") ) # False
print( is_numeric("1x") ) # False
```
# Installation
```
pip install is-numeric
```
# Notes on the algorithm
I tested 4 algorithms:
* str.isinstance
* error-driven
* regex
* regex (plus str.isinstance)
## str.isinstance
Using the built in .isinstance is limited in functionality, as it only looks for all characters being a digit, and fails on '-1' or '1.2'. Thus discard this approach.
```
def _str_isnumeric(value):
if isinstance(value, (int, float)):
return True
if isinstance(value, str) and value.isnumeric():
return True
return False
```
## error-driven
This is ugly in that it relies on errors in flow control. I hate it. It is like nails on chalk board to me.
```
def _is_numeric_using_error(value):
try:
float(value)
return True
except ValueError:
return False
```
## regex
This seems elegant. Check for a pattern.
```
def _is_numeric_regex(value):
if isinstance(value, (int, float)):
return True
if isinstance(value, str):
return bool(re.match(r"^-?\d+(\.\d+)?$", value))
return False
```
## regex (plus str.isinstance)
The regex solution, with a small change to check if all numeric digits prior to doing regex.
```
def _is_numeric_regex_plus_isnumeric(value):
if isinstance(value, (int, float)):
return True
if isinstance(value, str) and value.isnumeric():
return True
if isinstance(value, str):
return bool(re.match(r"^-?\d+(\.\d+)?$", value))
return False
```
## Performance Comparison
I ran a very non-scientific test.
**Approach**
Create a list of inputs, with a mix of float/int, strings that represent numeric values, and non-numeric values.
Iterate x time over the list for a given algorithm.
Repeat for each algorithm.
**Variable**
* % of inputs that are non-numeric (theory is that error-driven will perform better when the value is numeric, and less efficient when the value cannot be cast to float and an error is raised)
**Findings**
When most values are numeric [^1] (0-50%), the error-driven approach outperforms other approaches (up to 3x).
When most values are non-numeric [^2] (80%), the regex (plus str.isinstance) approach has a very slight advantage (4%).
**Conclusion**
without knowning the % of non-numeric values, it is recommended to use the error-driven approach. As such, the error-drive approach is exposed through the `is_numeric` function in this package.
### Raw Data
const: iterations=1000000
| Algorithm | % Non-Numeric | Time in Seconds | Iterations per Second |
|-------------------------------------|---------------|-----------------|-----------------------|
| is_numeric_regex_plus_isnumeric | 0.0 | 4.176071 | 239,459.52 |
| is_numeric_using_error | 0.0 | 1.469144 | 680,668.39 |
| is_numeric_regex | 0.0 | 4.719131 | 211,903.44 |
| is_numeric_regex_plus_isnumeric | 0.2 | 5.893955 | 169,665.37 |
| is_numeric_using_error | 0.2 | 3.295613 | 303,433.65 |
| is_numeric_regex | 0.2 | 6.379531 | 156,751.32 |
| is_numeric_regex_plus_isnumeric | 0.5 | 10.687538 | 93,566.92 |
| is_numeric_using_error | 0.5 | 8.338870 | 119,920.32 |
| is_numeric_regex | 0.5 | 10.848000 | 92,182.89 |
| is_numeric_regex_plus_isnumeric | 0.8 | 29.750488 | 33,612.89 |
| is_numeric_using_error | 0.8 | 28.508906 | 35,076.76 |
| is_numeric_regex | 0.8 | 28.439334 | 35,162.57 |
[^1]: numeric = float/int, strings that represent numeric values
[^2]: non-numeric = strings that cannot be cast to float/int
Raw data
{
"_id": null,
"home_page": "",
"name": "is-numeric",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "isnumeric,is_numeric,number,isnumber,is_number,\"is numeric\",\"is number\",numeric",
"author": "Mighty Pulpo",
"author_email": "jayray.net@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/e0/c2/3805470d4056c8eb4f121ebf7906889b60b96b0c664fe6e739725af0037e/is-numeric-1.0.1.tar.gz",
"platform": null,
"description": "[![PyPI version](https://badge.fury.io/py/is-numeric.svg)](https://badge.fury.io/py/is-numeric)\n[![Python package](https://github.com/jasonray/is-numeric/actions/workflows/python-package.yml/badge.svg)](https://github.com/jasonray/is-numeric/actions/workflows/python-package.yml)\n\n# is-numeric\n\nReally simple. A method to test if a value `is_numeric`.\n\n``` python \nfrom is_numeric import is_numeric\nprint( is_numeric(1) ) # True\nprint( is_numeric(-1) ) # True\nprint( is_numeric(123) ) # True\nprint( is_numeric(123.456) ) # True\nprint( is_numeric(\"123.456\") ) # True\nprint( is_numeric(\"x\") ) # False\nprint( is_numeric(\"1x\") ) # False\n```\n\n# Installation\n\n```\npip install is-numeric\n```\n\n# Notes on the algorithm\n\nI tested 4 algorithms:\n* str.isinstance\n* error-driven\n* regex\n* regex (plus str.isinstance)\n\n## str.isinstance\n\nUsing the built in .isinstance is limited in functionality, as it only looks for all characters being a digit, and fails on '-1' or '1.2'. Thus discard this approach.\n\n```\ndef _str_isnumeric(value):\n if isinstance(value, (int, float)):\n return True\n if isinstance(value, str) and value.isnumeric():\n return True\n return False\n```\n\n## error-driven\n\nThis is ugly in that it relies on errors in flow control. I hate it. It is like nails on chalk board to me.\n\n```\ndef _is_numeric_using_error(value):\n try:\n float(value)\n return True\n except ValueError:\n return False\n```\n\n## regex\n\nThis seems elegant. Check for a pattern.\n\n```\ndef _is_numeric_regex(value):\n if isinstance(value, (int, float)):\n return True\n if isinstance(value, str):\n return bool(re.match(r\"^-?\\d+(\\.\\d+)?$\", value))\n return False\n```\n\n## regex (plus str.isinstance)\n\nThe regex solution, with a small change to check if all numeric digits prior to doing regex.\n\n```\ndef _is_numeric_regex_plus_isnumeric(value):\n if isinstance(value, (int, float)):\n return True\n if isinstance(value, str) and value.isnumeric():\n return True\n if isinstance(value, str):\n return bool(re.match(r\"^-?\\d+(\\.\\d+)?$\", value))\n return False\n```\n\n## Performance Comparison\n\nI ran a very non-scientific test.\n\n**Approach** \nCreate a list of inputs, with a mix of float/int, strings that represent numeric values, and non-numeric values.\nIterate x time over the list for a given algorithm.\nRepeat for each algorithm.\n\n**Variable** \n* % of inputs that are non-numeric (theory is that error-driven will perform better when the value is numeric, and less efficient when the value cannot be cast to float and an error is raised)\n\n**Findings** \nWhen most values are numeric [^1] (0-50%), the error-driven approach outperforms other approaches (up to 3x). \nWhen most values are non-numeric [^2] (80%), the regex (plus str.isinstance) approach has a very slight advantage (4%).\n\n**Conclusion** \nwithout knowning the % of non-numeric values, it is recommended to use the error-driven approach. As such, the error-drive approach is exposed through the `is_numeric` function in this package.\n\n\n### Raw Data \nconst: iterations=1000000\n\n| Algorithm | % Non-Numeric | Time in Seconds | Iterations per Second |\n|-------------------------------------|---------------|-----------------|-----------------------|\n| is_numeric_regex_plus_isnumeric | 0.0 | 4.176071 | 239,459.52 |\n| is_numeric_using_error | 0.0 | 1.469144 | 680,668.39 |\n| is_numeric_regex | 0.0 | 4.719131 | 211,903.44 |\n| is_numeric_regex_plus_isnumeric | 0.2 | 5.893955 | 169,665.37 |\n| is_numeric_using_error | 0.2 | 3.295613 | 303,433.65 |\n| is_numeric_regex | 0.2 | 6.379531 | 156,751.32 |\n| is_numeric_regex_plus_isnumeric | 0.5 | 10.687538 | 93,566.92 |\n| is_numeric_using_error | 0.5 | 8.338870 | 119,920.32 |\n| is_numeric_regex | 0.5 | 10.848000 | 92,182.89 |\n| is_numeric_regex_plus_isnumeric | 0.8 | 29.750488 | 33,612.89 |\n| is_numeric_using_error | 0.8 | 28.508906 | 35,076.76 |\n| is_numeric_regex | 0.8 | 28.439334 | 35,162.57 |\n\n[^1]: numeric = float/int, strings that represent numeric values\n[^2]: non-numeric = strings that cannot be cast to float/int\n",
"bugtrack_url": null,
"license": "",
"summary": "The missing Python method to determine if a value is numeric",
"version": "1.0.1",
"project_urls": null,
"split_keywords": [
"isnumeric",
"is_numeric",
"number",
"isnumber",
"is_number",
"\"is numeric\"",
"\"is number\"",
"numeric"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1553538986934103d039a19de13949e83118ad9cd904b9865c0ef707fddacabb",
"md5": "99544c51c64b8d39c04d78b9a9f78ad7",
"sha256": "75fada43516c536aa18004a1241df3a42b391ed5cddbc5c950b1ea6fca2fb815"
},
"downloads": -1,
"filename": "is_numeric-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "99544c51c64b8d39c04d78b9a9f78ad7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 5148,
"upload_time": "2023-12-13T17:57:01",
"upload_time_iso_8601": "2023-12-13T17:57:01.411106Z",
"url": "https://files.pythonhosted.org/packages/15/53/538986934103d039a19de13949e83118ad9cd904b9865c0ef707fddacabb/is_numeric-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "e0c23805470d4056c8eb4f121ebf7906889b60b96b0c664fe6e739725af0037e",
"md5": "b73f4d18709ed615769a50888844dae9",
"sha256": "bbee639abcc946e49bd57ac10c180f38915007ee7c72942b7dffc181c31fbdde"
},
"downloads": -1,
"filename": "is-numeric-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "b73f4d18709ed615769a50888844dae9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 4634,
"upload_time": "2023-12-13T17:57:03",
"upload_time_iso_8601": "2023-12-13T17:57:03.072956Z",
"url": "https://files.pythonhosted.org/packages/e0/c2/3805470d4056c8eb4f121ebf7906889b60b96b0c664fe6e739725af0037e/is-numeric-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-12-13 17:57:03",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "is-numeric"
}