NumPy або Pandas: зберігає тип масиву як ціле число, маючи значення NaN


160

Чи є кращий спосіб зберегти тип даних numpyмасиву фіксованим як int( int64або будь-який інший), зберігаючи при цьому елемент всередині, вказаний як numpy.NaN?

Зокрема, я перетворюю внутрішню структуру даних у DataFrame Pandas. У нашій структурі є стовпці цілого типу, які все ще мають NaN (але тип стовпця є int). Здається, переробляємо все як плаву, якщо ми зробимо це DataFrame, але нам би хотілося бути таким int.

Думки?

Решта:

Я спробував використовувати from_records()функцію під pandas.DataFrame, з coerce_float=Falseі це не допомогло. Я також спробував використовувати замасковані масиви NumPy, з NaN fill_value, який також не працював. Все це призвело до того, що тип даних стовпців став плаваючим.


Чи можете ви використовувати масивний маскуваний масив?
mgilson

Я спробую. Я також спробував from_recordsфункцію під pandas.DataFrame, з coerce_float=False, але не пощастило ... він все ще робить нові дані мають тип float64.
ely

1
Так, не пощастило. Навіть із маскованим масивом він все ще перетворюється на плаваючий. Схоже, Пандас виглядає так: "Чи є ніде NaN? ... Тоді все пливе". Сподіваємось, існує спосіб цього.
ely

1
Необов'язкова підтримка Nullable Integer тепер офіційно додана на пандах 0.24.0 - нарешті :) - знайдіть оновлену відповідь нижче. pandas 0.24.x note notes
mork

Відповіді:


70

Ця можливість була додана до панд (починаючи з версії 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

На даний момент він вимагає використання dtype-розширення Int64 (з великої літери), а не dtype int64 (малі регістри) за замовчуванням.


1
Наразі вам потрібно вказати спеціальний тип, як, 'Int64'щоб він працював. Ще краще буде, коли це буде включено за замовчуванням.
Жан Пол

Це чудово! Існує невелика проблема, хоча PyCharm не може відобразити кадр даних у вікні налагодження, якщо використовується таким чином. Ви можете побачити мою відповідь на інше питання, як змусити її відображати: stackoverflow.com/questions/38956660/… (оригінальна проблема там інша, але рішення для відображення фрейму даних працює)
Алаа М.

Чи потрібно використовувати 'Int64'чи є щось подібне 'Int8'? Він використовує шалений об'єм пам'яті порівняно з np.float.
Супердопергеро

'Int8'здається, працює, але np.floatвсе ще здається, що швидше завантажується. Проблема, здається, полягає в тому, що він не звільняє пам’ять між ними. Припустимо, збирач сміття з часом працює.
Superdooperhero

103

NaNне може бути збережено у цілому масиві. Це відоме на сьогодні обмеження панд; Я чекаю на досягнення прогресу зі значеннями NA в NumPy (подібні до NA в R), але пройде, щонайменше, 6 місяців до року, перш ніж NumPy отримає ці особливості, здається:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(Ця функція була додана, починаючи з версії 0.24 панд, але зауважте, що вона вимагає використання dtype-розширення Int64 (з великої літери), а не типу dt int64 за замовчуванням (нижній регістр): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # необов'язково-ціле число-на-підтримку )


7
Привіт Уесе, чи є оновлення з цього приводу? Ми стикаємося з проблемами, які об'єднують стовпці, перетворюються або в ints, або floats, виходячи з наявності значення NA у вихідному списку. (Створення проблем пізніше при спробі об’єднання цих фреймів даних)
Карст,

1
Оновлено посилання: pandas-docs.github.io/pandas-docs-travis/whatsnew/…
techvslife

8

Якщо продуктивність не є основною проблемою, ви можете замість цього зберегти рядки.

df.col = df.col.dropna().apply(lambda x: str(int(x)) )

Тоді ви можете змішати потім NaNскільки завгодно. Якщо ви дійсно хочете мати цілі числа, залежно від вашої програми, ви можете використовувати -1або 0, або 1234567890, або якесь інше виділене значення для представленняNaN .

Ви також можете тимчасово дублювати стовпці: один, як у вас є, з плавцями; інший експериментальний, з вставками або рядками. Потім вставляє assertsв кожне розумне місце, перевіряючи, чи вони синхронізовані. Після достатнього тестування ви можете відпустити поплавці.


5

Це не рішення для всіх випадків, але моє (геномні координати) я вдався до використання 0 як NaN

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

Принаймні, це дозволяє використовувати належний тип "рідного" стовпця, такі операції, як віднімання, порівняння тощо, працюють як слід


5

Панди v0.24 +

Функціональність для підтримки NaNцілих рядів буде доступна в версії v0.24. Там же інформація про це в v0.24 «Що нового» розділ, і більш детальну інформацію по Nullable Integer Тип даних .

Панди v0.23 та новіші

Загалом, найкраще працювати з floatсерією , де це можливо, навіть якщо ряд є вентиляційний від intдо floatза рахунок включенняNaN значень. Це дозволяє векторизовані обчислення на основі NumPy, де в іншому випадку обробляться петлі рівня Python.

Документи пропонують : "Одна можливість використовувати dtype=objectзамість них масиви." Наприклад:

s = pd.Series([1, 2, 3, np.nan])

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

З косметичних міркувань, наприклад, вихід у файл, це може бути бути кращим.

Панди v0.23 і новіші: фон

NaNвважається аfloat . Наразі в Документах (станом на v0.23) вказується причина, через яку цілі ряди переходять на float:

За відсутності високопродуктивної підтримки NA, яка вбудовується в NumPy з нуля, першочерговим випадком є ​​можливість представлення NA в цілих масивах.

Цей компроміс зроблений значною мірою з пам’яті та продуктивності, а також таким чином, що отримана серія продовжує залишатися «числовою».

Документи також надають правила для оновлення через NaNвключення:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object


1

Просто хотів би додати, що у випадку, якщо ви намагаєтеся перетворити вектор float (1.143) у ціле число (1), який має NA перетворення у новий тип 'Int64', ви отримаєте помилку. Для того, щоб вирішити це, вам слід округлювати числа, а потім робити ".astype ('Int64')"

s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error 
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0      1
1      2
2    NaN
dtype: Int64

Мій випадок використання полягає в тому, що у мене є плаваюча серія, яку я хочу округлити до int, але коли ви зробите .round () a '* .0' в кінці числа залишається, тож ви можете скинути це 0 з кінця на перетворення в int.


0

Якщо в текстових даних є пробіли, стовпці, які, як правило, є цілими числами, будуть перенесені на плаваючі елементи як float64 dtype, оскільки int64 dtype не може обробляти нулі. Це може спричинити непослідовність схеми, якщо ви завантажуєте декілька файлів, деякі з пробілами (які вийдуть як float64, а інші, без яких закінчуються як int64

Цей код намагатиметься перетворити стовпці будь-якого числа в Int64 (на відміну від int64), оскільки Int64 може обробляти нулі

import pandas as pd
import numpy as np

#show datatypes before transformation
mydf.dtypes

for c in mydf.select_dtypes(np.number).columns:
    try:
        mydf[c] = mydf[c].astype('Int64')
        print('casted {} as Int64'.format(c))
    except:
        print('could not cast {} to Int64'.format(c))

#show datatypes after transformation
mydf.dtypes
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.