Int64用法:pandas数组和Series(Pandas版本0.24)之间的区别

时间:2019-04-02 16:28:42

标签: pandas numpy

昨天我注意到熊猫的皱纹可能值得一提。 .astype('Int64')方法的行为与Pandas系列和Pandas数组完全不同。

在Pandas 0.24中,可以将缺失值分配给整数变量而无需将其提升为浮点数。这是通过使用dtype“ Int64”完成的,它是创建系列的函数中Int64Dtype()的缩写。

我想要一个函数,该函数将接收字符串,然后进行必要的最小限度的提升以将其转换为整数或数字(如果可能)。如果字符串数据为[“ 1”,“ 2”,“ 3”,np.nan],则将创建Int64;如果字符串数据为[“ 1.1”,“ 2”,“ 3”,np.nan],则将创建float64 。这与内置infer_objects的Pandas所做的工作非常相似,不同的是我需要使用“ Int64”,而不是对可以看作整数的数据使用“ int64”。如果输入不能转换为floatInt64,那么我想输出字符串。如果对象当前是一个浮点数,我很高兴 它可以正常更改为Int64,而不会丢失信息。

这是我的努力:

import pandas as pd
import numpy as np
def string_promote(x):
    """Minimum promotion of string to numeric, preserving missing values".

    Convert x to number if possible, using lowest compatible storage type. Prefers
    integer, using pandas Int64Dtype because that preserves missing values.   
    """
    try:
        y = x.astype('Int64')
    except (TypeError, ValueError):
        try:
            y = x.astype('float')
        except:
            y = x.astype('object')
    return y

如果输入是pandas Series对象,这很好用,如您在此处看到的

In [9]: x1 = pd.Series([1,2,3,4, np.nan, 4.4])

In [10]: string_promote(x1)
Out[10]: 
0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
5    4.4
dtype: float64

In [11]: x2 = pd.Series([1,2,3,4, np.nan, 65])

In [12]: string_promote(x2)
Out[12]: 
0      1
1      2
2      3
3      4
4    NaN
5     65
dtype: Int64


In [15]: x5 = pd.Series([1, 3, 5, 66, 88], dtype='float64')

In [16]: string_promote(x5)
Out[16]: 
0     1
1     3
2     5
3    66
4    88
dtype: Int64

所有这些示例均按预期工作。

但是,我想知道如果有人使用带有此功能的Pandas数组对象,并且输出完全失败了,那会发生什么。也许总灾难更准确浮点数向下取整为int,np.nan变为机器最小值,或类似的值:

In [13]: x3 = pd.array([1, 2, 3, 4.5, np.nan])

In [14]: string_promote(x3)
Out[14]: 
array([                   1,                    2,                    3,
                          4, -9223372036854775808])

在我看来,.astype('Int64')如果不打算用于numpy数组或pandas数组对象,则应该引发异常。

我正在考虑解决此问题的方法。因为我是Pandas的新手,所以我不确定正确的方法。

我知道我不是第一个发现此问题的人。我在Pandas源代码(io.parsers中注意到,有一个名为_validate_integer()的函数,它似乎正在检查我在此描述的问题。在尝试将变量强制为整数之前,它正在检查安全性。对于使用np.nan的Int64来说,该功能并不安全,但它朝着我需要结束的方向发展。

In [25]: x4 = pd.array([1, 2, 3, 4])
In [26]: [pd.io.parsers._validate_integer(name="fred", val = i) for i in x4]
Out[26]: [1, 2, 3, 4]
In [27]: x5 = pd.array([1, 2, 3, 4, 5.1])
In [28]: [pd.io.parsers._validate_integer(name="fred", val = i) for i in x5]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-28-e90d15827cfc> in <module>
----> 1 [pd.io.parsers._validate_integer(name="fred", val = i) for i in x5]

<ipython-input-28-e90d15827cfc> in <listcomp>(.0)
----> 1 [pd.io.parsers._validate_integer(name="fred", val = i) for i in x5]

~/LinuxDownloads/anaconda3/lib/python3.7/site-packages/pandas/io/parsers.py in _validate_integer(name, val, min_val)
    367         if is_float(val):
    368             if int(val) != val:
--> 369                 raise ValueError(msg)
    370             val = int(val)
    371         elif not (is_integer(val) and val >= min_val):

ValueError: 'fred' must be an integer >=0

该异常应该发生。似乎也应该与.astype('Int64')一起发生,但这是另一回事了。

我想知道您对此有什么看法,以及是否有一种方法可以使astype('Int64')对于数组安全。

1 个答案:

答案 0 :(得分:0)

这是修改函数以避免使用pd.array输入而不是pd.Series时出现问题的方法。

def infer_types(x):
    """Minimum promotion of string to numeric, preserving missing values".

    This is a Goldilocks function, looking for value type that is just
    right. It converts string or float to Int64 if possible without
    losing information. Preserves missing values, using Int64, which
    is only possible in pandas 0.24 or later. If that fails, it
    converts to float64 if possible.

    Similar in purpose to Pandas method "infer_objects", except that it
    preserves missing values for integer with Int64.

    :param x: an input vector. Must be a pandas.Series object or something that
    pd.Series() can convert to a Pandas Series.

    Examples
    --------
    # works as intended with pd.Series

    x1 = pd.Series([1,2,3,4.5, np.nan])
    infer_types(x1)

    x2 = pd.Series([1, 2, 3, 4, np.nan])
    infer_types(x2)

    x3 = pd.Series([1, 2, 3, 4, np.nan], dtype = "float64")
    infer_types(x4)

    # Array input also succeeds, will be coerced to pd.Series inside function
    x4 = pd.array([1, 2, 3, 4.5])
    infer_types(x4)
    """
    if not isinstance(x, pd.Series):
        try:
            x = pd.Series(x)
        except:
            msg = "Failed to create Pandas Series from input"
            raise ValueError(msg)
    try:
        y = x.astype('Int64')
    except (TypeError, ValueError):
        try:
            y = x.astype('float')
        except:
            y = x.astype('object')
    return y

我希望将Int64作为类型插入Pandas .24中,将来会导致其他方法的修订。当前的infer_objects()方法不了解Int64。如果确实如此,它将完美解决我偶然发现的问题。

唯一的其他合理策略是,如果输入的内容不是pd.Series,则从一开始就引发异常,但是现在,如果我给其他内容加上一些强制性内容,我就会受到损害。如果输入是pd.array或原始Python列表(例如[1、2、3、4,np.nan]),则当前版本适用。