Análisis Exploratorio de Datos¶
Hemos hablado mucho de datos, todo muy de libro o juguete. Esta clase intentará acercarte a algunos de los principales desafíos a la hora de trabajar con distintas fuentes de datos y los problemas usuales que podrías encontrar.
Fuentes de datos¶
Para variar un poco, utilizaremos la librería pathlib
en lugar de os
para manejar directorios. El paradigma es un poco distinto, en lugar de muchas funciones, la filosofía es tratar a los directorios como objetos que tienen sus propios métodos y operaciones.
import numpy as np
import pandas as pd
from pathlib import Path
data_path = Path().resolve().parent / "data"
print(data_path)
/home/runner/work/mat281_2020S2/mat281_2020S2/data
CSV¶
Del inglés Comma-Separated Values, los archivos CSV utilizan comas (“,”) para separar valores y cada registro consiste de una fila.
Pros:
Livianos.
De fácil entendimiento.
Editables usando un editor de texto.
Contras:
No está totalmente estandarizado (e.g. ¿Qué pasa si un valor tiene comas?)
Son sensible al encoding (es la forma en que se codifica un carácter).
Pandas posee su propia función para leer csv: pd.read_csv()
.
# Documentación
# pd.read_csv?
Un ejemplo de encoding incorrecto
pd.read_csv(data_path / "encoding_example.csv", sep=",", encoding="gbk")
nombre | apellido | edad | |
---|---|---|---|
0 | Juan | P茅rez | 12.0 |
1 | Le贸n | Pardo | 29.0 |
2 | Jos茅 | Nu帽ez | NaN |
Mientras que el mismo dataset con el encoding correcto luce así
pd.read_csv(data_path / "encoding_example.csv", sep=",", encoding="utf-8")
nombre | apellido | edad | |
---|---|---|---|
0 | Juan | Pérez | 12.0 |
1 | León | Pardo | 29.0 |
2 | José | Nuñez | NaN |
JSON¶
Acrónimo de JavaScript Object Notation, utilizado principalmente para intercambiar datos entre una aplicación web y un servidor.
Pros:
Livianos.
De fácil entendimiento.
Editables usando un editor de texto.
Formato estandarizado.
Contras:
La lectura con pandas puede ser un poco complicada.
Pandas posee su propia función para leer JSON: pd.read_json()
.
# pd.read_json?
Se parecen mucho a los diccionarios de python pero en un archivo de texto.
!head ../data/json_example.json
{
"integer": {
"0": 5,
"1": 5,
"2": 9,
"3": 6,
"4": 6,
"5": 9,
"6": 7,
"7": 1,
pd.read_json(data_path / "json_example.json", orient="columns").head()
integer | datetime | category | |
---|---|---|---|
0 | 5 | 2015-01-01 00:00:00 | 0 |
1 | 5 | 2015-01-01 00:00:01 | 0 |
2 | 9 | 2015-01-01 00:00:02 | 0 |
3 | 6 | 2015-01-01 00:00:03 | 0 |
4 | 6 | 2015-01-01 00:00:04 | 0 |
Pickle¶
Es un módulo que implementa protocolos binarios de serialización y des-serialización de objetos de Python.
Pros
Puede representar una inmensa cantidad de tipos de objetos de python.
En un contexto de seguridad, como no es legible por el ser humano (representación binaria) puede ser útil para almacenar datos sensibles.
Contras:
Solo Python.
Si viene de un tercero podría tener contenido malicioso.
Pandas posee su propia función para leer pickles: pd.read_pickle()
.
# pd.read_pickle?
pd.read_pickle(data_path / 'nba.pkl').head()
name | year_start | year_end | position | height | weight | birth_date | college | |
---|---|---|---|---|---|---|---|---|
0 | Alaa Abdelnaby | 1991 | 1995 | F-C | 6-10 | 240.0 | June 24, 1968 | Duke University |
1 | Zaid Abdul-Aziz | 1969 | 1978 | C-F | 6-9 | 235.0 | April 7, 1946 | Iowa State University |
2 | Kareem Abdul-Jabbar | 1970 | 1989 | C | 7-2 | 225.0 | April 16, 1947 | University of California, Los Angeles |
3 | Mahmoud Abdul-Rauf | 1991 | 2001 | G | 6-1 | 162.0 | March 9, 1969 | Louisiana State University |
4 | Tariq Abdul-Wahad | 1998 | 2003 | F | 6-6 | 223.0 | November 3, 1974 | San Jose State University |
SQL¶
Conocimos las bases de datos relacionales SQL en clases anteriores y como recordarás existe la función pd.read_sql()
, lo interesante aquí es que debes crear una conexión antes de poder leer la base de datos. Cada Sistema de Gestión de Bases de Datos Relacionales (Relational Database Management System o RDBMS) tiene su propia forma de conectarse.
# pd.read_sql?
import sqlite3
connector = sqlite3.connect(data_path / "chinook.db")
pd.read_sql_query("select * from albums", con=connector).head()
AlbumId | Title | ArtistId | |
---|---|---|---|
0 | 1 | For Those About To Rock We Salute You | 1 |
1 | 2 | Balls to the Wall | 2 |
2 | 3 | Restless and Wild | 2 |
3 | 4 | Let There Be Rock | 1 |
4 | 5 | Big Ones | 3 |
API¶
¿Has escuchado el término API? Fuera de todo tecnicismo, las APIs (Application Programming Interface) permiten hacer uso de funciones ya existentes en otro software (o de la infraestructura ya existente en otras plataformas) para no estar reinventando la rueda constantemente, reutilizando así código que se sabe que está probado y que funciona correctamente. Por ejemplo, cuando haces una compra online y utilizas WebPay o una página utiliza los mapas de GoogleMaps. ¡Hay APIs en todos lados!
Utilizaremos la API de Open Notify para obtener cuántas personas hay en el espacio en este momento (link).
import requests
response = requests.get("http://api.open-notify.org/astros.json")
print(f"response has type {type(response)}")
print(response)
response has type <class 'requests.models.Response'>
<Response [200]>
Puedes acceder a su contenido como un JSON de la siguiente manera
response.json()
{'message': 'success',
'number': 7,
'people': [{'craft': 'ISS', 'name': 'Sergey Ryzhikov'},
{'craft': 'ISS', 'name': 'Kate Rubins'},
{'craft': 'ISS', 'name': 'Sergey Kud-Sverchkov'},
{'craft': 'ISS', 'name': 'Mike Hopkins'},
{'craft': 'ISS', 'name': 'Victor Glover'},
{'craft': 'ISS', 'name': 'Shannon Walker'},
{'craft': 'ISS', 'name': 'Soichi Noguchi'}]}
Lo cual en la práctica lo carga como un diccionario en Python
type(response.json())
dict
Por lo que podemos cargar ciertas estructuras a dataframes con métodos de pandas que utilicen diccionarios. Por dar un ejemplo, dentro del JSON obtenido hay una lista de personas.
pd.DataFrame.from_dict(response.json()["people"])
craft | name | |
---|---|---|
0 | ISS | Sergey Ryzhikov |
1 | ISS | Kate Rubins |
2 | ISS | Sergey Kud-Sverchkov |
3 | ISS | Mike Hopkins |
4 | ISS | Victor Glover |
5 | ISS | Shannon Walker |
6 | ISS | Soichi Noguchi |
Manos a la obra¶
El análisis exploratorio de datos es una forma de analizar datos definido por John W. Tukey (E.D.A.: Exploratory data analysis) es el tratamiento estadístico al que se someten las muestras recogidas durante un proceso de investigación en cualquier campo científico. Para mayor rapidez y precisión, todo el proceso suele realizarse por medios informáticos, con aplicaciones específicas para el tratamiento estadístico.
El análisis exploratorio de datos debería dar respuestas (al menos) a lo siguiente:
¿Qué pregunta(s) estás tratando de resolver (o probar que estás equivocado)?
¿Qué tipo de datos tiene y cómo trata los diferentes tipos?
¿Qué falta en los datos y cómo los maneja?
¿Qué hacer con los datos faltantes, outliers o información mal inputada?
¿Se puede sacar más provecho a los datos ?
Ejemplo: Datos de terremotos¶
El dataset earthquakes.csv
contiene la información de los terremotos de los países durante el año 2000 al 2011. Debido a que la información de este dataset es relativamente fácil de trabajar, hemos creado un dataset denominado earthquakes_contaminated.csv
que posee información contaminada en cada una de sus columnas. De esta forma se podrá ilustrar los distintos inconvenientes al realizar análisis exploratorio de datos.
pd.read_csv(data_path / "earthquakes.csv").head()
Año | Pais | Magnitud | |
---|---|---|---|
0 | 2011 | Turkey | 7.1 |
1 | 2011 | India | 6.9 |
2 | 2011 | Japan | 7.1 |
3 | 2011 | Burma | 6.8 |
4 | 2011 | Japan | 9.0 |
earthquakes = pd.read_csv(data_path / "earthquakes_contaminated.csv")
earthquakes.head()
Año | Pais | Magnitud | Informacion | |
---|---|---|---|---|
0 | 2000 | Turkey | 6 | info no valiosa |
1 | 2000 | Turkmenistan | 7 | info no valiosa |
2 | 2000 | Azerbaijan | 6.5 | info no valiosa |
3 | 2000 | Azerbaijan | 6.8 | info no valiosa |
4 | 2000 | Papua New Guinea | 8 | info no valiosa |
Variables
Pais:
Descripción: País del devento sísmico.
Tipo: string
Observaciones: No deberían encontrarse nombres de ciudades, comunas, pueblos, estados, etc.
Año:
Descripción: Año del devento sísmico.
Tipo: integer
Observaciones: Los años deben estar entre 2000 y 2011.
Magnitud:
Descripción: Magnitud del devento sísmico medida en Magnitud de Momento Sísmico.
Tipo: float
Observaciones: Magnitudes menores a 9.6.
Informacion:
Descripción: Columna contaminante.
Tipo: string
Observaciones: A priori pareciera que no entrega información a los datos.
A pesar que la magnitud es un float, el conocimiento de los datos nos da información relevante, pues el terremoto con mayor magnitud registrado a la fecha fue el de Valdivia, Chile el 22 de mayo de 1960 con una magnitud entre 9.4 - 9.6.
Los datos son solo bytes en el disco duro si es que no entregan valor y conocimiento.
¿Qué pregunta(s) estás tratando de resolver (o probar que estás equivocado)?¶
A modo de ejemplo, consideremos que que queremos conocer la mayor magnitud de terremoto en cada país a lo largo de los años.
¿Qué tipo de datos tiene y cómo trata los diferentes tipos?¶
Por el conocimiento de los datos sabemos que Pais
e Información
son variables categóricas, mientras que Año
y Magnitud
son variables numéricas.
Utilizemos las herramientas que nos entrega pandas
.
earthquakes.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 228 entries, 0 to 227
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Año 226 non-null object
1 Pais 226 non-null object
2 Magnitud 225 non-null object
3 Informacion 220 non-null object
dtypes: object(4)
memory usage: 7.2+ KB
earthquakes.describe(include="all").T
count | unique | top | freq | |
---|---|---|---|---|
Año | 226 | 16 | 2003 | 31 |
Pais | 226 | 74 | Indonesia | 27 |
Magnitud | 225 | 45 | 6.4 | 14 |
Informacion | 220 | 3 | info valiosa | 166 |
earthquakes.dtypes
Año object
Pais object
Magnitud object
Informacion object
dtype: object
Todas las columnas son de tipo object
, sospechoso. Además, algunas no tienen datos.
Tip: Típicamente se utilizan nombres de columnas en minúsculas y sin espacios. Un truco es hacer lo siguiente:
earthquakes = earthquakes.rename(columns=lambda x: x.lower().strip())
earthquakes.head()
año | pais | magnitud | informacion | |
---|---|---|---|---|
0 | 2000 | Turkey | 6 | info no valiosa |
1 | 2000 | Turkmenistan | 7 | info no valiosa |
2 | 2000 | Azerbaijan | 6.5 | info no valiosa |
3 | 2000 | Azerbaijan | 6.8 | info no valiosa |
4 | 2000 | Papua New Guinea | 8 | info no valiosa |
Se le aplicó una función lambda
a cada nombre de columna! Puum!
¿Qué falta en los datos y cómo los maneja?¶
No es necesario agregar más variables, pero si procesarla.
¿Qué hacer con los datos faltantes, outliers o información mal inputada?¶
A continuación iremos explorando cada una de las columnas.
for col in earthquakes:
print(f"La columna {col} posee los siguientes valores únicos:\n {earthquakes[col].sort_values().unique()}\n\n")
La columna año posee los siguientes valores únicos:
['1990' '1997' '1999' '2000' '2001' '2002' '2003' '2004' '2005' '2006'
'2007' '2008' '2009' '2010' '2011' 'dos mil uno' nan]
La columna pais posee los siguientes valores únicos:
['Afghanistan' 'Afghanistan ' 'Algeria' 'Algeria ' 'Argentina'
'Azerbaijan' 'Azerbaijan ' 'Bangladesh' 'Burma ' 'Chile' 'Chile ' 'China'
'China ' 'Colombia' 'Costa Rica' 'Costa Rica '
'Democratic Republic of the Congo' 'Democratic Republic of the Congo '
'Dominican Republic' 'Ecuador' 'El Salvador ' 'Greece' 'Greece '
'Guadeloupe' 'Guatemala' 'Haiti ' 'India' 'India ' 'Indonesia'
'Indonesia ' 'Iran' 'Iran ' 'Iran, 2005 Qeshm earthquake' 'Italy'
'Italy ' 'Japan' 'Japan ' 'Kazakhstan' 'Kyrgyzstan ' 'Martinique'
'Mexico ' 'Morocco' 'Morocco ' 'Mozambique' 'New Zealand' 'New Zealand '
'Nicaragua' 'Pakistan' 'Pakistan ' 'Panama' 'Papua New Guinea' 'Peru'
'Peru ' 'Philippines' 'Russian Federation' 'Rwanda' 'Samoa ' 'Serbia'
'Slovenia' 'Solomon Islands ' 'Taiwan' 'Taiwan ' 'Tajikistan'
'Tajikistan ' 'Tanzania' 'Tanzania ' 'Turkey' 'Turkey ' 'Turkmenistan'
'United States ' 'Venezuela' 'Vietnam' 'arica' 'shile' nan]
La columna magnitud posee los siguientes valores únicos:
['-10' '2002-Tanzania-5.8' '2003-japan-8.5' '4.7' '4.9' '5' '5.1' '5.2'
'5.3' '5.4' '5.5' '5.6' '5.7' '5.8' '5.9' '6' '6.1' '6.2' '6.3' '6.4'
'6.5' '6.6' '6.7' '6.8' '6.9' '7' '7.1' '7.2' '7.3' '7.4' '7.5' '7.6'
'7.7' '7.8' '7.9' '8' '8.1' '8.3' '8.4' '8.5' '8.6' '8.8' '9' '9.1' '9.7'
nan]
La columna informacion posee los siguientes valores únicos:
['info no valiosa' 'info valiosa' 'valiosa' nan]
En la columna
año
se presentan las siguientes anomalías:Datos vacíos.
Años sin importancia: Se ha establecido que los años de estudios son desde el año 2000 al 2011.
Nombres mal escritos: en este caso sabemos que ‘dos mil uno’ corresponde a ‘2001’.
En la columna
pais
se presentan las siguientes anomalías:Datos vacíos.
Ciudades, e.g. arica.
Países mal escritos e.g. shile.
Países repetidos pero mal formateados, e.g. Turkey.
Cruce de información, e.g. Iran, 2005 Qeshm earthquake.
En la columna
magnitud
se presentan las siguientes anomalías:Datos vacíos.
Cruce de información, e.g. 2002-Tanzania-5.8.
Valores imposibles, e.g. 9.7.
La columna
informacion
realmente no está entregando ninguna información valiosa al problema.
Partamos por eliminar la columna informacion
.
eqk = earthquakes.drop(columns="informacion") # A veces es importante no sobrescribir el dataframe original para realizar análisis posteriores.
eqk.head()
año | pais | magnitud | |
---|---|---|---|
0 | 2000 | Turkey | 6 |
1 | 2000 | Turkmenistan | 7 |
2 | 2000 | Azerbaijan | 6.5 |
3 | 2000 | Azerbaijan | 6.8 |
4 | 2000 | Papua New Guinea | 8 |
Respecto a la columna año
, corregir estos errores no es difícil, pero suele ser tedioso. Aparte que si no se realiza un correcto análisis es posible no detectar estos errores a tiempo. Empecemos con los registros nulos.
eqk.loc[lambda x: x["año"].isnull()]
año | pais | magnitud | |
---|---|---|---|
225 | NaN | NaN | 2002-Tanzania-5.8 |
226 | NaN | NaN | 2003-japan-8.5 |
Veamos el archivo
! sed -n "226,228p" data/earthquakes_contaminated.csv
sed: can't read data/earthquakes_contaminated.csv: No such file or directory
Toda la información está contenida en una columna!
Para editar la información usaremos dos herramientas:
Los métodos de
str
enpandas
, en particular para dividir una columna.loc
para asignar los nuevos valores.
eqk.loc[lambda x: x["año"].isnull(), "magnitud"].str.split("-", expand=True).values
array([['2002', 'Tanzania', '5.8'],
['2003', 'japan', '8.5']], dtype=object)
eqk.loc[lambda x: x["año"].isnull(), :] = eqk.loc[lambda x: x["año"].isnull(), "magnitud"].str.split("-", expand=True).values
eqk.loc[[225, 226]]
año | pais | magnitud | |
---|---|---|---|
225 | 2002 | Tanzania | 5.8 |
226 | 2003 | japan | 8.5 |
Ahora los registros que no se pueden convertir a numeric
. Veamos que no es posible convertirlo.
try:
eqk["año"].astype(np.int)
except Exception as e:
print(e)
invalid literal for int() with base 10: 'dos mil uno'
eqk["año"].str.isnumeric().fillna(False)
0 True
1 True
2 True
3 True
4 True
...
223 True
224 True
225 True
226 True
227 True
Name: año, Length: 228, dtype: bool
eqk.loc[lambda x: ~ x["año"].str.isnumeric()]
año | pais | magnitud | |
---|---|---|---|
31 | dos mil uno | China | 5.4 |
Veamos el valor a cambiar
eqk.loc[lambda x: ~ x["año"].str.isnumeric(), "año"].iloc[0]
'dos mil uno'
Reemplazar es muy fácil!
eqk["año"].str.replace("dos mil uno", "2001")
0 2000
1 2000
2 2000
3 2000
4 2000
...
223 1990
224 1999
225 2002
226 2003
227 2005
Name: año, Length: 228, dtype: object
Para asignar en el dataframe basta con:
eqk["año"] = eqk["año"].str.replace("dos mil uno", "2001").astype(np.int)
La forma encadenada sería:
# eqk["año"] = eqk.assign(año=lambda x: x["año"].str.replace("dos mil uno", "2001").astype("int"))
eqk.dtypes
año int64
pais object
magnitud object
dtype: object
Finalmentem, filtremos los años necesarios:
eqk = eqk.query("2000 <= año <= 2011")
Siguiendo de forma análoga con la columna magnitud
.
eqk.loc[lambda x: x["magnitud"].isnull()]
año | pais | magnitud | |
---|---|---|---|
219 | 2010 | Colombia | NaN |
220 | 2005 | Indonesia | NaN |
221 | 2010 | Venezuela | NaN |
La verdad es que no hay mucho que hacer con estos valores, por el momento no inputaremos ningún valor y los descartaremos.
eqk = eqk.loc[lambda x: x["magnitud"].notnull()]
try:
eqk["magnitud"].astype(np.float)
print("Ya es posible transformar la columna a float.")
except:
print("Aún no es posible transformar la columna a float.")
Ya es posible transformar la columna a float.
eqk = eqk.astype({"magnitud": np.float})
eqk.dtypes
año int64
pais object
magnitud float64
dtype: object
eqk.magnitud.unique()
array([ 6. , 7. , 6.5, 6.8, 8. , 5.7, 6.4, 5.5, 6.3,
5.4, 6.1, 6.7, 7.9, 7.2, 7.5, 5.3, 5.9, 9.7,
5.8, 4.7, 7.6, 8.4, 5. , 5.6, 6.6, 6.2, 7.1,
7.3, 5.1, 5.2, 8.3, 6.9, 9.1, 4.9, 7.8, 8.6,
7.7, 7.4, 8.5, 8.1, 8.8, 9. , -10. ])
eqk.query("magnitud < 0 or 9.6 < magnitud")
año | pais | magnitud | |
---|---|---|---|
22 | 2000 | shile | 9.7 |
217 | 2011 | shile | -10.0 |
218 | 2011 | shile | -10.0 |
eqk = eqk.query("0 <= magnitud <= 9.6")
eqk.query("magnitud < 0 or 9.6 < magnitud")
año | pais | magnitud |
---|
Finalmente, para la columna pais
. Comenzaremos con los nombres erróneos, estos los podemos mapear directamente.
map_paises = {"arica": "Chile", "shile": "Chile", "Iran, 2005 Qeshm earthquake": "Iran"}
eqk["pais"].map(map_paises).fillna(eqk["pais"])
0 Turkey
1 Turkmenistan
2 Azerbaijan
3 Azerbaijan
4 Papua New Guinea
...
215 China
216 New Zealand
225 Tanzania
226 japan
227 Chile
Name: pais, Length: 219, dtype: object
Para editarlo en el dataframe basta hacer un assign
.
eqk = eqk.assign(pais=lambda x: x["pais"].map(map_paises).fillna(x["pais"]))
Ahora formatearemos los nombres, pasándolos a minúsculas y quitando los espacios al principio y final de cada string. Y ahabíamos hablado del ejemplo de Turkey.
eqk.loc[lambda x: x["pais"].apply(lambda s: "Turkey" in s), "pais"].unique()
array(['Turkey', 'Turkey '], dtype=object)
# Chaining method
eqk = eqk.assign(pais=lambda x: x["pais"].str.lower().str.strip())
eqk.loc[lambda x: x["pais"].apply(lambda s: "turkey" in s), "pais"].unique()
array(['turkey'], dtype=object)
Nota que no hay países con valores nulos porque ya fueron reparados.
eqk.loc[lambda x: x["pais"].isnull()]
año | pais | magnitud |
---|
¿Se puede sacar más provecho a los datos ?¶
No es posible crear variables nuevas o algo por el estilo, ya se hizo todo el procesamiento necesario para cumplir las reglas de negocio.
earthquakes.shape
(228, 4)
eqk.shape
(219, 3)
Dar respuesta¶
Como es un método de agregación podríamos simplemente hacer un groupby
.
eqk.groupby(["pais", "año"])["magnitud"].max()
pais año
afghanistan 2000 6.3
2001 5.0
2002 7.3
2003 5.8
2004 6.5
...
turkmenistan 2000 7.0
united states 2001 6.8
2003 6.6
venezuela 2006 5.5
vietnam 2005 5.3
Name: magnitud, Length: 134, dtype: float64
Sin embargo, en ocasiones, una tabla pivoteada es mucho más explicativa.
eqk.pivot_table(
index="pais",
columns="año",
values="magnitud",
aggfunc="max",
fill_value=""
)
año | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
pais | ||||||||||||
afghanistan | 6.3 | 5 | 7.3 | 5.8 | 6.5 | 6.5 | ||||||
algeria | 5.7 | 6.8 | 5.2 | 5.5 | ||||||||
argentina | 7.2 | 6.1 | ||||||||||
azerbaijan | 6.8 | |||||||||||
bangladesh | 5.6 | |||||||||||
burma | 6.8 | |||||||||||
chile | 6.3 | 8 | 7.7 | 8.8 | ||||||||
china | 5.9 | 5.6 | 5.5 | 6.3 | 5.3 | 5.2 | 5 | 6.1 | 7.9 | 5.7 | 6.9 | 5.4 |
colombia | 6.5 | 5.9 | ||||||||||
costa rica | 6.4 | 6.1 | ||||||||||
democratic republic of the congo | 6.2 | 5.9 | ||||||||||
dominican republic | 6.4 | |||||||||||
ecuador | 5.5 | |||||||||||
el salvador | 7.6 | |||||||||||
greece | 6.2 | 6.4 | ||||||||||
guadeloupe | 6.3 | |||||||||||
guatemala | 6.4 | |||||||||||
haiti | 7 | |||||||||||
india | 7.6 | 6.5 | 5.1 | 5.3 | 5.1 | 6.9 | ||||||
indonesia | 7.9 | 7.5 | 6.9 | 9.1 | 8.6 | 7.7 | 8.5 | 7.3 | 7.6 | |||
iran | 5.3 | 6.5 | 6.6 | 6.3 | 6.4 | 6.1 | ||||||
italy | 4.7 | 5.9 | 6.2 | |||||||||
japan | 6.1 | 6.8 | 8.5 | 6.6 | 6.6 | 6.7 | 6.9 | 6.4 | 9 | |||
kazakhstan | 6 | |||||||||||
kyrgyzstan | 6.9 | |||||||||||
martinique | 7.4 | |||||||||||
mexico | 7.5 | |||||||||||
morocco | 6.3 | |||||||||||
mozambique | 7 | |||||||||||
new zealand | 5.4 | 6.6 | 6.3 | |||||||||
nicaragua | 5.4 | |||||||||||
pakistan | 6.3 | 5.4 | 7.6 | 4.9 | 5.2 | 6.4 | ||||||
panama | 6.5 | |||||||||||
papua new guinea | 8 | 7.6 | 6.1 | |||||||||
peru | 8.4 | 7.5 | 8 | |||||||||
philippines | 7.5 | 6.5 | 7.1 | 5.3 | ||||||||
russian federation | 7.3 | 6.2 | ||||||||||
rwanda | 5.3 | |||||||||||
samoa | 8.1 | |||||||||||
serbia | 5.7 | |||||||||||
slovenia | 5.2 | |||||||||||
solomon islands | 8.1 | |||||||||||
taiwan | 6.4 | 7.1 | 5.2 | 7 | ||||||||
tajikistan | 5.2 | 5.6 | 5.2 | |||||||||
tanzania | 6.4 | 5.8 | 6.8 | |||||||||
turkey | 6 | 6.5 | 6.3 | 5.6 | 5.9 | 6.1 | 7.1 | |||||
turkmenistan | 7 | |||||||||||
united states | 6.8 | 6.6 | ||||||||||
venezuela | 5.5 | |||||||||||
vietnam | 5.3 |
¿Notas las similitudes con groupby
? Ambos son métodos de agregación, pero retornan formas de la matriz distintas.
Sin embargo, esto se vería mucho mejor con una visualización, que es lo que veremos en el próximo módulo.
import altair as alt
alt.themes.enable('opaque')
alt.Chart(
eqk.groupby(["pais", "año"])["magnitud"].max().reset_index()
).mark_rect().encode(
x='año:O',
y='pais:N',
color='magnitud:Q'
)
Resumen¶
En el mundo real te encontrarás con múltiples fuentes de datos, es importante adaptarse ya que las tecnologías cambian constantemente.
Datos deben entregar valor a través del análisis.
Es poco probable que los datos vengan “limpios”.
El análisis exploratorio de datos (EDA) es una metodología que sirve para asegurarse de la calidad de los datos.
A medida que se tiene más experticia en el tema, mejor es el análisis de datos y por tanto, mejor son los resultados obtenidos.
No existe un procedimiento estándar para realizar el EDA, pero siempre se debe tener claro el problema a resolver.