Estou trabalhando com linhas individuais de quadros de dados de pandas, mas tropeço em questões de coerção ao indexar e inserir linhas. Pandas parece sempre querer coagir de um tipo misto de int / float para all-float, e não consigo ver nenhum controle óbvio sobre esse comportamento.
Por exemplo, aqui está um quadro de dados simples com a
as int
e b
como float
:
import pandas as pd
pd.__version__ # '0.25.2'
df = pd.DataFrame({'a': [1], 'b': [2.2]})
print(df)
# a b
# 0 1 2.2
print(df.dtypes)
# a int64
# b float64
# dtype: object
Aqui está um problema de coerção ao indexar uma linha:
print(df.loc[0])
# a 1.0
# b 2.2
# Name: 0, dtype: float64
print(dict(df.loc[0]))
# {'a': 1.0, 'b': 2.2}
E aqui está um problema de coerção ao inserir uma linha:
df.loc[1] = {'a': 5, 'b': 4.4}
print(df)
# a b
# 0 1.0 2.2
# 1 5.0 4.4
print(df.dtypes)
# a float64
# b float64
# dtype: object
Nos dois casos, quero que a a
coluna permaneça como um tipo inteiro, em vez de ser coagida a um tipo de flutuação.
df.loc[[0], df.columns]
.read_[type]
embora suporte a vários tipos ...Respostas:
Após algumas pesquisas, aqui estão algumas soluções terrivelmente feias. (Uma resposta melhor será aceita.)
Uma peculiaridade encontrada aqui é que as colunas não numéricas interrompem a coerção, e aqui está como indexar uma linha para
dict
:E a inserção de uma linha pode ser feita criando um novo quadro de dados com uma linha:
Ambos os truques não são otimizados para grandes quadros de dados, então eu apreciaria muito uma resposta melhor!
fonte
df['a'] = df.a.astype(mytype)
... Ainda está sujo e provavelmente não é eficiente..astype()
é perigoso para float -> inteiro; não há nenhum problema1.1
para mudar para1
, então você realmente precisa ter certeza de que todos os seus valores são semelhantes a números inteiros antes de fazê-lo. Provavelmente melhor para usarpd.to_numeric
comdowncast='integer'
A raiz do problema é que
Nós podemos ver isso:
E uma série pode ter apenas um tipo, no seu caso, int64 ou float64.
Há duas soluções alternativas que vêm à minha cabeça:
ou
https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py#L6973
Portanto, sua solução é realmente sólida, caso contrário, poderíamos:
fonte
object
tipos de dados! Outra é criar um objeto DataFrame desde o início:df = pd.DataFrame({'a': [1], 'b': [2.2]}, dtype=object)
Sempre que você estiver obtendo dados do dataframe ou anexando dados a um dataframe e precisar manter o mesmo tipo de dados, evite a conversão para outras estruturas internas que não estão cientes dos tipos de dados necessários.
Quando você faz
df.loc[0]
a conversão parapd.Series
,E agora,
Series
só terá um singledtype
. Coagindo assimint
afloat
.Em vez disso, mantenha a estrutura como
pd.DataFrame
,Selecione a linha necessária como um quadro e depois converta para
dict
Da mesma forma, para adicionar uma nova linha, use a
pd.DataFrame.append
função pandas ,O acima não causará conversão de tipo,
fonte
Uma abordagem diferente com pequenas manipulações de dados:
Suponha que você tenha uma lista de dicionários (ou quadros de dados)
lod=[{'a': [1], 'b': [2.2]}, {'a': [5], 'b': [4.4]}]
onde cada dicionário representa uma linha (observe as listas no segundo dicionário). Em seguida, você pode criar um quadro de dados facilmente através de:
e você mantém os tipos das colunas. Veja concat
Então, se você tem um quadro de dados e uma lista de dictos, você pode simplesmente usar
fonte
No primeiro caso, você pode trabalhar com o tipo de dados inteiro nulo . A seleção de séries não é coerente
float
e os valores são colocados em umobject
contêiner. O dicionário é criado corretamente, com o valor subjacente armazenado como anp.int64
.Com sua sintaxe, isso quase funciona também para o segundo caso, mas é compatível com
object
, então não é ótimo:No entanto, podemos fazer uma pequena alteração na sintaxe para adicionar uma linha no final (com um RangeIndex) e agora os tipos são tratados adequadamente.
fonte