Crear Pandas DataFrame a partir de una cadena

276

Para probar alguna funcionalidad, me gustaría crear un a DataFramepartir de una cadena. Digamos que mis datos de prueba se ven así:

TESTDATA="""col1;col2;col3
1;4.4;99
2;4.5;200
3;4.7;65
4;3.2;140
"""

¿Cuál es la forma más sencilla de leer esos datos en un pandas DataFrame?

Emil H
fuente

Respuestas:

498

Una manera simple de hacer esto es usar StringIO.StringIO(python2) o io.StringIO(python3) y pasar eso a la pandas.read_csvfunción. P.ej:

import sys
if sys.version_info[0] < 3: 
    from StringIO import StringIO
else:
    from io import StringIO

import pandas as pd

TESTDATA = StringIO("""col1;col2;col3
    1;4.4;99
    2;4.5;200
    3;4.7;65
    4;3.2;140
    """)

df = pd.read_csv(TESTDATA, sep=";")
Emil H
fuente
77
Si necesita un código que sea compatible con Python 2 y 3, también puede usarlo opcionalmente from pandas.compat import StringIO, teniendo en cuenta que es la misma clase que la que viene con Python.
Acumenus
3
FYI - pd.read_table()es una función equivalente, sólo un poco mejor nomenclatura: df = pd.read_table(TESTDATA, sep=";").
wkzhu
55
@AntonvBR Notó que se podría usar pandas.compat.StringIO. De esa manera no tenemos que importar por StringIOseparado. Sin embargo, el pandas.compatpaquete se considera privado de acuerdo con pandas.pydata.org/pandas-docs/stable/api.html?highlight=compat, por lo que deja la respuesta como está por ahora.
Emil H
Si crea TESTDATA con df.to_csv(TESTDATA), useTESTDATA.seek(0)
user3226167
18

Método dividido

data = input_string
df = pd.DataFrame([x.split(';') for x in data.split('\n')])
print(df)
shaurya uppal
fuente
2
Si desea que se use la primera línea para los nombres de columna, cambie la segunda línea a esta:df = pd.DataFrame([x.split(';') for x in data.split('\n')[1:]], columns=[x for x in data.split('\n')[0].split(';')])
Mabyn
1
Esto está mal, ya que en los archivos CSV el carácter de nueva línea (\ n) puede ser parte de un campo.
Antonio Ercole De Luca
Esto no es muy robusto, y la mayoría de las personas serían mejores con la respuesta aceptada. Hay una lista muy parcial de cosas que pueden salir mal con esto en thomasburette.com/blog/2014/05/25/…
DanB
10

Una solución rápida y fácil para el trabajo interactivo es copiar y pegar el texto cargando los datos del portapapeles.

Seleccione el contenido de la cadena con el mouse:

Copie datos para pegar en un marco de datos de Pandas

En el uso de la shell Python read_clipboard()

>>> pd.read_clipboard()
  col1;col2;col3
0       1;4.4;99
1      2;4.5;200
2       3;4.7;65
3      4;3.2;140

Use el separador apropiado:

>>> pd.read_clipboard(sep=';')
   col1  col2  col3
0     1   4.4    99
1     2   4.5   200
2     3   4.7    65
3     4   3.2   140

>>> df = pd.read_clipboard(sep=';') # save to dataframe
usuario2314737
fuente
2
No es bueno para la reproducibilidad, ¡pero por lo demás es una solución bastante buena!
Mabyn
5

Esta respuesta se aplica cuando una cadena se ingresa manualmente, no cuando se lee desde algún lugar.

Un CSV tradicional de ancho variable es ilegible para almacenar datos como una variable de cadena. Especialmente para usar dentro de un .pyarchivo, considere en su lugar datos separados por tuberías de ancho fijo. Varios IDE y editores pueden tener un complemento para formatear texto separado por tuberías en una tabla ordenada.

Utilizando read_csv

Almacene lo siguiente en un módulo de utilidad, por ejemplo util/pandas.py. Se incluye un ejemplo en la cadena de documentación de la función.

import io
import re

import pandas as pd


def read_psv(str_input: str, **kwargs) -> pd.DataFrame:
    """Read a Pandas object from a pipe-separated table contained within a string.

    Input example:
        | int_score | ext_score | eligible |
        |           | 701       | True     |
        | 221.3     | 0         | False    |
        |           | 576       | True     |
        | 300       | 600       | True     |

    The leading and trailing pipes are optional, but if one is present,
    so must be the other.

    `kwargs` are passed to `read_csv`. They must not include `sep`.

    In PyCharm, the "Pipe Table Formatter" plugin has a "Format" feature that can 
    be used to neatly format a table.

    Ref: https://stackoverflow.com/a/46471952/
    """

    substitutions = [
        ('^ *', ''),  # Remove leading spaces
        (' *$', ''),  # Remove trailing spaces
        (r' *\| *', '|'),  # Remove spaces between columns
    ]
    if all(line.lstrip().startswith('|') and line.rstrip().endswith('|') for line in str_input.strip().split('\n')):
        substitutions.extend([
            (r'^\|', ''),  # Remove redundant leading delimiter
            (r'\|$', ''),  # Remove redundant trailing delimiter
        ])
    for pattern, replacement in substitutions:
        str_input = re.sub(pattern, replacement, str_input, flags=re.MULTILINE)
    return pd.read_csv(io.StringIO(str_input), sep='|', **kwargs)

Alternativas que no funcionan

El siguiente código no funciona correctamente porque agrega una columna vacía en los lados izquierdo y derecho.

df = pd.read_csv(io.StringIO(df_str), sep=r'\s*\|\s*', engine='python')

En cuanto a read_fwf, en realidad no usa tantos kwargs opcionales que read_csvacepta y usa. Como tal, no debe usarse para datos separados por tuberías.

Acumenus
fuente
1
Encontré (por prueba y error) que read_fwftoma más read_csvargumentos de lo que se documenta, pero es cierto que algunos no tienen ningún efecto .
Gerrit
-4

La manera más simple es guardarlo en un archivo temporal y luego leerlo:

import pandas as pd

CSV_FILE_NAME = 'temp_file.csv'  # Consider creating temp file, look URL below
with open(CSV_FILE_NAME, 'w') as outfile:
    outfile.write(TESTDATA)
df = pd.read_csv(CSV_FILE_NAME, sep=';')

Forma correcta de crear un archivo temporal: ¿Cómo puedo crear un archivo tmp en Python?

QtRoS
fuente
¿Qué pasa si no hay permiso para crear el archivo?
BingLi224
En mi opinión, ya no es el caso más simple. Tenga en cuenta que "más simple" se indica explícitamente en la pregunta.
QtRoS