Es muy habitual trabajar con archivos CSV, pero en ocasiones disponemos de ficheros de texto con determinado formato o con ancho fijo para las columnas.

Hace tiempo ya escribí sobre la lectura de archivos CSV con Python y Pandas, pero en esta ocasión vamos a leer archivos que no tienen un separador.

Evidentemente tienen que darnos el formato del archivo; en este caso, para ilustrar el ejemplo, vamos a pasar un código en R a un código en Python.

Necesitamos leer unos datos usados en el libro Non-Life Insurance Pricing with GLM; con R teníamos el siguiente programa:

varib <- c(edad = 2L, sexo = 1L, zona = 1L, clase_moto = 1L, antveh = 2L,
           bonus = 1L, exposicion = 8L, nsin = 4L, impsin = 8L)
varib.classes <- c("integer", rep("factor", 3), "integer",
                   "factor", "numeric", rep("integer", 2))
con <- url("https://staff.math.su.se/esbj/GLMbook/mccase.txt")
moto <- read.fwf(con, widths = varib, header = FALSE,
                 col.names = names(varib),
                 colClasses = varib.classes,
                 na.strings = NULL, comment.char = "")

Necesitamos crear ese data.frame moto con Python.

Evidentemente una lectura a las bravas no tiene sentido:

import pandas as pd
data1 = pd.read_fwf("http://staff.math.su.se/esbj/GLMbook/mccase.txt")
data1.head(10)

Lectura de texto con Python 1

El programa R ya nos define el formato del fichero y es necesario “traducirlo” a Pandas:

# Asignamos columnas
columnas = [(0, 2), (2, 3), (3, 4), (4, 5), (5, 7), (7, 8), (8, 16), (16, 20), (20, 28)]
data1 = pd.read_fwf("http://staff.math.su.se/esbj/GLMbook/mccase.txt", colspecs=columnas, header=None)
data1.head(10)

Vamos a crear una lista con el ancho de los campos que denominamos columnas; como siempre, en este caso empezamos en 0, no en el 1, y el final del anterior campo será el principio del siguiente; después sumamos a ese principio la longitud del campo que nos han definido.

Ahora, cuando usemos read_fwf, que es la función necesaria para leer files-with-format (ficheros con formato), en colspecs pondremos la lista con las longitudes de los campos y, en este caso, no tenemos encabezados, por lo que es necesario poner header = None.

Ya tenemos un data.frame al que solo falta asignar los nombres con los campos:

data1.columns = ['edad', 'sexo', 'zona', 'clase_moto', 'antveh', 'bonus', 'exposicion', 'nsin', 'impsin']
data1.head()

El ejemplo os sirve, pero se puede simplificar porque los campos son consecutivos usando widths = lista de longitudes:

data2 = pd.read_fwf("http://staff.math.su.se/esbj/GLMbook/mccase.txt", widths = [2, 1, 1, 1, 2, 1, 8, 4, 8], header=None)
data2.columns = ['edad', 'sexo', 'zona', 'clase_moto', 'antveh', 'bonus', 'exposicion', 'nsin', 'impsin']
data2.head()

Equivale a lo que hemos visto con anterioridad, pero es preferible usar el primer método porque es más rápida la lectura.

Saludos.