Как добавить ячейку в pd.DataFrame, но сохранить тип (np.uint64) значения?

#python-3.x #pandas #dataframe #numpy #types

Вопрос:

когда у меня есть фрейм данных со значениями np.uint64

 import pandas as pd
import numpy as np

df = pd.DataFrame(columns=['a','b'])
df.a = [1, 3]
df.b = [1, 3]
df = df.astype({'a': np.uint64, 'b': np.uint64})
print(df.info())
 

он возвращается

 <class 'pandas.core.frame.DataFrame'>
RangeIndex: 2 entries, 0 to 1
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   a       2 non-null      uint64
 1   b       2 non-null      uint64
dtypes: uint64(2)
memory usage: 160.0 bytes
None
 

Когда я сейчас пытаюсь добавить ячейку в фрейм данных:

 x = np.uint64(5)
print(type(x))
 

ВОЗВРАТ

 <class 'numpy.uint64'>
 

и

 df.at[2, 'a'] = x
print(type(df.at[2, 'a']))
print(df.dtypes)
 

ВОЗВРАТ

 <class 'numpy.float64'>
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3 entries, 0 to 2
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   a       3 non-null      float64
 1   b       2 non-null      float64
dtypes: float64(2)
memory usage: 180.0 bytes
None
 

Почему тип столбцов и значений меняется с np.uint64 на float64?

Ответ №1:

Я верю, что это потому, что, когда вы это делаете:

 x = np.uint64(5)
df.at[2, 'a'] = x
 

Он генерирует NaN для столбца B, а pandas не позволяет NaN быть целыми числами, что и делает uint(64).

Если вы добавите строку, в которой нет нулей, и преобразуете фрейм данных обратно в unit64, это позволит:

 df = df.astype({'a': np.uint64, 'b': np.uint64})
 

Если это ответ на ваш вопрос, пожалуйста, отметьте это как решение!

Ответ №2:

Обратите внимание, что numpy uint64 не может быть нулевым, поэтому он автоматически преобразуется в float64 значение после nan появления.

Однако у панд есть целочисленный тип с dtype=pd.Int64Dtype() возможностью обнуления (или псевдоним как dtype='Int64' ):

 df = pd.DataFrame({'a': [1, 3], 'b': [1, 3]}, dtype='Int64')

#    a  b
# 0  1  1
# 1  3  3
 
 df.dtypes

# a    Int64
# b    Int64
# dtype: object
 
 df.loc[2, 'a'] = np.uint64(5)

#    a     b
# 0  1     1
# 1  3     3
# 2  5  <NA>
 
 df.dtypes

# a    Int64
# b    Int64
# dtype: object
 

Комментарии:

1. Спасибо вам за это объяснение! Что заставляет меня задуматься: почему весь dtype фрейма данных изменен на float64? Делает df.at[2, ‘a’] = x сгенерировать/добавить новую строку со значениями NaN перед копированием x в ячейку?

2. @rafa Да, он добавляет новую серию строк, что вызывает цепную реакцию, поскольку серии не могут иметь смешанные типы dtypes (если вы не превратите их в dtype='object' ). Он не может добавить новую строку как [int,float] , поэтому он передает строку в [float,float] . Затем в результате float в каждый столбец теперь добавляется a, поэтому каждый столбец float также обновляется.

3. Спасибо за объяснение!

Ответ №3:

Обновление и следующий вопрос:

Мое решение для решения проблемы:

Вместо того, чтобы использовать np.uint64 значения, в которые я их предварительно привел np.int64 .

 df = pd.DataFrame(columns=['a','b'])
df.a = [1, 3]
df.b = [1, 3]
df = df.astype({'a': np.int64, 'b': np.int64})
print(df.dtypes)
# a    int64
# b    int64
# dtype: object
 

В случае одного я снова добавляю одно значение:

 x = np.int64(5)
print(type(x))  # <class 'numpy.int64'>
df.at[2, 'a'] = x
print(type(df.at[2, 'a']))  # <class 'numpy.float64'>
print(df.dtypes)
# a    float64
# b    float64
# dtype: object
 

Опять же, оба столбца приводятся к плавающему значению, потому NaN что не допускают целых чисел. Верно?

Но в случае второго, когда я вместо этого делаю следующее:

 df = pd.DataFrame(columns=['a','b'])
df.a = [1, 3]
df.b = [1, 3]
df = df.astype({'a': np.int64, 'b': np.int64})
print(df.dtypes)
# a    int64
# b    int64
# dtype: object

df_row = pd.DataFrame(columns=['a','b'])
df_row.a = [5]
df_row.b = [5]
print(df_row.dtypes)
# a    int64
# b    int64
# dtype: object

df = df.append(df_row, ignore_index=True)
print(df.dtypes)
# a    int64
# b    int64
# dtype: object
 

это означает, что при добавлении в этой ситуации фрейм данных не изменяет тип данных.

Но опять же, если сделать то же самое с np.uint64 :

 df = pd.DataFrame(columns=['a','b'])
df.a = [1, 3]
df.b = [1, 3]
df = df.astype({'a': np.uint64, 'b': np.uint64})
print(df.dtypes)
# a    uint64
# b    uint64
# dtype: object

df_row = pd.DataFrame(columns=['a','b'])
df_row.a = [np.uint64(5)]
df_row.b = [np.uint64(5)]
print(df_row.dtypes)
# a    int64
# b    int64
# dtype: object

df = df.append(df_row, ignore_index=True)
print(df.dtypes)
# a    float64
# b    float64
# dtype: object
 

Типы столбцов df_row объясняет, что позже в append типах будут изменены на float64

Но почему тип данных в df_row.a = [np.uint64(5)] изменен на int64 ?