Converter colunas em string no Pandas


179

Eu tenho o seguinte DataFrame de uma consulta SQL:

(Pdb) pp total_rows
     ColumnID  RespondentCount
0          -1                2
1  3030096843                1
2  3030096845                1

e eu quero rodar assim:

total_data = total_rows.pivot_table(cols=['ColumnID'])

(Pdb) pp total_data
ColumnID         -1            3030096843   3030096845
RespondentCount            2            1            1

[1 rows x 3 columns]


total_rows.pivot_table(cols=['ColumnID']).to_dict('records')[0]

{3030096843: 1, 3030096845: 1, -1: 2}

mas quero garantir que as colunas 303 sejam convertidas como cadeias de caracteres em vez de números inteiros, para que eu entenda isso:

{'3030096843': 1, '3030096845': 1, -1: 2}

No pandas 1.0, a documentação recomenda usar, em astype("string")vez de,astype(str) por boas razões, dar uma olhada.
cs95

Respostas:


333

Uma maneira de converter para string é usar astype :

total_rows['ColumnID'] = total_rows['ColumnID'].astype(str)

No entanto, talvez você esteja procurando a to_jsonfunção, que converterá chaves em json válido (e, portanto, suas chaves em strings):

In [11]: df = pd.DataFrame([['A', 2], ['A', 4], ['B', 6]])

In [12]: df.to_json()
Out[12]: '{"0":{"0":"A","1":"A","2":"B"},"1":{"0":2,"1":4,"2":6}}'

In [13]: df[0].to_json()
Out[13]: '{"0":"A","1":"A","2":"B"}'

Nota: você pode passar um buffer / arquivo para salvá-lo, além de outras opções ...


3
Eu acho que to_string () é preferível devido à preservação de NULLs stackoverflow.com/a/44008334/3647167
Keith

1
@Keith preservação nula é atraente. mas o documento diz que seu objetivo é 'Renderizar um DataFrame em uma saída tabular compatível com o console'. eu gostaria que alguém com autoridade para pesar
3pitt

to_json()provavelmente não chama astype(str), pois deixa datetime64 e suas subclasses em milissegundos desde a época.
Sussch 10/07/19

1
@Sussch Suspeito que o json não tenha um formato explícito de data e hora, então você é meio que forçado a usar a época. Ou seja, acho que esse é o padrão.
Andy Hayden

50

Se você precisar converter TODAS as colunas em cadeias, basta usar:

df = df.astype(str)

Isso é útil se você precisar que tudo, exceto algumas colunas, seja string / objeto, depois volte e converta as outras para o que você precisar (número inteiro neste caso):

 df[["D", "E"]] = df[["D", "E"]].astype(int) 

28

Aqui está o outro, particularmente útil para converter várias colunas em string, em vez de apenas uma coluna:

In [76]: import numpy as np
In [77]: import pandas as pd
In [78]: df = pd.DataFrame({
    ...:     'A': [20, 30.0, np.nan],
    ...:     'B': ["a45a", "a3", "b1"],
    ...:     'C': [10, 5, np.nan]})
    ...: 

In [79]: df.dtypes ## Current datatype
Out[79]: 
A    float64
B     object
C    float64
dtype: object

## Multiple columns string conversion
In [80]: df[["A", "C"]] = df[["A", "C"]].astype(str) 

In [81]: df.dtypes ## Updated datatype after string conversion
Out[81]: 
A    object
B    object
C    object
dtype: object


0

pandas> = 1.0: É hora de parar de usar astype(str)!

Antes do pandas 1.0 (na verdade, 0,25), essa era a maneira mais comum de declarar uma série / coluna como uma string:

# pandas <= 0.25
# Note to pedants: specifying the type is unnecessary since pandas will 
# automagically infer the type as object
s = pd.Series(['a', 'b', 'c'], dtype=str)
s.dtype
# dtype('O')

Do pandas 1.0 em diante, considere usar o "string"tipo .

# pandas >= 1.0
s = pd.Series(['a', 'b', 'c'], dtype="string")
s.dtype
# StringDtype

Aqui está o porquê, conforme citado pelos documentos:

  1. Você pode armazenar acidentalmente uma mistura de cadeias e não cadeias em uma matriz de tipo de objeto. É melhor ter um dtype dedicado.

  2. objectdtype quebra operações específicas do tipo, como DataFrame.select_dtypes(). Não há uma maneira clara de selecionar apenas o texto, excluindo as colunas que não são de texto, mas ainda o tipo de objeto.

  3. Ao ler o código, o conteúdo de uma objectmatriz dtype é menos claro que 'string'.

Veja também a seção Diferenças comportamentais entre "string"eobject .

Os tipos de extensão (introduzidos em 0.24 e formalizados em 1.0) estão mais próximos dos pandas do que do numpy, o que é bom porque os tipos numpy não são poderosos o suficiente. Por exemplo, o NumPy não tem como representar dados ausentes em dados inteiros (desde type(NaN) == float). Mas os pandas podem usar colunas Nullable Integer .


Por que devo parar de usá-lo?

Mistura acidental de tipos de dados
O primeiro motivo, conforme descrito nos documentos, é que você pode armazenar acidentalmente dados que não são de texto em colunas de objetos.

# pandas <= 0.25
pd.Series(['a', 'b', 1.23])   # whoops, this should have been "1.23"

0       a
1       b
2    1.23
dtype: object

pd.Series(['a', 'b', 1.23]).tolist()
# ['a', 'b', 1.23]   # oops, pandas was storing this as float all the time.
# pandas >= 1.0
pd.Series(['a', 'b', 1.23], dtype="string")

0       a
1       b
2    1.23
dtype: string

pd.Series(['a', 'b', 1.23], dtype="string").tolist()
# ['a', 'b', '1.23']   # it's a string and we just averted some potentially nasty bugs.

Desafiando a diferenciação de strings e outros objetos python
Outro exemplo óbvio de exemplo é que é mais difícil distinguir entre "strings" e "objetos". Os objetos são essencialmente do tipo geral para qualquer tipo que não suporte operações vetorizáveis .

Considerar,

# Setup
df = pd.DataFrame({'A': ['a', 'b', 'c'], 'B': [{}, [1, 2, 3], 123]})
df
 
   A          B
0  a         {}
1  b  [1, 2, 3]
2  c        123

Até os pandas 0,25, não havia praticamente nenhuma maneira de distinguir que "A" e "B" não tinham o mesmo tipo de dados.

# pandas <= 0.25  
df.dtypes

A    object
B    object
dtype: object

df.select_dtypes(object)

   A          B
0  a         {}
1  b  [1, 2, 3]
2  c        123

No pandas 1.0, isso se torna muito mais simples:

# pandas >= 1.0
# Convenience function I call to help illustrate my point.
df = df.convert_dtypes()
df.dtypes

A    string
B    object
dtype: object

df.select_dtypes("string")

   A
0  a
1  b
2  c

Legibilidade
Isso é auto-explicativo ;-)


OK, então devo parar de usá-lo agora?

...Não. No momento em que escrevemos esta resposta (versão 1.1), não há benefícios de desempenho, mas os documentos esperam que aprimoramentos futuros melhorem significativamente o desempenho e reduzam o uso de memória para "string"colunas em oposição a objetos. Com isso dito, no entanto, nunca é cedo para formar bons hábitos!


-1

O uso de .apply()uma lambdafunção de conversão também funciona neste caso:

total_rows['ColumnID'] = total_rows['ColumnID'].apply(lambda x: str(x))

Para quadros de dados inteiros, você pode usar .applymap(). (mas, em qualquer caso, provavelmente .astype()é mais rápido)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.