Вчера я заметил морщину панд, о которой стоит упомянуть.Метод .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') безопасным для массивов.