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)

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.