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.