Замена nan в цифрах фрейма данных

Я пытаюсь понять, почему замена NaN пробелами уменьшает количество отображаемых цифр.

У меня есть DataFrame:

0    -3.030889       -3.510211       -3.502291       -3.502357       -3.502817
1    -3.460590             NaN       -3.584687             NaN             NaN
2    -2.151932       -2.504276       -2.494087       -2.493053       -2.493741
3    -2.462477             NaN       -2.556205             NaN             NaN
4    -1.712807       -1.906281       -1.902953       -1.902297       -1.902253
5    -1.883432             NaN       -1.932924             NaN             NaN

После использования `df = df.replace (np.nan, '', regex = True) некоторые числа отображаются с 5 десятичными знаками.

0    -3.030889       -3.51021       -3.502291       -3.50236       -3.50282
1    -3.460590                      -3.584687                                                        
2    -2.151932       -2.50428       -2.494087       -2.49305       -2.49374
3    -2.462477                      -2.556205                                                        
4    -1.712807       -1.90628       -1.902953        -1.9023       -1.90225
5    -1.883432                      -1.932924                               

Как я могу контролировать это и сохранять точность представления чисел, как в первом DataFrame?

Это может быть просто репрезентативная проблема. Вам действительно следует проверить два фрейма данных и сравнить их значения (для ячеек без NaN), равны ли они. Просто вычтите старый и новый столбцы и посмотрите, каковы результирующие значения столбцов.

9769953 10.08.2018 16:47

Примечание: df = df.replace(np.nan, '', regex=True) кажется неправильным способом замены NaN. Теперь вы превращаете числа с плавающей запятой в строки; это фактически объяснило бы вашу проблему: ячейки в вашем первом фрейме являются плавающими, ячейки во втором фрейме данных - строками. Замените NaN на какое-нибудь правильное значение с плавающей запятой, например 0, или 1, или np.inf, или что вы сочтете наиболее подходящим.

9769953 10.08.2018 16:50

Это побочный эффект операции replace, вы изменили dtype на object с float64 (для поддержки смешанных типов dtypes), вставив пустые строки. Как только вы это сделаете, вы откроете для себя новый мир боли. И зачем это делать? В чем проблема с NaN

EdChum 10.08.2018 16:50

Почему вы не поддерживаете единообразие типов? Почему бы не заменить NaN на число с плавающей запятой?

Andrew Li 10.08.2018 16:50

Используйте pandas.pydata.org/pandas-docs/stable/generated/…

9769953 10.08.2018 16:51

@ 9769953, который дает тот же результат

EdChum 10.08.2018 16:52

@EdChum Ну, конечно, в сочетании с заполнением правильным поплавком. Но .fillna() понятнее, чем .replace().

9769953 10.08.2018 16:53

@EdChum и 9769953 Я понял проблему. Я попытался обойти и преобразовать DataFrame в строку: df = df.astype (str), а затем df = df.fillna ("") или df = df.replace (np.nan, '', regex = True) . Моя логика говорит, что он должен работать со строками. Есть ли что-нибудь особенное в NaN?

Monica 10.08.2018 17:16
1
8
143
1

Ответы 1

Как следует из комментариев, причина потери точности заключается в том, что когда вы вставляете строку в столбец с плавающей запятой, pandas вынужден преобразовать dtype этого столбца в object. В этом ответе я постараюсь уточнить суть дела. Вот пример:

import pandas as pd
import numpy as np
NaN = np.NaN

rows = [[-3.030889, -3.510211, -3.502291, -3.502357, -3.502817],
        [-3.460590, NaN, -3.584687, NaN, NaN],
        [-2.151932, -2.504276, -2.494087, -2.493053, -2.493741],
        [-2.462477, NaN, -2.556205, NaN, NaN],
        [-1.712807, -1.906281, -1.902953, -1.902297, -1.902253],
        [-1.883432, NaN, -1.932924, NaN, NaN]]

df = pd.DataFrame(rows)
print(df)
print(df.dtypes)
print()

new_df = df.replace(np.nan, '', regex=True)
print(new_df)
print(new_df.dtypes)

Это выводит:

          0         1         2         3         4
0 -3.030889 -3.510211 -3.502291 -3.502357 -3.502817
1 -3.460590       NaN -3.584687       NaN       NaN
2 -2.151932 -2.504276 -2.494087 -2.493053 -2.493741
3 -2.462477       NaN -2.556205       NaN       NaN
4 -1.712807 -1.906281 -1.902953 -1.902297 -1.902253
5 -1.883432       NaN -1.932924       NaN       NaN
0    float64
1    float64
2    float64
3    float64
4    float64
dtype: object

          0        1         2        3        4
0 -3.030889 -3.51021 -3.502291 -3.50236 -3.50282
1 -3.460590          -3.584687
2 -2.151932 -2.50428 -2.494087 -2.49305 -2.49374
3 -2.462477          -2.556205
4 -1.712807 -1.90628 -1.902953  -1.9023 -1.90225
5 -1.883432          -1.932924
0    float64
1     object
2    float64
3     object
4     object
dtype: object

Обратите внимание, что любой столбец, в котором NaN был заменен на '', теперь имеет тип object (столбцы 1, 3 и 4 в приведенном выше примере). Вы не только теряете точность при преобразовании в объект, но также теряете семантику. Ваши данные больше не относятся к типу float64. Поэтому, если вы попытаетесь выполнить какую-либо операцию над столбцом, это будет сложно, потому что все элементы столбца не одного типа.

Если мы перейдем к pdb (вызвав import pdb; pdb.set_trace()) в конце приведенного выше фрагмента, мы легко увидим это:

(Pdb) df[1].apply(lambda x: x**2)
0    12.321581
1          NaN
2     6.271398
3          NaN
4     3.633907
5          NaN
Name: 1, dtype: float64
(Pdb) new_df[1].apply(lambda x: x**2)
*** TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

Вероятно, вы захотите сохранить все как float64. Вопрос в том, чем заменить NaN? И ответ на это: это зависит от обстоятельств. Только вы знаете свои данные и то, что они представляют. Вот пара вариантов (их бесконечно много):

Вы можете просто оставить их как NaN, что может быть подходящим в зависимости от того, что вы делаете.

>>> np.NaN ** 2
nan
>>> np.NaN - 100
nan
>>> np.sqrt(np.NaN)
nan

Операции с плавающей точкой просто ничего не сделают: данные останутся как NaN. Некоторые библиотеки python также прекрасно справляются с NaN прямо из коробки.

Другой вариант - заменить NaN на какое-нибудь другое значение с плавающей запятой. WLOG, предположим, вы пытаетесь вычислить евклидово расстояние между столбцами, и это расстояние представляет собой что-то для вашей модели или представляет некоторую ценность для вашей проблемы.

Вы можете заменить NaN на какое-нибудь значение «далеко». Если ваши данные имеют шкалу [-1, 1] (например, если это синусоидальные данные), тогда хорошей заменой может быть -999. Можно с уверенностью сказать, что -999 отодвинет столбцы с NaN достаточно далеко от других столбцов с. Евклидово расстояние. Так что, если вы хотите «наказать» столбцы с помощью NaN, то вы можете это сделать.

OTOH, возможно, вы хотите, чтобы столбцы с NaN просто «усредняли» относительно. евклидово расстояние (по сути, просто заполните NaN разумным значением в вашем диапазоне). 0 находится прямо в середине [-1, 1], так что это может быть хорошим выбором. Это означает, что NaN на самом деле не будут «наказывать» или «помогать» противнику. Евклидово расстояние. Вы также можете использовать среднее значение (или другую форму интерполяции) для вычисления недостающих значений. Например, если вектор-столбец был [0, 1, NaN, .5, NaN, .7], вы можете заменить его на [0, 1, .75, .5, .6, .7] (линейная интерполяция).

Только вы можете решить, какая замена вам подходит.

Если сомневаетесь, просто пытаться что-нибудь. Может быть трудно предсказать, как данная замена повлияет на результаты в большом трубопроводе. Если в дальнейшем вы не получите ожидаемых результатов, скорректируйте свою стратегию замены соответствующим образом и попробуйте еще раз.

Каждая стратегия замены имеет свои плюсы и минусы и вносит предубеждения в ваши последующие модели / конвейеры: просто знайте, что вы сделали, и у вас есть веская причина, чтобы объяснить странные результаты и объяснить, какие предубеждения вы могли внести.

Вы можете сделать замену float64 следующим образом (расширяя приведенный выше код):

fill_value = 0.0 # Make sure it's a float. Only you can decide what it should be.
float_df = df.fillna(fill_value)
print(float_df)
print(float_df.dtypes)

и этот вывод (соблюдайте точность):

          0         1         2         3         4
0 -3.030889 -3.510211 -3.502291 -3.502357 -3.502817
1 -3.460590  0.000000 -3.584687  0.000000  0.000000
2 -2.151932 -2.504276 -2.494087 -2.493053 -2.493741
3 -2.462477  0.000000 -2.556205  0.000000  0.000000
4 -1.712807 -1.906281 -1.902953 -1.902297 -1.902253
5 -1.883432  0.000000 -1.932924  0.000000  0.000000
0    float64
1    float64
2    float64
3    float64
4    float64
dtype: object

Другие вопросы по теме