No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Muestreo estratificado en Python

6/22
Recursos

Aportes 16

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Muestreo Estratificado en Python

Yo lo entendí de esta forma:

  1. Separa los datos por categorías (valores estratificados).
  2. Define las categorías que quieres en tú muestro y su proporción.
  3. Toma tantos datos como necesites de cada categoría para llegar a la proporción deseada.
  4. Junta esas muestras de cada categoría para tu muestreo final.

Explicación del ejemplo

Se crean dichas categorías a partir de la delegación y el tipo, esta información se almacena en la columna estratificado:

econdata['estratificado'] = econdata['delegacion'] + "," + econdata['tipo']
(econdata['estratificado'].value_counts() / len(econdata)).sort_values(ascending=False)

Con esto ya tenemos los datos por categorías (paso 1) y podemos ver su proporción real:

Para nuestro muestreo definimos las siguientes proporciones (paso 2):

  • Cuautémoc, Hotel: 0.5,
  • Cuautémoc, Museo: 0.2,
  • Venustiano Carranza, Hotel: 0.1,
  • Cuauhtémoc, Mercado: 0.1,
  • Venustiano Carranza, Mercado: 0.1

Para crear dicha proporción a partir de los datos originales se utiliza la siguiente función que encuentra el número de muestras necesario de cada categoría (paso 3), hace un sampleo simple dentro de dicha categoria y luego junta esas muestras (paso 4):

def data_estratificada(econdata, nombres_columnas_estrat, valores_estrat, prop_estrat, random_state=None):

  df_estrat = pd.DataFrame(columns = econdata.columns) 

  pos = -1
  for i in range(len(valores_estrat)):
    pos += 1
    if pos == len(valores_estrat) - 1: 
      ratio_len = len(econdata) - len(df_estrat)
    else:
      ratio_len = int(len(econdata) * prop_estrat[i])

    df_filtrado = econdata[econdata[nombres_columnas_estrat] ==valores_estrat[i]]
    df_temp = df_filtrado.sample(replace=True, n=ratio_len, random_state=random_state)

    df_estrat = pd.concat([df_estrat, df_temp]) 
  return df_estrat

Utiliza este código para crear el sampleo:

valores_estrat = ['Cuautémoc,Hotel', 'Cuautémoc,Museo', 'Venustiano Carranza,Hotel', 'Cuauhtémoc,Mercado','Venustiano Carranza,Mercado']
prop_estrat = [0.5, 0.2, 0.1, 0.1, 0.1]
df_estrat = data_estratificada(econdata, 'estratificado', valores_estrat, prop_estrat, random_state=42)
df_estrat

Adicionalmente, puedes usar este código para ver la proporción final de tus datos:

(df_estrat['estratificado'].value_counts()/len(df_estrat)).sort_values(ascending=False)

Si todo salió bien, deberías ver algo similar a esto:

Con esto ya tienes una muestra en las proporciones que deseábamos en un principio.

No me está quedando del todo claro que papel juega el random_state 😕

No había entendido bien el muestreo estratificado, pero al hacer el código ya lo entendí mejor.

  • Básicamente es muy útil cuando quieres indicar qué proporción de datos de una misma categoría quieres obtener.
  • Obtener el mismo muestreo (con las mismas condiciones) gracias al random_state.

Para entender este muestreo vamos a analizar que sucede cuando apliquemos un muestreo aleatorio simple vs un estratificado a los registros de Mercados que se encuentran en la delegacion Venustiano Carranza al cual me referire como el grupo “MVC” . Escogi el grupo MVS dado que de los 230 registros que tiene nuestro dataset (poblacion) solo el 4.7% (11 registros)son mercados en en la delegacion venustiano carranza, por lo cual es muy sensible a que estos registros desaparezcan al hacer un muestreo aleatorio. en cambio al hacer un estratificado podemos asignar una fraccion a recolectar de cada grupo y asi lograr que el grupo MVC no desaparesca ya que si queremos hacer un muestreo de 40 registros (n = 40) el 4.78% de las muestras seran del grupo MVC osea 1.8 registros que redondeando serian 2. en resumen al hacer un muestro n =40 la probabilidad de que se seleccione un registro del grupo MVC usando un muestreo aleatorio simple es muy bajo siendo probable que no se recoja ningun registro MVC al aplicar el muestreo, pero si en cambio usamos un muestreo estratificado recogeremos al menos 2 registros del grupo.
pd: sin ayuda de la comunidad no hubiera entendido la clase, gracias !

Creo que esta es una alternativa mas clara y menos larga:

data['strat'] = data['delegacion'] + " - " + data['tipo']

def stratified_sample(data, category, size):
  strat_values = data[category].unique()
  strat_props = (data[category].value_counts() / len(data))

  strat_sample = pd.DataFrame(columns = data.columns)

  for value in strat_values:
    strat_sample = pd.concat([strat_sample, data[(data[category] == value)].sample(n = round(strat_props[value]*size)) ])
  
  return strat_sample

strat_sample = stratified_sample(data, 'strat', 40)

Otro curso que se lleva todos los aplausos.

Me pareció un poco complicado el código de la profesora, por lo que me dí la tarea de escribir mi propio código:

def stratified_sampling(df, col_name, size, random_state=0):
    ratio_df = df[col_name].value_counts() / len(df) 
    
    strat_idx = []
    for val in ratio_df.index:
        n_val = int(ratio_df[val] * len(df)) 
        size_per_val = round(n_val * size)
        strat = df[df[col_name]==val].sample(size_per_val, random_state=random_state)
        strat_idx.append(strat.index)
 
    strat_idx = [i for sublist in strat_idx for i in sublist]

    return df.iloc[strat_idx]

​Aquí te doy un ejemplo de cómo realizar el muestreo estratificado en Python con la librería pandas:

Copiar código
import pandas as pd

Crear un dataframe

data = {'Género': ['Hombre', 'Mujer', 'Hombre', 'Mujer', 'Hombre', 'Mujer'],
        'Edad': [22, 38, 27, 35, 18, 45],
        'Ingresos': [50000, 100000, 75000, 120000, 90000, 80000]}
df = pd.DataFrame(data)

Crear estratos

strat_df = df.groupby(
strat_df =
'Género').apply(lambda x: x.sample(frac=0.3))

Muestra estratificada

print(strat_df)
  • En este ejemplo, se crea un marco de datos con tres columnas: Género, Edad e Ingresos. Luego, se agrupa el dataframe por la columna “Género” y se aplica una función de prueba de forma aleatoria a cada estrato con una fracción de 0.3. El resultado es una muestra estratificada donde el 30% de los elementos de cada estrato son seleccionados al azar.

La librería numpy también tiene la función ‘random.choice’ que permite elegir elementos al azar de una lista o una matriz de una manera similar.

Bueno, me costo mas de lo que me hubiera gustado la verda, pero aca les dejo el reto que hice con una dataset de dibujitos que encontre en kaggle (voy a hacer el muestreo estratificado, ya que los otros son simples dentro de todo :p)


# Creamos una variable nueva en el dataset
df_cartoons['estratificado'] = df_cartoons['Country']+','+df_cartoons['Original Channel']
(df_cartoons['estratificado'].value_counts() / len(df_cartoons)).sort_values(ascending=False)

def data_estratificada(df_cartoons, nombres_columnas_estrat, valores_estrat, prop_estrat, random_state=None):
    
    df_estrat = pd.DataFrame(columns = df_cartoons.columns) # Creamos un data frame vacío con los nombres de las columnas de df_cartoons

    pos = -1
    for i in range(len(valores_estrat)): # iteración sobre los valores estratificados
        pos += 1
        if pos == len(valores_estrat) - 1: 
            ratio_len = len(df_cartoons) - len(df_estrat) # si es la iteración final calcula el número de valores de salida tenga el mismo número de filas que de entrada
        else:
            ratio_len = int(len(df_cartoons) * prop_estrat[i]) # calcula el número de filas según la proporción deseada

        df_filtrado = df_cartoons[df_cartoons[nombres_columnas_estrat] ==valores_estrat[i]] # filtra los datos de origen según los valores seleccionados en la estratificación de datos
        df_temp = df_filtrado.sample(replace=True, n=ratio_len, random_state=random_state) # haz un sample de los datos filtrados usando la ratio que hemos calculado
        
        df_estrat = pd.concat([df_estrat, df_temp]) # junta las tablas de sample con la estratificada para producir el resultado final
        
    return df_estrat # Return the stratified, re-sampled data  

valores_estrat = ['United States,Netflix', 'United States,Cartoon Network', 'United States,Adult Swim']
prop_estrat = [0.6, 0.3, 0.1]
df_estrat = data_estratificada(df_cartoons, 'estratificado', valores_estrat, prop_estrat, random_state=42)
df_estrat

(df_estrat['estratificado'].value_counts()/len(df_estrat)).sort_values(ascending=False)

Si bien el código debería funcionar bien, es mejor comprobar el resultado, aquí una pequeña validación del objetivo

(df_estrat['estratificado'].value_counts() / len(df_estrat)).sort_values(ascending=False)

Cuautémoc,Hotel                0.5
Cuautémoc,Museo                0.2
Venustiano Carranza,Hotel      0.1
Cuauhtémoc,Mercado             0.1
Venustiano Carranza,Mercado    0.1
Name: estratificado, dtype: float64

En el contexto de muestreo, el parámetro “random_state” es utilizado para establecer una semilla para el generador de números aleatorios utilizado en la selección de muestras. Esto asegura que los resultados son reproducibles, ya que si utiliza el mismo valor para “random_state” en dos llamadas diferentes, se seleccionarán las mismas muestras. Sin embargo, si se utiliza un valor diferente para “random_state” en cada llamada, se seleccionarán muestras diferentes.

El siguiente código me regresa los valores de cada categoría ordenados del que mas veces aparece al que menos, seria una forma de reemplazar valores_estrat pero igual generar uno mismo la lista con los valores nos da un mayor control sobre las proporciones que le asignamos a cada categoría.

econdata[columna_strat].value_counts().index

Hay una imprecisión: El parámetro random_state (en este caso 42) la instructora indica que hace que las filas se tomen de 42 en 42 lo cual no tiene nada que ver con su verdadero comportamiento (pueden verificar que las filas no tienen ese intervalo). Lo que hace es servir de semilla para el generador de números aleatorios y es un parámetro opcional en la función sample.

Resultado final:

(df_estrat['estratificado'].value_counts() / len(econdata)).sort_values(ascending=False)

Le hice unos pequeños cambio al código, nos permite colocar el tamaño de la muestra.

def mi_estratificado (datos,columna,size,valores_estrat ,random_state = None):
  df_estrat = pd.DataFrame(columns=datos.columns)
  datos_en_col = datos[columna].unique()
  con = 0
  for i in datos_en_col :
    df_filtrado = datos[datos[columna] == datos_en_col[con]]
    df_temp = df_filtrado.sample(replace=True , n= int(valores_estrat[con] *size) ,random_state= random_state)
    df_estrat = pd.concat([df_estrat,df_temp])
    con += 1
  return df_estrat
### Para Probarlo
val_estrat = [0.5,0.2,0.1,0.1,0.1]
df_p = mi_estratificado(econodata,'estratificado',150,val_estrat, random_state=4)
print((df_p['estratificado'].value_counts()/len(df_p)).sort_values(ascending=False))
df_p

Muy bueno para dar esas proporciones al final y luego hacer otros estudios con los datos resultantes