Изменение значений fill_values ​​в SparseDataFrame - замена throws TypeError

Текущая версия pandas: 0.22


У меня есть SparseDataFrame.

A = pd.SparseDataFrame(
    [['a',0,0,'b'],
     [0,0,0,'c'],
     [0,0,0,0],
     [0,0,0,'a']])

A

   0  1  2  3
0  a  0  0  b
1  0  0  0  c
2  0  0  0  0
3  0  0  0  a

В настоящее время значения заполнения 0. Однако я хотел бы изменить fill_values ​​на np.nan. Мой первый инстинкт состоял в том, чтобы называть replace:

A.replace(0, np.nan)

Но это дает

TypeError: cannot convert int to an sparseblock

Что действительно не помогает мне понять, что я делаю неправильно.

Я знаю, что могу сделать

A.to_dense().replace(0, np.nan).to_sparse()

Но есть ли лучший способ? Или мое фундаментальное понимание ошибочных разреженных фреймов?

Ответ 1

tl; dr: Это определенно ошибка.
Но, пожалуйста, продолжайте читать, есть нечто большее...

Все ниже работает отлично с pandas 0.20.3, но не с какой-либо новой версией:

A.replace(0,np.nan)
A.replace({0:np.nan})
A.replace([0],[np.nan])

и т.д. (вы получаете идею).

(отныне весь код выполняется с помощью pandas 0.20.3).

Однако, те (вместе с большинством обходных решений, которые я пробовал) работают, потому что мы случайно сделали что-то неправильно. Вы угадаете это сразу, если мы это сделаем:

A.density

1.0

Этот SparseDataFrame на самом деле плотный!
Мы можем исправить это, пройдя default_fill_value=0:

A = pd.SparseDataFrame(
     [['a',0,0,'b'],
     [0,0,0,'c'],
     [0,0,0,0],
     [0,0,0,'a']],default_fill_value=0)

Теперь A.density выведет 0.25, как ожидалось.

Это произошло потому, что инициализатор не мог вывести dtypes столбцов. Цитирование из pandas docs:

Редкие данные должны иметь тот же тип dtype, что и его плотное представление. В настоящее время поддерживаются типы float64, int64 и bool. В зависимости от исходного типа dtype значение fill_value по умолчанию изменяется:

  • float64: np.nan
  • int64: 0
  • bool: False

Но dtypes нашего SparseDataFrame:

A.dtypes

0    object
1    object
2    object
3    object
dtype: object

И почему SparseDataFrame не может решить, какое значение заливки использовать, и, таким образом, использовало значение по умолчанию np.nan.

ОК, так что теперь у нас есть SparseDataFrame. Попробуйте заменить некоторые записи в нем:


A.replace('a','z')
    0   1   2   3
0   z   0   0   b
1   0   0   0   c
2   0   0   0   0
3   0   0   0   z
И странно:

A.replace(0,np.nan)
    0   1   2   3
0   a   0   0   b
1   0   0   0   c
2   0   0   0   0
3   0   0   0   a
И это, как вы можете видеть, неверно!
Из моих собственных экспериментов с различными версиями pandas кажется, что SparseDataFrame.replace() работает только с значениями, отличными от заполнения. Чтобы изменить значение заполнения, у вас есть следующие параметры:
  • Согласно pandas docs, если вы измените типы dtypes, это автоматически изменит значение заполнения. (Это не сработало со мной).
  • Преобразуйте в плотную DataFrame, замените, а затем переведите обратно в SparseDataFrame.
  • Вручную воссоздайте новый SparseDataFrame, например ответ Wen, или передав default_fill_value значение нового значения заполнения.

Пока я экспериментировал с последним вариантом, произошло что-то еще более странное:

B = pd.SparseDataFrame(A,default_fill_value=np.nan)

B.density
0.25

B.default_fill_value
nan

До сих пор так хорошо. Но...:

B
    0   1   2   3
0   a   0   0   b
1   0   0   0   c
2   0   0   0   0
3   0   0   0   a

Это действительно шокировало меня поначалу. Это даже возможно!?
Продолжая, я попытался посмотреть, что происходит в столбцах:

B[0]

0    a
1    0
2    0
3    0
Name: 0, dtype: object
BlockIndex
Block locations: array([0], dtype=int32)
Block lengths: array([1], dtype=int32)

dtype столбца object, но связанный с ним тип BlockIndex int32, следовательно, странное поведение.
Происходит гораздо больше "странных" вещей, но я остановлюсь здесь.
Из всего вышеизложенного могу сказать, что вам следует избегать использования SparseDataFrame до полной перезаписи для него:).

Ответ 2

Это то, что я пробовал

pd.SparseDataFrame(np.where(A==0, np.nan, A))

     0    1    2    3
0    a  NaN  NaN    b
1  NaN  NaN  NaN    c
2  NaN  NaN  NaN  NaN
3  NaN  NaN  NaN    a