is-numeric


Nameis-numeric JSON
Version 1.0.1 PyPI version JSON
download
home_page
SummaryThe missing Python method to determine if a value is numeric
upload_time2023-12-13 17:57:03
maintainer
docs_urlNone
authorMighty Pulpo
requires_python>=3.6
license
keywords isnumeric is_numeric number isnumber is_number "is numeric" "is number" numeric
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            [![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"
}
        
Elapsed time: 0.14916s