import pandas as pd
# Almacenar datos en un dataframe
df = pd.read_csv('items_ordered_2years.txt', delimiter='|', on_bad_lines='skip')
# El parametro on_bad_lines='skip' elimina 10 lineas del fichero original que dan error
# Quitar errores de espacios al principio o al final
df['zipcode'] = df['zipcode'].str.strip()
df.head(2)
Algunos codigos postales son de otros paises. Estos tienen un formato distinto a espanol, que es de cinco numeros "#####".
Ademas hay otros errores, y los mas comunes se separan caso por caso para poder ser corregidos
# Encuentra las lineas con un codigo postal distinto del espanol
def zipcode_espanol(row):
zipcode = str(row['zipcode'])
city = str(row['city'])
'''
Devuelve >10 para las estaciones donde el codigo postal este ausente o sea extranjero y 0 para los espanoles
Esto detecta codigos postales de otros paises que no se van a incluir en el analisis
Por ejemplo los Cogidos Postales de Portugal tienen el formato '1000-017'
Los de Inglaterra tienen el formato 'NW10 5XA' o similar
Tambien detecta los tipos de errores que hemos visto que son mas comunes entre los cp espanoles para poder ser corregidos
'''
# Si el codigo es espanol
if len(zipcode) <= 5 and zipcode.isdigit():
return 0
# Comprueba si al permutar city y zipcode si que da un codigo postal espanol
if len(city) <= 5 and city.isdigit() and zipcode.isalpha():
return 2
# Si el codigo es portugues
if '-' in zipcode:
return 11
# Comprueba si al cambiar 'o' por 0 se queda un codigo postal espanol
if len(zipcode) <= 5 and zipcode.replace('o', '0').isdigit():
return 3
# Comprueba si al cambiar 'O' por 0 se queda un codigo postal espanol
if len(zipcode) <= 5 and zipcode.replace('O', '0').isdigit():
return 4
# Si ambos zipcode y city son texto
if zipcode.isalpha() and city.isalpha():
return 11
# Si el codigo es ingles
if len(zipcode.split()) > 1:
return 11
# Si el codigo es chino
if len(zipcode) > 5 and zipcode.isdigit():
return 11
# Comprueba si al quitar un espacio al principio da un cp espanol
if zipcode[0] == ' ' and zipcode[1:].isdigit() and len(zipcode) == 6:
return 5
# Comprueba si al quitar un espacio al final da un cp espanol
if zipcode[-1] == ' 'and zipcode[:-1].isdigit() and len(zipcode) == 6:
return 5
# Comprueba si al quitar un punto da un codigo postal espanol
if len(zipcode.replace('.', '')) <= 5 and zipcode.replace('.', '').isdigit():
return 6
# Si hay alguna combinacion de caracteres y letras
if not zipcode.isalpha() and not zipcode.isdigit():
return 11
# En el resto de casos, eliminarlos
return 11
# Crear nueva columna con un codigo que determina si es un codigo postal no valido
df['zip_error'] = df.apply(zipcode_espanol, axis=1)
# Corregir errores de permutar city y zipcode Tipo=2
cond = df['zip_error'] == 2
df.loc[cond, ['city','zipcode']] = df.loc[cond, ['zipcode', 'city']].values
# Corregir errores de quitar espacios
# Se han corregido al principio aplicando str.strip()
# Corregir el resto de errores identificados
# Se han corregido a mano para los errores mas comunes identificados
df.count()
Todos aquellas filas que:
Les falta el CP o la ciudad
Son codigos de otros paises
No incluyen el CP y en vez de eso incluyen el nombre de la ciudad por ejemplo city="Madrid" zipcode="Madrid"
Tienen otro tipo de errores mas raros, como empezar por "-", contener letras en vez de numeros, contener "/", etc.
Todas esas filas son eliminadas para dejar un dataset limpio
# Mantiene todas las lineas con los errores corregidos
# Elimina las lineas con los errores de codigos extranjeros y otros errores
df = df[df.zip_error < 3]
# Elimina la columna zip_error que ya no hace falta al haber acabado la limpieza
del df['zip_error']
# Elimina las otras filas con datos faltantes
df.dropna(axis='rows', how='any', inplace=True)
# Definir funciones para mapear con map y apply a estas nuevas columnas
import re
from datetime import datetime
def get_weekday(fecha):
'''
Recibe una fecha con formato "aaaa-mm-dd hh:mm:ss"
Devuelve el nombre del dia de la semana: Monday, Tuesday...
'''
days = ["Lunes", "Martes", "Miercoles",
"Jueves", "Viernes", "Sabado", "Domingo"]
items = re.split(':|-| ', fecha) # Lista con cada uno de los campos que componen la fecha por separado
x = datetime(int(items[0]), int(items[1]), int(items[2]), int(items[3]), int(items[4]), int(items[5]))
return(days[x.weekday()])
def corregir_base_cost(row):
'''
Soluciona los errores de algunas entradascon un "base_cost" demasiado alto relativo al "price"
Por ejemplo: "price" = 54.06 y "base_cost" = 47000.02
Entendemos que son errores de poner la coma demasiado a la derecha.
Se solucionan dividiendo base_cost entre 10 hasta que quede relativamente normal
En este ejemplo quedaria "price" = 54.06 y "base_cost" = 47.00002
"price" siempre debe ser mayor que "base_cost" o de lo contrario generaria perdidas para la empresa
'''
price = float(row['price'])
base_cost = float(row['base_cost'])
# Establece un criterio para filtrar los datos anomalos
if price > 1 and base_cost / price > 60:
# Dividir entre 10 para solucionar el error del fichero hasta que sea un coste normal relativo al precio
while base_cost > price:
base_cost /= 10
return base_cost
def obtener_provincia(cp):
# cp = codigo postal
relacion = {'04':'Almería',
'11':'Cádiz',
'14':'Córdoba',
'18':'Granada',
'21':'Huelva',
'23':'Jaén',
'29':'Málaga',
'41':'Sevilla',
'22':'Huesca',
'44':'Teruel',
'50':'Zaragoza',
'33':'Asturias',
'07':'Balears, Illes',
'35':'Palmas, Las',
'38':'Santa Cruz de Tenerife',
'39':'Cantabria',
'05':'Ávila',
'09':'Burgos',
'24':'León',
'34':'Palencia',
'37':'Salamanca',
'40':'Segovia',
'42':'Soria',
'47':'Valladolid',
'49':'Zamora',
'02':'Albacete',
'13':'Ciudad Real',
'16':'Cuenca',
'19':'Guadalajara',
'45':'Toledo',
'08':'Barcelona',
'17':'Girona',
'25':'Lleida',
'43':'Tarragona',
'03':'Alicante/Alacant',
'12':'Castellón/Castelló',
'46':'Valencia/València',
'06':'Badajoz',
'10':'Cáceres',
'15':'Coruña, A',
'27':'Lugo',
'32':'Ourense',
'36':'Pontevedra',
'28':'Madrid',
'30':'Murcia',
'31':'Navarra',
'01':'Araba/Álava',
'48':'Bizkaia',
'20':'Gipuzkoa',
'26':'Rioja, La'}
if cp[:2] in relacion:
return relacion[cp[:2]]
else:
return None
def obtener_comunidad(cp):
# cp = codigo postal
relacion = {'04':'Andalucía',
'11':'Andalucía',
'14':'Andalucía',
'18':'Andalucía',
'21':'Andalucía',
'23':'Andalucía',
'29':'Andalucía',
'41':'Andalucía',
'22':'Aragón',
'44':'Aragón',
'50':'Aragón',
'33':'Asturias, Principado de',
'07':'Balears, Illes',
'35':'Canarias',
'38':'Canarias',
'39':'Cantabria',
'05':'Castilla y León',
'09':'Castilla y León',
'24':'Castilla y León',
'34':'Castilla y León',
'37':'Castilla y León',
'40':'Castilla y León',
'42':'Castilla y León',
'47':'Castilla y León',
'49':'Castilla y León',
'02':'Castilla-La Mancha',
'13':'Castilla-La Mancha',
'16':'Castilla-La Mancha',
'19':'Castilla-La Mancha',
'45':'Castilla-La Mancha',
'08':'Cataluña',
'17':'Cataluña',
'25':'Cataluña',
'43':'Cataluña',
'03':'Comunitat Valenciana',
'12':'Comunitat Valenciana',
'46':'Comunitat Valenciana',
'06':'Extremadura',
'10':'Extremadura',
'15':'Galicia',
'27':'Galicia',
'32':'Galicia',
'36':'Galicia',
'28':'Madrid, Comunidad de',
'30':'Murcia, Región de',
'31':'Navarra, Comunidad Foral de',
'01':'País Vasco',
'48':'País Vasco',
'20':'País Vasco',
'26':'Rioja, La'}
if cp[:2] in relacion:
return relacion[cp[:2]]
else:
return None
# Usar map para crear la nueva columna Weekday
df['weekday'] = df['created_at'].map(get_weekday)
# Cambiar la columna discount_percent para que este en tanto por 1
df['discount_percent'] = df['discount_percent'].div(100)
# Renombra a "discount"
df.rename(columns = {'discount_percent':'discount'}, inplace = True)
# Cambiar la columna base cost para solucionar datos anomalos de base_cost demasiado alto
df['base_cost'] = df.apply(corregir_base_cost, axis=1)
# Anadir una nueva columna que indique la provincia del codigo postal
df['provincia'] = df['zipcode'].map(obtener_provincia)
# Anadir una nueva columna que indique la comunidad del codigo postal
df['comunidad'] = df['zipcode'].map(obtener_comunidad)
df.to_csv('items_o_2_corregido.csv', sep='|', index=False)
Este fichero tiene repetidas entradas de la columna product_id. Tras investigarlo, se trata de variaciones en la descripcion del producto, tales como cambiar el orden de las palabras, cambiar el idioma de la descripcion, cambiar alguna palabra de la descripcion. Sin embargo, campos como el sku y el URL se mantienen identicos entre los duplicaods.
Vamos a eliminar los duplicados de esta columna para facilitar su uso posterior con SQLite y evitar lineas duplicadas.
El criterio es mantener el primer product_id que aparezca en el fichero products.csv y eliminar los posteriores.
import pandas as pd
df = pd.read_csv("products.csv", sep=";")
df.head(2)
'''
Un ejemplo de product_id repetido es el siguiente
'''
df2 = df.loc[df["product_id"] == 78433, ["product_id", "name", "sku", "picture"]]
df2
'''
Elimina las lineas que tienen product_id repetidas, manteniendo la primera
'''
df = df.drop_duplicates(subset='product_id', keep='first')
# Comprobar que se han eliminado
df2 = df.loc[df["product_id"] == 78433, ["product_id", "name", "sku", "picture"]]
df2
# Escribir a csv
df.to_csv("products_corregido.csv", sep="|", index=False)
Este programa limpia los datos de renta media bruta de la Agencia Tributaria
Como entrada recibe un fichero rentas_cp.txt y devuelve un fichero de la forma:
Ciudad (str)-----------------Barrio (str)-----------------Codigo Postal (str)-----------------Renta media bruta (int)
import csv
with open("renta_bruta_media_cp.csv", "w") as fout:
writer = csv.writer(fout, lineterminator='\n')
writer.writerow(['Ciudad', 'Barrio', 'Codigo Postal', 'Renta Bruta Media'])
with open("rentas_cp.txt", "r") as fi:
# Indica las lineas con nombre del barrio y las que tienen datos, segun apareen en el fichero txt
iterator = 0
# Indica si se debe leer la siguiente linea de datos
iterator2 = 1
for line in fi.readlines():
# Si la linea es de inicio de una seccion de provincia
# En ese caso empieza por "Contrae la tabla"
if line[0:7] == "Contrae":
# Encontrar la posicion del final del nombre de la ciudad
pos = line.find("-")
ciudad = line[17:pos]
iterator = 1
else:
if iterator == 1: # Si es una linea con el nombre del barrio
if line[:5] != "Resto": # Omitir las lineas con info sobre el resto de la ciudad mezclado
cp = line[:5]
barrio = line[6:-1]
iterator2 = 1
else:
iterator2 = 0
iterator = 0
elif iterator2 == 1: # Si es una linea con datos de renta
renta_bruta_media = int(line.split()[1].replace('.', ''))
# Guardar los datos
datos = []
datos.append(ciudad)
datos.append(barrio)
datos.append(cp)
datos.append(renta_bruta_media)
writer.writerow(datos)
iterator = 1