Curso: Análisis de datos
Magíster en Data Science - Universidad del Desarrollo
En el mundo actual, la generación y recopilación de datos se ha vuelto más accesible y significativa que nunca antes.
El proceso de transformar estos datos crudos en información útil y significativa es fundamental.
En este curso nos enfocaremos en:

El primer paso en el proceso de análisis de datos implica la adquisición y el almacenamiento de los datos.
Esto se refiere a la recolección de los datos necesarios para abordar una pregunta o problema en particular.
Puede implicar la recopilación de datos de fuentes diversas, como bases de datos, archivos CSV, páginas web o incluso sensores en tiempo real.
Existen tantas fuentes de datos, como podríamos imaginar…
Datos disponibles
En nuestro proyecto vamos a usar datos de tres posibles fuentes:
Veamos como acceder algunos de estos datos.
La Encuesta de Caracterización Socioeconómica Nacional (CASEN), se realuza en chile:
Si tenemos los datos alojados en una dependencia, simplemente los cargamos. . . .
| folio | o | id_persona | region | comuna | zona | expr | edad | sexo | tot_per | ... | esc2 | educ | o1 | yaut | yauth | yautcor | yautcorh | ytrabajocor | ytrabajocorh | yae | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.101100e+11 | 1 | 5 | Región de Tarapacá | Iquique | Urbano | 67 | 34 | Mujer | 2 | ... | 12.0 | Media humanista completa | No | 220000.0 | 300000 | 220000.0 | 300000 | 150000.0 | 150000.0 | 240586.0 |
| 1 | 1.101100e+11 | 2 | 6 | Región de Tarapacá | Iquique | Urbano | 67 | 4 | Mujer | 2 | ... | NaN | Sin educación formal | NaN | 80000.0 | 300000 | 80000.0 | 300000 | NaN | 150000.0 | 240586.0 |
| 2 | 1.101100e+11 | 2 | 31 | Región de Tarapacá | Iquique | Urbano | 67 | 5 | Mujer | 3 | ... | NaN | Básica incompleta | NaN | 25000.0 | 941583 | 25000.0 | 941583 | NaN | 891583.0 | 439170.0 |
| 3 | 1.101100e+11 | 1 | 32 | Región de Tarapacá | Iquique | Urbano | 67 | 45 | Hombre | 3 | ... | 15.0 | Técnico nivel superior incompleta | Sí | 889500.0 | 941583 | 889500.0 | 941583 | 889500.0 | 891583.0 | 439170.0 |
4 rows × 22 columns
Otra opción es que los datos estén en una API:
#pandas remote data access support for calls to the World Bank Indicators API
from pandas_datareader import data, wb
# para instalar: conda install pandas-datareader
# o pip install pandas-datareader
#Revisemos que indicadores hay disponibles.
# En este caso revisare de PIB (GDP en ingés),
# pero se pueden explorar muchas más opciones.
wb.search('gdp')| id | name | unit | source | sourceNote | sourceOrganization | topics | |
|---|---|---|---|---|---|---|---|
| 688 | 6.0.GDP_current | GDP (current $) | LAC Equity Lab | GDP is the sum of gross value added by all res... | b'World Development Indicators (World Bank)' | Economy & Growth | |
| 689 | 6.0.GDP_growth | GDP growth (annual %) | LAC Equity Lab | Annual percentage growth rate of GDP at market... | b'World Development Indicators (World Bank)' | Economy & Growth | |
| 690 | 6.0.GDP_usd | GDP (constant 2005 $) | LAC Equity Lab | GDP is the sum of gross value added by all res... | b'World Development Indicators (World Bank)' | Economy & Growth | |
| 691 | 6.0.GDPpc_constant | GDP per capita, PPP (constant 2011 internation... | LAC Equity Lab | GDP per capita based on purchasing power parit... | b'World Development Indicators (World Bank)' | Economy & Growth |
| iso3c | iso2c | name | region | adminregion | incomeLevel | lendingType | capitalCity | longitude | latitude | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ABW | AW | Aruba | Latin America & Caribbean | High income | Not classified | Oranjestad | -70.0167 | 12.5167 | |
| 1 | AFE | ZH | Africa Eastern and Southern | Aggregates | Aggregates | Aggregates | NaN | NaN | ||
| 2 | AFG | AF | Afghanistan | South Asia | South Asia | Low income | IDA | Kabul | 69.1761 | 34.5228 |
| 3 | AFR | A9 | Africa | Aggregates | Aggregates | Aggregates | NaN | NaN | ||
| 4 | AFW | ZI | Africa Western and Central | Aggregates | Aggregates | Aggregates | NaN | NaN |
#sabemos que queremos Chile, asi que busquemos su info
countries[ countries['name'] == 'Chile' ]
# Descarguemos la data desde la API del banco mundial a un dataframe
df_GPDpc_Chile = wb.download(
#Use the indicator attribute to identify which indicator or indicators to download
indicator='NY.GDP.PCAP.KD',
#Use the country attribute to identify the countries you want data for
country=['CL'],
#Identify the first year for which you want the data, as an integer or a string
start='1980',
#Identify the last year for which you want the data, as an integer or a string
end=2020
)
df_GPDpc_Chile.info()<class 'pandas.core.frame.DataFrame'>
MultiIndex: 41 entries, ('Chile', '2020') to ('Chile', '1980')
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 NY.GDP.PCAP.KD 41 non-null float64
dtypes: float64(1)
memory usage: 2.0+ KB
Data frame con los datos de Chile, entre 1980 y 2020.
Si quisieramos, por simplicidad quedarnos solo con el indice del año y reordenar el dataframe:
| NY.GDP.PCAP.KD | |
|---|---|
| year | |
| 1980 | 4694.337113 |
| 1981 | 4928.563103 |
| 1982 | 4322.647868 |
| 1983 | 4047.790234 |
| 1984 | 4154.496068 |
Ahora, realicemos un grafico rápido con nuestros datos:
Pregunta 1 - Bajando y formateando datos del Banco Mundial
Replique el ejemplo práctico de importar datos desde la API del Banco Mundial y empezar la base para su análisis de series de tiempo.
Importe la serie de GDP total Y Percapita para otro país serie desde la API del Banco mundial, muestre sus principales características y realice un grafico.
¿pareciera haber tendencias?
El proceso de abstraer la realidad
Pregunta 2 - Investigando sobre países:
Considere que tenemos los datos del banco mundial, del país que selecciono anteriormente, y desea aprender sobre alguna característica de dicho país en el periodo.
Escriba una pregunta de investigación que se pueda responder con los datos disponibles.
Inferencia se refiere al proceso de hacer generalizaciones de una población a partir de una muestra de esa población.
Población y Muestra
El proceso de inferencia estadística se basa en el principio de que una muestra bien s eleccionada puede proporcionar información valiosa sobre la población en general.
El uso de la inferencia estadística es fundamental, especialmente si es impracticable o costoso analizar cada elemento de una población en particular.
Estadigrafos

Estadigrafos más comunes
La distribución de las medias muestrales de una población se aproxima a una distribución normal
Independientemente de la forma de la distribución original de la población.
Este teorema es esencial en inferencia estadística y tiene amplias aplicaciones en análisis de datos y toma de decisiones.
La media muestral se distribuye normal, sin importar la distribución de la variable subyacente
Formalmente: \[ \bar{x} \sim_a N\left(\mu, \frac{\sigma}{\sqrt{n}}\right)\]
Si tomamos muestras aleatorias de tamaño n de esta población y calculamos la media muestral de cada muestra
Las medias muestrales se aproximará a una distribución normal con media μ y desviación estándar σ/√n.
Formalmente: \[ \bar{x} \sim_a N\left(\mu, \frac{\sigma}{\sqrt{n}}\right)\]
Un intervalo de confianza contiene los posibles valores del estimador, entre un límite inferior y un límite superior, con cierta probabilidad.
Este intervalo es aleatorio, porque \(\bar{y}\) es diferente en cada muestra.
Matemáticamente, para cada muestra podemos construir un intervalo.
\[ P\left( \bar{y}-\frac{1.96\sigma }{\sqrt{n}} < \mu < \bar{y} + \frac{1.96\sigma }{\sqrt{n}} \right) = 0.95 \]
\[ \left(\bar{y} - \frac{c\times S}{\sqrt{n}}, \bar{y} + \frac{c\times S}{\sqrt{n}} \right) \]
Pensemos en un 95% de confianza (un valor usual):
Una forma de verificar hipotesis sobre los parámetros es mediante el contraste de hipótesis.
Empezamos suponiendo que hay una distribución conocida para el estadígrafo, centrada en un valor específico.
Y nos preguntamos, si esto fuea verdad ¿qué tan probable es la muestra que tengo?
Llamamos la hipótesis a probar Ho, y su alternativa H1.
Asociada esta prueba, entonces, hay asociados dos tipos de errores:
Se elige nivel de significancia de contraste (α) = probabilidad de cometer error Tipo I. Típicamente α = 0,01, 0,05, 0,10.
Definimos la prueba de hipótesis de significancia como aquella que indica si un estimador \(\hat{T}\) es 0.
\[ H_0: T =0\text{ vs }H_1: T \neq 0 \]
El Valor de probabilidad (ó p-valor) es el nivel probabilidad más alto para el cual no podemos rechazar la hipótesis nula de la prueba de significancia.
Los datos “Palmer Penguins” son un conjunto que detalla medidas morfológicas y características de tres especies de pingüinos: Adelie, Gentoo y Chinstrap.
Recopilados por el Dr. Bill Link y su equipo. (Horst AM, Hill AP, Gorman KB (2020). palmerpenguins: Palmer Archipelago (Antarctica) penguin data. doi:10.5281/zenodo.3960218, R package version 0.1.0, https://allisonhorst.github.io/palmerpenguins/index.html)

| species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | |
|---|---|---|---|---|---|---|---|
| 0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | Male |
| 1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | Female |
| 2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | Female |
| 3 | Adelie | Torgersen | NaN | NaN | NaN | NaN | NaN |
| 4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | Female |
| 5 | Adelie | Torgersen | 39.3 | 20.6 | 190.0 | 3650.0 | Male |
La elección de la muestra, la interpretación de los resultados y el nivel de confianza seleccionado son aspectos cruciales para realizar inferencias precisas y significativas.
Relicemos algunos ejemplos de pruebas de hipótesis, sobre el peso de los pingüinos.
Por ahora, pensemos que nuestra información es la población completa
Calcularemos el promedio muestral y lo veremos en el contexto de los datos observados: . . .
import matplotlib.pyplot as plt
# Calcular el promedio del peso de los pingüinos
promedio_peso = penguins['body_mass_g'].mean()
# Crear un histograma de la distribución del peso con el promedio
plt.figure(figsize=(10, 6))
sns.histplot(data=penguins, x='body_mass_g', bins=20, kde=True)
plt.axvline(x=promedio_peso, color='red', linestyle='dashed', label='Promedio')
plt.title('Distribución de Peso de Pingüinos')
plt.xlabel('Masa Corporal (g)')
plt.ylabel('Frecuencia')
plt.legend()
plt.show()
Nuestra idea de la inferencia, es aprovechar las propiedades del promedio muestral.
De que es el promedio muestral el que se distribuye normal, su media es la media poblacional y conocemos sus características.
Por ejemplo, consideremos que de esta población de pingüinos obtenemos 1000 muestras de 40 individuos cada una.
Si graficamos sus medias, podremos ver que estas se distribuyen aproximadamente normal.
import numpy as np
# Definir el tamaño de cada muestra y la cantidad de muestras
tamano_muestra = 50
cantidad_muestras = 10000
# Crear una lista para almacenar las medias de cada muestra
medias_muestras = []
# Realizar el muestreo y cálculo de medias para cada muestra
for _ in range(cantidad_muestras):
muestra = np.random.choice(penguins['body_mass_g'], size=tamano_muestra, replace=False)
media_muestra = np.mean(muestra)
medias_muestras.append(media_muestra)
# Calcular el promedio de los promedios de las muestras
promedio_promedios = np.mean(medias_muestras)
# Crear el gráfico de las medias de las muestras
plt.figure(figsize=(10, 6))
plt.hist(medias_muestras, bins=20, edgecolor='black', alpha=0.7)
plt.axvline(x=promedio_promedios, color='red', linestyle='dashed', label='Promedio de Promedios')
plt.title('Distribución de Medias de Muestras')
plt.xlabel('Media de Muestra de Peso (g)')
plt.ylabel('Frecuencia')
plt.legend()
plt.show()
Obtengamos una muestra y calculemos un intervalo de confianza:
import seaborn as sns
import numpy as np
import scipy.stats as stats
# Obtener una muestra simple de 40 pingüinos
sample_size = 40
sample = np.random.choice(penguins["body_mass_g"], size=sample_size)
# Calcular el error estándar de la media muestral
sample_std = np.std(sample, ddof=1) # Usar ddof=1 para calcular la desviación estándar muestral
standard_error = sample_std / np.sqrt(sample_size)
# Nivel de confianza (por ejemplo, 95%)
confidence_level = 0.95
# Calcular el margen de error
margin_of_error = stats.t.ppf((1 + confidence_level) / 2, df=sample_size - 1) * standard_error
# Calcular el intervalo de confianza
sample_mean = np.mean(sample)
confidence_interval = (sample_mean - margin_of_error, sample_mean + margin_of_error)
print("Intervalo de Confianza para el Peso:")
print(confidence_interval)Obtengamos una muestra y calculemos un intervalo de confianza:
El resultado será un rango de valores dentro del cual es probable que se encuentre el verdadero peso promedio de los pingüinos en la población, con un nivel de confianza del 95%.
¿Como nos fue? ¿Contiene al verdadero valor?
Ahora consideremos que tenemos grupos que queremos comparar.
Si hacemos una grafica de distribución de tamaño por especie y sexo, podriamos empezar a analizar diferencias entre los grupos.
# Crear la tabla de doble entrada por tipo y sexo de los pinguinos
tabla_doble_entrada = penguins.groupby(['species', 'sex'])['body_mass_g'].agg(['mean', 'var']).reset_index()
# Renombrar las columnas para mayor claridad
tabla_doble_entrada.rename(columns={'mean': 'Promedio', 'var': 'Varianza'}, inplace=True)
# Mostrar la tabla de doble entrada
print(tabla_doble_entrada) species sex Promedio Varianza
0 Adelie Female 3368.835616 72565.639269
1 Adelie Male 4043.493151 120278.253425
2 Chinstrap Female 3527.205882 81415.441176
3 Chinstrap Male 3938.970588 131143.605169
4 Gentoo Female 4679.741379 79286.335451
5 Gentoo Male 5484.836066 98068.306011
Podriamos querer saber si el peso es diferente para los pinguinos de la especie Adelie, para diferentes sexos:
Pregunta de Prueba de Hipótesis: ¿Existe una diferencia significativa en el peso promedio entre los pingüinos machos y las pingüinas hembras en la especie “Adelie”?
Hipótesis Nula (H0):
No hay diferencia significativa en el peso promedio entre los pingüinos machos y las pingüinas hembras en la especie “Adelie”.
Hipótesis Alternativa (H1):
Existe una diferencia significativa en el peso promedio entre los pingüinos machos y las pingüinas hembras en la especie “Adelie”.
import matplotlib.pyplot as plt
# Cargar el conjunto de datos "Penguins"
penguins = sns.load_dataset("penguins")
# Filtrar los pingüinos de la especie "Adelie"
adelie_penguins = penguins[penguins['species'] == 'Adelie']
# Crear un histograma para la distribución de peso por sexo
plt.figure(figsize=(10, 6))
sns.histplot(data=adelie_penguins, x='body_mass_g', hue='sex', bins=20, kde=True)
plt.title('Distribución de Peso por Sexo para Pingüinos Adelie')
plt.xlabel('Masa Corporal (g)')
plt.ylabel('Frecuencia')
plt.legend(title='Sexo')
plt.show()
# Crear un gráfico de densidad con líneas de promedio
plt.figure(figsize=(10, 6))
sns.kdeplot(data=adelie_penguins, x='body_mass_g', hue='sex', fill=True, common_norm=False)
plt.axvline(x=adelie_penguins.groupby('sex')['body_mass_g'].mean()['Female'], color='blue', linestyle='dashed', label='Promedio Femenino')
plt.axvline(x=adelie_penguins.groupby('sex')['body_mass_g'].mean()['Male'], color='orange', linestyle='dashed', label='Promedio Masculino')
plt.title('Densidad de Peso por Sexo para Pingüinos Adelie')
plt.xlabel('Masa Corporal (g)')
plt.ylabel('Densidad')
plt.legend()
plt.show()
# Filtrar los pingüinos de la especie "Adelie"
adelie_penguins = penguins[penguins['species'] == 'Adelie']
# Filtrar machos y hembras
machos = adelie_penguins[adelie_penguins['sex'] == 'Male']
hembras = adelie_penguins[adelie_penguins['sex'] == 'Female']
# Realizar la prueba t independiente
t_statistic, p_value = stats.ttest_ind(machos['body_mass_g'], hembras['body_mass_g'], equal_var=False)
# Imprimir resultados
print("Estadística t:", t_statistic)
print("Valor p:", p_value)
# Crear un gráfico de comparación de peso
plt.figure(figsize=(10, 6))
sns.boxplot(data=[machos['body_mass_g'], hembras['body_mass_g']], palette=['blue', 'pink'])
plt.title('Comparación de Peso entre Machos y Hembras de Pingüinos Adelie')
plt.xticks([0, 1], ['Machos', 'Hembras'])
plt.ylabel('Peso (g)')
plt.show()Estadística t: 13.126285923485874
Valor p: 6.402319748031793e-26

import seaborn as sns
import scipy.stats as stats
# Cargar el conjunto de datos "Penguins"
penguins = sns.load_dataset("penguins")
# Filtrar machos y hembras
machos = penguins[penguins['sex'] == 'Male']
hembras = penguins[penguins['sex'] == 'Female']
# Realizar una prueba ANOVA
result = stats.f_oneway(machos['body_mass_g'], hembras['body_mass_g'])
# Imprimir resultados
print("Estadística F:", result.statistic)
print("Valor p:", result.pvalue)Estadística F: 72.96098633250911
Valor p: 4.897246751596325e-16

Imaginemos que trabajamos en una empresa de e-commerce que vende productos electrónicos y queremos aumentar las ventas en una línea de productos específica, como teléfonos móviles.
Para ello, decidimos utilizar una promoción de ventas basada en una ruleta lúdica que ofrecerá descuentos a los clientes que la utilicen.
Para implementar la promoción, primero seleccionamos aleatoriamente un grupo de clientes y les enviamos un correo electrónico
import numpy as np
import pandas as pd
import random
#| output: false
# Define una semilla para la generación de números aleatorios
np.random.seed(123)
random.seed(123)
# Crear un vector de 200 valores aleatorios para el grupo de control
control = np.random.choice(["Control"], size=200, replace=True)
# Crear un vector de 200 valores aleatorios para el grupo de tratamiento
tratamiento = np.random.choice(["Treatment 1", "Treatment 2"], size=100, replace=True, p=[0.7, 0.3])
# Crear un vector de número de compras para cada grupo
control_compras = np.random.binomial(5, 0.2, size=200)
tratamiento1_compras = np.random.binomial(5, 0.4, size=100)
tratamiento2_compras = np.random.binomial(5, 0.6, size=100)
# Combinar los vectores en un DataFrame
data = {
'grupo': np.concatenate((control, np.repeat("Treatment", 200))),
'tipo_tratamiento': np.concatenate((np.repeat("Control", 200), np.repeat(["Treatment 1", "Treatment 2"], [100, 100]))),
'ventas': np.concatenate((control_compras, tratamiento1_compras, tratamiento2_compras))
}
ventas_df = pd.DataFrame(data)
# Verificar el DataFrame
ventas_df| grupo | tipo_tratamiento | ventas | |
|---|---|---|---|
| 0 | Control | Control | 1 |
| 1 | Control | Control | 1 |
| 2 | Control | Control | 0 |
| 3 | Control | Control | 0 |
| 4 | Control | Control | 0 |
| ... | ... | ... | ... |
| 395 | Treatment | Treatment 2 | 1 |
| 396 | Treatment | Treatment 2 | 2 |
| 397 | Treatment | Treatment 2 | 1 |
| 398 | Treatment | Treatment 2 | 3 |
| 399 | Treatment | Treatment 2 | 2 |
400 rows × 3 columns
import numpy as np
import pandas as pd
import random
#| echo: false
# Define una semilla para la generación de números aleatorios
np.random.seed(123)
random.seed(123)
# Crear un vector de 200 valores aleatorios para el grupo de control
control = np.random.choice(["Control"], size=200, replace=True)
# Crear un vector de 200 valores aleatorios para el grupo de tratamiento
tratamiento = np.random.choice(["Treatment 1", "Treatment 2"], size=100, replace=True, p=[0.7, 0.3])
# Crear un vector de número de compras para cada grupo
control_compras = np.random.binomial(5, 0.2, size=200)
tratamiento1_compras = np.random.binomial(5, 0.4, size=100)
tratamiento2_compras = np.random.binomial(5, 0.6, size=100)
# Combinar los vectores en un DataFrame
data = {
'grupo': np.concatenate((control, np.repeat("Treatment", 200))),
'tipo_tratamiento': np.concatenate((np.repeat("Control", 200), np.repeat(["Treatment 1", "Treatment 2"], [100, 100]))),
'ventas': np.concatenate((control_compras, tratamiento1_compras, tratamiento2_compras))
}
ventas_df = pd.DataFrame(data)
# Verificar el DataFrame
ventas_df| grupo | tipo_tratamiento | ventas | |
|---|---|---|---|
| 0 | Control | Control | 1 |
| 1 | Control | Control | 1 |
| 2 | Control | Control | 0 |
| 3 | Control | Control | 0 |
| 4 | Control | Control | 0 |
| ... | ... | ... | ... |
| 395 | Treatment | Treatment 2 | 1 |
| 396 | Treatment | Treatment 2 | 2 |
| 397 | Treatment | Treatment 2 | 1 |
| 398 | Treatment | Treatment 2 | 3 |
| 399 | Treatment | Treatment 2 | 2 |
400 rows × 3 columns
Pregunta 3 -Ejemplo AB test en Marketing:
Estudiemos si la promoción fue efectiva en estos datos. Para esto:
Key ideas:
GIT, un sistema de control de versiones ampliamente utilizado, no solo se aplica al desarrollo de software, sino que también es una herramienta poderosa en el análisis de datos.
Permite rastrear cada modificación realizada en el código y en los documentos, incluidos los notebooks.
Cada cambio es registrado como un “commit”, lo que proporciona un historial completo y auditable de las transformaciones realizadas en los datos.
La aplicación de GIT en proyectos de preparación de datos agrega un nivel adicional de transparencia y colaboració
Un esquema de git por Allison Horst @allison_horst
Inicio reproducible
Vamos a empezar el proyecto, dando los primeros pasos considerando que sea reproducible y transparente.
Uno de los productos del proyecto es un notebook de reporte del análisis. Para esto, iremos avanzando desde hoy.
La siguiente sesión, vamos a explorar los datos y empezar los primeros pasos en su análisis.

Curso Análisis de Datos - Sesión 1