Использование Int64: разница между массивом Pandas и Series (версия Pandas 0.24) - PullRequest
0 голосов
/ 02 апреля 2019

Вчера я заметил морщину панд, о которой стоит упомянуть.Метод .astype('Int64') ведет себя очень по-разному с массивами Pandas Series и Pandas.

В Pandas 0.24 можно назначать пропущенные значения целочисленным переменным, не переводя их в числа с плавающей точкой.Это делается с помощью dtype "Int64", который является сокращением для Int64Dtype() в функциях, которые создают серии.

Мне нужна функция, которая получит строку, а затем проведет минимальное продвижение, необходимое для ее преобразования вцелое число или число (если возможно).Если строковыми данными являются ["1", "2", "3", np.nan], это создаст Int64, или, если ["1.1", "2", "3", np.nan], это создаст float64,Это очень похоже на работу, проделанную встроенной Pandas infer_objects, за исключением того, что вместо использования «int64» для данных, которые можно рассматривать как целые числа, мне нужен «Int64».Если ввод не может быть преобразован в float или Int64, тогда я хочу, чтобы строка символов вышла.Если объект в настоящее время является плавающим, я рад, если он изящно изменится на 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, как вы видите здесь:

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 (io.parsers) я заметил, что есть функция с именем _validate_integer(), и она, кажется, выполняет работу по проверке этой проблемы, которую я описал здесь.Это проверка безопасности, прежде чем пытаться привести переменную к целому числу.Эта функция небезопасна для Int64, использующих np.nan, но она движется в том направлении, где мне нужно закончить.

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 голосов
/ 02 апреля 2019

Вот как я пересмотрел функцию, чтобы избежать проблемы при использовании ввода 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].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...