- Validación de usuarios, fechas, tipos de datos y categorías del experimento.
- Prueba de Levene y t de Welch para comparar gasto entre convertidos.
- Prueba z bilateral para comparar tasas de conversión de A y B.
- Chi-cuadrado para fuente de tráfico y tipo de usuario frente a conversión.
- V de Cramér para valorar la magnitud de las asociaciones.
- Visualizaciones de conteos y porcentajes por categoría.
- Insight ejecutivo con recomendaciones, cautelas y próximos pasos.
Validando hipótesis de negocio con pruebas estadísticas
Un experimento con 40.000 usuarios compara dos versiones de landing page. La versión B alcanza una mayor conversión y un mayor gasto medio entre convertidos, con evidencia estadística y una lectura orientada a decisiones de negocio.
Descarga directa desde esta página, disponible sin Google Drive.
Resumen del experimento
La comparación se realiza con grupos próximos a 50/50: 19.982 usuarios en A y 20.018 en B. El análisis contiene siete pasos y utiliza un nivel de significancia de 0,05.
El proyecto transforma preguntas comerciales en hipótesis contrastables. Cada decisión combina una métrica observada, una prueba estadística y una lectura de su relevancia práctica.
La distinción entre todos los usuarios expuestos y quienes convirtieron evita interpretar el gasto de compradores como si fuera el gasto de toda la base. La separación entre conteos y tasas evita favorecer canales por su volumen.
| Indicador | Página A | Página B | Diferencia B − A |
|---|---|---|---|
| Usuarios expuestos | 19.982 | 20.018 | Grupos próximos a 50/50 |
| Conversiones | 2.512 | 3.194 | 682 observadas |
| Tasa de conversión | 12,57 % | 15,96 % | +3,38 pp |
| Gasto medio entre convertidos | USD 61,09 | USD 68,75 | +USD 7,66 |
| No convertidos | 17.470 | 16.824 | No equivale a abandono |
Los diferenciales se calculan antes de redondear. Por eso 15,96 − 12,57 no coincide exactamente con el diferencial de 3,38 pp. La diferencia de 682 conversiones es un conteo observado, no una estimación de conversiones incrementales causadas.
Diccionario de las nueve variables
| Campo | Definición | Tipo |
|---|---|---|
user_id | Identificador único del usuario. | Identificador |
date | Fecha de exposición a la página; convertida a datetime. | Temporal |
landing | Versión A o B mostrada al usuario. | Categórica |
region | Región geográfica del usuario. | Categórica |
dispositivo | Tipo de dispositivo utilizado. | Categórica |
traffic_source | Canal de llegada: Ads, Email, Organic o Referral. | Categórica |
user_type | Historial previo: Nuevo o Recurrente. | Categórica |
converted | 1 si el usuario convirtió; 0 en caso contrario. | Binaria |
gasto | Monto gastado; cero si no hubo conversión. | Numérica |
Proceso analítico, paso a paso
Una secuencia reproducible: validar el dataset, contrastar hipótesis, evaluar la magnitud de las diferencias y comunicar decisiones.
Paso 1. Cargar y validar los datos
El análisis comienza con la carga de landing_experiment.csv, la revisión de las primeras filas y la inspección de tipos y valores no nulos. La columna date se transforma a datetime.
La validación de user_id identifica 40.000 usuarios únicos y ningún duplicado. Las nueve columnas están completas. El periodo observado va del 1 al 28 de enero de 2026.
Se distingue una variable monetaria, gasto, de seis variables de análisis categórico: conversión, versión, región, dispositivo, fuente y tipo de usuario. Las frecuencias se exploran con una función de barras agrupadas.
| Control | Resultado |
|---|---|
| Filas / columnas | 40.000 / 9 |
| Usuarios duplicados | 0 |
| Valores nulos | 0 en las nueve columnas |
| Periodo | 01–28 de enero de 2026 |
| Conversión global | 5.706 de 40.000 · 14,265 % |
| Gasto global medio | USD 9,33 · incluye ceros |
Código y evidencia documentada
Celda 4 · código del análisis
# importar librerías
import pandas as pd
import numpy as np
from scipy.stats import chi2_contingency
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import ttest_ind
from statsmodels.stats.proportion import proportions_ztest
from scipy.stats import leveneCelda 5 · código del análisis
# cargar archivo
df = pd.read_csv('/datasets/landing_experiment.csv')
# df = pd.read_csv('https://drive.google.com/uc?export=download&id=15zGd_F-LzxCOcwNc74n_1x_o6OjAWYUV')Celda 7 · código del análisis
# mostrar las primeras 5 filas
df.head(5)| user_id | date | landing | region | dispositivo | traffic_source | user_type | converted | gasto | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 26f3052e-8500-44ea-8fff-06de65258abb | 2026-01-01 | A | Norte | Mobile | Recurrente | 1 | 38.08 | |
| 1 | 92378c09-4bbf-40c7-945e-82b84f392d22 | 2026-01-23 | A | Occidente | Mobile | Organic | Nuevo | 0 | 0.00 |
| 2 | a4397360-40e5-45d6-a7ff-dcb4da2c9a1f | 2026-01-01 | B | Centro | Mobile | Organic | Nuevo | 0 | 0.00 |
| 3 | 7ca3a26f-1e6c-44aa-9b09-b8cb01112956 | 2026-01-22 | A | Centro | Mobile | Ads | Nuevo | 0 | 0.00 |
| 4 | 8dc9593b-5b9c-479d-848b-a99493920419 | 2026-01-16 | A | Sur | Mobile | Organic | Nuevo | 0 | 0.00 |
Celda 8 · código del análisis
# información general
df.info()<class 'pandas.core.frame.DataFrame'> RangeIndex: 40000 entries, 0 to 39999 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 40000 non-null object 1 date 40000 non-null object 2 landing 40000 non-null object 3 region 40000 non-null object 4 dispositivo 40000 non-null object 5 traffic_source 40000 non-null object 6 user_type 40000 non-null object 7 converted 40000 non-null int64 8 gasto 40000 non-null float64 dtypes: float64(1), int64(1), object(7) memory usage: 2.7+ MB
Celda 10 · código del análisis
df['date'] = df['date'].astype('datetime64')
df.info()<class 'pandas.core.frame.DataFrame'> RangeIndex: 40000 entries, 0 to 39999 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 40000 non-null object 1 date 40000 non-null datetime64[ns] 2 landing 40000 non-null object 3 region 40000 non-null object 4 dispositivo 40000 non-null object 5 traffic_source 40000 non-null object 6 user_type 40000 non-null object 7 converted 40000 non-null int64 8 gasto 40000 non-null float64 dtypes: datetime64[ns](1), float64(1), int64(1), object(6) memory usage: 2.7+ MB
Celda 11 · código del análisis
variables_numericas = ['gasto']
variables_categoricas = ['converted', 'landing', 'region', 'dispositivo', 'traffic_source', 'user_type']Celda 15 · código del análisis
print(f"usuarios duplicados: {df['user_id'].duplicated().sum()}")
print(f"usuarios unicos: {df['user_id'].nunique()}")usuarios duplicados: 0 usuarios unicos: 40000
Celda 17 · código del análisis
# Resumen estadístico
df["date"].describe()count 40000 unique 28 top 2026-01-24 00:00:00 freq 1512 first 2026-01-01 00:00:00 last 2026-01-28 00:00:00 Name: date, dtype: object
Celda 18 · código del análisis
# Identificar rango temporal del experimento
print("Fecha mínima:", df["date"].min())
print("Fecha máxima:", df["date"].max())Fecha mínima: 2026-01-01 00:00:00 Fecha máxima: 2026-01-28 00:00:00
Celda 20 · código del análisis
# Resumen estadístico
df["gasto"].describe()count 40000.000000 mean 9.325554 std 25.667986 min 0.000000 25% 0.000000 50% 0.000000 75% 0.000000 max 303.680000 Name: gasto, dtype: float64
Celda 21 · código del análisis
# Resumen estadístico de usuarios que se convirtieron
df["converted"].describe()count 40000.00000 mean 0.14265 std 0.34972 min 0.00000 25% 0.00000 50% 0.00000 75% 0.00000 max 1.00000 Name: converted, dtype: float64
Celda 23 · código del análisis
# Explorar variables categóricas y cómo se distribuyen
print("\nConteo de categorías:")
print(df[variables_categoricas].describe())
Conteo de categorías:
converted
count 40000.00000
mean 0.14265
std 0.34972
min 0.00000
25% 0.00000
50% 0.00000
75% 0.00000
max 1.00000
Celda 24 · código del análisis
print(df[variables_categoricas].value_counts()) # frecuencia absoluta
print('____________________________')
print(df[variables_categoricas].value_counts(normalize=True)) # frecuencia relativa (porcentaje)
def graficar_cat(df, columnas):
for col in columnas:
otra_col = [c for c in columnas if c != col][0]
# Gráfico de barras agrupadas
plt.figure(figsize=(10, 5))
ax = sns.countplot(data=df, x=col, hue=otra_col)
# Agregar valores
for bar in ax.patches: # Recorrer todas las barras del gráfico
height = bar.get_height() # Obtener altura de barra (conteo de usuarios)
ax.text(x=bar.get_x()+bar.get_width() /2, # Establecer coordenada X del texto (centro de la barra)
y=height, # Establecer coordenada Y del texto
s=height, # Valor a mostrar
ha='center', # Alinear centro del texto en coordenada
va='bottom', # Alinear parte inferior del texto en coordenada
fontsize=11) # Tamaño de la fuente
# Añadir detalles
plt.title(f"Frecuencias absolutas de {col}", fontsize=15)
return plt.show()
graficar_cat(df, variables_categoricas)converted landing region dispositivo traffic_source user_type
0 B Norte Mobile Organic Nuevo 907
A Norte Mobile Organic Nuevo 861
Centro Mobile Organic Nuevo 793
B Centro Mobile Organic Nuevo 747
A Sur Mobile Organic Nuevo 659
...
1 A Occidente Desktop Referral Recurrente 3
B Oriente Desktop Referral Recurrente 3
A Oriente Desktop Referral Nuevo 2
Recurrente 2
Mobile Referral Recurrente 2
Length: 320, dtype: int64
____________________________
converted landing region dispositivo traffic_source user_type
0 B Norte Mobile Organic Nuevo 0.022675
A Norte Mobile Organic Nuevo 0.021525
Centro Mobile Organic Nuevo 0.019825
B Centro Mobile Organic Nuevo 0.018675
A Sur Mobile Organic Nuevo 0.016475
...
1 A Occidente Desktop Referral Recurrente 0.000075
B Oriente Desktop Referral Recurrente 0.000075
A Oriente Desktop Referral Nuevo 0.000050
Recurrente 0.000050
Mobile Referral Recurrente 0.000050
Length: 320, dtype: float64
Paso 2. Comparar el gasto promedio entre A y B
La comparación monetaria se realiza únicamente sobre usuarios que convirtieron: 2.512 en A y 3.194 en B. No corresponde al gasto medio de todos los usuarios expuestos.
H₀: μA = μB, igualdad de gasto medio entre convertidos.
H₁: μA ≠ μB, diferencia de gasto medio.
Levene detecta evidencia de varianzas diferentes (estadístico 29,176465; p < 0,001). Se aplica una prueba t de Welch con equal_var=False: t = −9,481011 y p < 0,001.
| Grupo de convertidos | n | Gasto medio |
|---|---|---|
| A | 2.512 | USD 61,09 |
| B | 3.194 | USD 68,75 |
| Diferencia B − A | — | +USD 7,66 · +12,54 % relativo |
Código y evidencia documentada
Celda 27 · código del análisis
# Gasto por versión
gasto_A = df[(df['converted']==1) & (df['landing']=='A')]['gasto']
gasto_B = df[(df['converted']==1) & (df['landing']=='B')]['gasto']
# Verificar cantidad de datos que tiene cada grupo
len(gasto_A), len(gasto_B)(2512, 3194)
Celda 30 · código del análisis
# Prueba de varianzas iguales
l_stat, p_value_var = levene(gasto_A, gasto_B)
print(f"Estadístico de Levene: {l_stat:.6f}")
print(f"Valor p: {p_value_var:.6f}")
alpha = 0.05 # umbral de significancia
if p_value_var < alpha:
print("Rechazamos la hipótesis nula: hay evidencia de varianzas diferentes (equal_var=False).")
else:
print("No rechazamos la hipótesis nula: no hay evidencia de varianzas diferentes (equal_var=True).")
print()
# Ejecutar prueba t y mostrar resultados
t_stat, p_value = ttest_ind(gasto_A, gasto_B, equal_var=False)
print(f"Estadístico t: {t_stat:.6f}")
print(f"Valor p: {p_value:.6f}")
print()
alpha = 0.05 # umbral de significancia
if p_value < alpha:
print("Rechazamos la hipótesis nula: hay evidencia de una diferencia.")
else:
print("No rechazamos la hipótesis nula: no hay evidencia suficiente de una diferencia.")
Estadístico de Levene: 29.176465 Valor p: 0.000000 Rechazamos la hipótesis nula: hay evidencia de varianzas diferentes (equal_var=False). Estadístico t: -9.481011 Valor p: 0.000000 Rechazamos la hipótesis nula: hay evidencia de una diferencia.
Celda 31 · código del análisis
# Calcular promedios
media_gasto_A = gasto_A.mean()
media_gasto_B = gasto_B.mean()
print('El gasto promedio de los usuarios que se convirtieron en clientes en la página A es: ', media_gasto_A)
print('El gasto promedio de los usuarios que se convirtieron en clientes en la página B es: ', media_gasto_B)
print('La diferencia es de: ', media_gasto_B - media_gasto_A)El gasto promedio de los usuarios que se convirtieron en clientes en la página A es: 61.0865724522293 El gasto promedio de los usuarios que se convirtieron en clientes en la página B es: 68.74536005009392 La diferencia es de: 7.658787597864624
Paso 3. Comparar las tasas de conversión
La conversión se evalúa sobre todos los usuarios de cada versión. El denominador es 19.982 usuarios para A y 20.018 para B; los numeradores son 2.512 y 3.194 conversiones.
H₀: pA = pB, igualdad de tasas.
H₁: pA ≠ pB, diferencia de tasas.
La prueba bilateral proportions_ztest() produce z = −9,67736 y p < 0,001. Las tasas son 12,57 % y 15,96 %. La diferencia exacta antes de redondear equivale a 3,3843 puntos porcentuales.
Código y evidencia documentada
Celda 37 · código del análisis
# Número de usuarios convertidos por página
conversiones = df.groupby('landing')['converted'].sum()
totales = df.groupby('landing')['converted'].count()
# Total de usuarios por página
conteos = [conversiones['A'], conversiones['B']]
num_observaciones = [totales['A'], totales['B']]
print("Usuarios convertidos por página:\n", conteos)
print("\nTotal de usuarios por página:\n", num_observaciones)
Usuarios convertidos por página: [2512, 3194] Total de usuarios por página: [19982, 20018]
Celda 38 · código del análisis
# Aplicar prueba
# Ejecutar el z-test para visualizar resultados
z_stat, p_value = proportions_ztest(conteos, num_observaciones)
print(f"Estadístico z: {z_stat:.5f}")
print(f"Valor P: {p_value:.7f}")
alpha = 0.05 # umbral de significancia
if p_value < alpha:
print("Rechazamos la hipótesis nula: hay evidencia de una diferencia.")
else:
print("No rechazamos la hipótesis nula: no hay evidencia suficiente de una diferencia.")
#---------------------------------------------------------------------------------------------
# Obtener tasa o porcentaje de éxito por grupo
tasa_A = conteos[0] / num_observaciones[0]
tasa_B = conteos[1] / num_observaciones[1]
print(f"\nTasa Pagina A: {tasa_A:.2%}")
print(f"Tasa Pagina B: {tasa_B:.2%}")
print(f"Diferencia: {tasa_B - tasa_A:.2%}")
Estadístico z: -9.67736 Valor P: 0.0000000 Rechazamos la hipótesis nula: hay evidencia de una diferencia. Tasa Pagina A: 12.57% Tasa Pagina B: 15.96% Diferencia: 3.38%
Paso 4. Relacionar fuente de tráfico y conversión
Se construye una tabla de contingencia entre traffic_source y converted. La prueba χ² evalúa independencia; la tabla normalizada por filas muestra la tasa dentro de cada canal.
H₀: fuente de tráfico y conversión son independientes.
H₁: existe asociación entre ambas variables.
| Fuente | Usuarios | Conversiones | Tasa |
|---|---|---|---|
| Ads | 11.935 | 1.759 | 14,74 % |
| 6.123 | 918 | 14,99 % | |
| Organic | 17.987 | 2.480 | 13,79 % |
| Referral | 3.955 | 549 | 13,88 % |
Con p = 0,034 se rechaza H₀ al nivel 0,05. La V de Cramér de 0,014716 indica una asociación de magnitud pequeña. Organic tiene más conversiones absolutas por su volumen, pero no la mayor tasa.
Código y evidencia documentada
Celda 43 · código del análisis
# Aplicar prueba
# Construir tabla de contingencia
tabla = pd.crosstab(df['traffic_source'], df['converted'])
print(tabla)
# Ejecutar prueba chi-cuadrada
chi2_stat, p_value, dof, expected = chi2_contingency(tabla)
print(f"\nEstadístico chi-cuadrado: {chi2_stat:.3f}")
print(f"Valor P: {p_value:.3f}")
# Intepretar resultados
alpha = 0.05 # umbral de significancia
if p_value < alpha:
print("Rechazamos la hipótesis nula: hay evidencia de asociación entre las variables.")
else:
print("No rechazamos la hipótesis nula: no hay evidencia suficiente de asociación entre las variables.")
print() #salto de línea
# Tabla de contingencia normalizada
print(pd.crosstab(df['traffic_source'], df['converted'], normalize='index') * 100)
converted 0 1 traffic_source Ads 10176 1759 Email 5205 918 Organic 15507 2480 Referral 3406 549 Estadístico chi-cuadrado: 8.662 Valor P: 0.034 Rechazamos la hipótesis nula: hay evidencia de asociación entre las variables. converted 0 1 traffic_source Ads 85.261835 14.738165 Email 85.007349 14.992651 Organic 86.212264 13.787736 Referral 86.118837 13.881163
Celda 44 · código del análisis
# Función mejorada y generalizable para calcular V de Cramér
from scipy.stats import chi2_contingency
def cramer_v(df, col_cat):
for i, col_1 in enumerate(col_cat):
for col_2 in col_cat[i+1:]:
print(f"\nCorrelacion de la variable categorica {col_1}")
# tabla de contingencia
tabla = pd.crosstab(df[col_1], df[col_2])
# calcular chi-cuadrado
chi2, p_value, dof, expected = chi2_contingency(tabla)
# calcular coeficiente V de Cramér
n = tabla.values.sum()
v = np.sqrt(chi2 / (n * (min(tabla.shape) -1)))
print(f"\ncon la variable {col_2}:")
print(v)
print('_________________________________')
# Aplicar V de Cramér en variables categoricas
cramer_v(df, variables_categoricas)Correlacion de la variable categorica converted con la variable landing: 0.04831532667804828 _________________________________ Correlacion de la variable categorica converted con la variable region: 0.010278341976512772 _________________________________ Correlacion de la variable categorica converted con la variable dispositivo: 0.04101098787148789 _________________________________ Correlacion de la variable categorica converted con la variable traffic_source: 0.014715730394205666 _________________________________ Correlacion de la variable categorica converted con la variable user_type: 0.0035828932074786447 _________________________________ Correlacion de la variable categorica landing con la variable region: 0.014057606516552168 _________________________________ Correlacion de la variable categorica landing con la variable dispositivo: 0.0007391750702443745 _________________________________ Correlacion de la variable categorica landing con la variable traffic_source: 0.009446259799705797 _________________________________ Correlacion de la variable categorica landing con la variable user_type: 0.00128853719463511 _________________________________ Correlacion de la variable categorica region con la variable dispositivo: 0.009504270138405066 _________________________________ Correlacion de la variable categorica region con la variable traffic_source: 0.008943088758917117 _________________________________ Correlacion de la variable categorica region con la variable user_type: 0.006016527715953276 _________________________________ Correlacion de la variable categorica dispositivo con la variable traffic_source: 0.006810081658582942 _________________________________ Correlacion de la variable categorica dispositivo con la variable user_type: 0.0004143842211088881 _________________________________ Correlacion de la variable categorica traffic_source con la variable user_type: 0.012103142185121284 _________________________________
Paso 5. Relacionar tipo de usuario y conversión
Se contrasta si los usuarios nuevos y recurrentes tienen diferente composición de conversión mediante una tabla de contingencia y una prueba χ².
H₀: tipo de usuario y conversión son independientes.
H₁: existe asociación entre ambas variables.
| Tipo | Usuarios | Conversiones | Tasa |
|---|---|---|---|
| Nuevo | 26.033 | 3.738 | 14,36 % |
| Recurrente | 13.967 | 1.968 | 14,09 % |
Las tasas observadas son 14,36 % para Nuevo y 14,09 % para Recurrente. Con p = 0,474 no se rechaza H₀. V de Cramér = 0,003583.
Código y evidencia documentada
Celda 48 · código del análisis
# Aplicar prueba
# Construir tabla de contingencia
tabla = pd.crosstab(df['user_type'], df['converted'])
print(tabla)converted 0 1 user_type Nuevo 22295 3738 Recurrente 11999 1968
Celda 49 · código del análisis
# Ejecutar prueba chi-cuadrada
chi2_stat, p_value, dof, expected = chi2_contingency(tabla)
print(f"\nEstadístico chi-cuadrado: {chi2_stat:.3f}")
print(f"Valor P: {p_value:.3f}")
# Intepretar resultados
alpha = 0.05 # umbral de significancia
if p_value < alpha:
print("Rechazamos la hipótesis nula: hay evidencia de asociación entre las variables.")
else:
print("No rechazamos la hipótesis nula: no hay evidencia suficiente de asociación entre las variables.")
print() #salto de línea
# Tabla de contingencia normalizada
print(pd.crosstab(df['user_type'], df['converted'], normalize='index') * 100)
Estadístico chi-cuadrado: 0.513 Valor P: 0.474 No rechazamos la hipótesis nula: no hay evidencia suficiente de asociación entre las variables. converted 0 1 user_type Nuevo 85.641301 14.358699 Recurrente 85.909644 14.090356
Paso 6. Visualizar los resultados categóricos
La función reutilizable relacion_2() genera dos paneles por variable: barras agrupadas con conteos y barras apiladas con porcentajes. Las tasas se obtienen con pd.crosstab(..., normalize="index").
El análisis visual cubre fuente de tráfico, tipo de usuario, versión de landing, región y dispositivo. Distingue cuánto volumen aporta una categoría de qué proporción de sus usuarios convierte.
El notebook conserva 13 salidas de figuras. Dos son repeticiones idénticas de las comparaciones de fuente y tipo de usuario; la galería reúne las 11 figuras distintas, sin duplicar información.
Código y evidencia documentada
Celda 54 · código del análisis
def relacion_2(df, variables_categoricas, target='converted'): # FUNCION GENERALIZABLE VISUALIZACIÓN DE RELACIONES ENTRE VARIABLES CATEGORICAS
for col in variables_categoricas:
if col == target:
continue
tab = pd.crosstab(df[col], df[target], normalize='index') * 100
fig, axes = plt.subplots(1, 2, figsize=(15, 5))
# Barras agrupadas
ax = sns.countplot(data=df, x=col, hue=target, palette=["#95D5B2", "#F4A261"], ax=axes[0], order=tab.index)
# Agregar valores
for bar in ax.patches: # Recorrer todas las barras del gráfico
height = bar.get_height() # Obtener altura de barra (conteo de usuarios)
ax.text(
x=bar.get_x() + bar.get_width() / 2, # Establecer coordenada X del texto (centro de la barra)
y=height, # Establecer coordenada Y del texto
s=height, # Valor a mostrar
ha='center', # Alinear centro del texto en coordenada
va='bottom', # Alinear parte inferior del texto en coordenada
fontsize=11 # Tamaño de la fuente
)
axes[0].set_title(f"Conversiones por {col}", fontsize=15)
axes[0].set_xlabel(f"{col}", fontsize=14)
axes[0].set_ylabel('Cantidad de usuarios', fontsize=14)
axes[0].legend(title='¿Convirtió?', labels=['No (0)', 'Sí (1)'])
# Barras apiladas
tab.plot(kind='bar', stacked=True, color=["#8ecae6", "#95d5b2"], ax=axes[1])
axes[1].set_title(f"Tasa de conversión por {col}", fontsize=15)
axes[1].set_xlabel(f"{col}", fontsize=14)
axes[1].set_ylabel('Porcentaje de usuarios', fontsize=14)
axes[1].set_xlim(-1, len(tab.index))
axes[1].legend(title='¿Convirtió?', labels=['No (0)', 'Sí (1)'], loc='upper right')
axes[1].tick_params(axis='x', rotation=0)
plt.tight_layout()
plt.show()
traffic_source = ['traffic_source']
relacion_2(df, traffic_source, target='converted')Celda 57 · código del análisis
user_type = ['user_type']
relacion_2(df, user_type, target='converted')Celda 59 · código del análisis
relacion_2(df, variables_categoricas, target='converted')Paso 7. Traducir resultados en decisiones de negocio
El insight ejecutivo integra las pruebas estadísticas, las tasas observadas y las visualizaciones. La versión B se presenta como candidata preferente para adopción, sujeta a validación de costos y condiciones del experimento.
La asociación por canal es pequeña y no sostiene una decisión presupuestaria aislada. El tipo de usuario no muestra evidencia suficiente de asociación con conversión. La segmentación requiere preguntas y métricas adicionales.
Antes de escalar, corresponde documentar la asignación experimental, revisar indicadores operativos y comprobar que el beneficio neto sea positivo.
Código y evidencia documentada
Celda 64 · código del análisis
'Link del repositorio: https://github.com/andaba2101/marketing_digital_ecommerce_validaci-n_hipotesis.git''Link del repositorio: https://github.com/andaba2101/marketing_digital_ecommerce_validaci-n_hipotesis.git'
Resultados que orientan la decisión
La evidencia favorece a B en conversión y gasto entre convertidos. Las segmentaciones requieren distinguir volumen, asociación estadística y tamaño práctico.
12,57 % en A frente a 15,96 % en B. Prueba z: −9,67736; p < 0,001.
La diferencia favorece a B: +3,38 pp y +26,92 % relativo. El resultado corresponde a la muestra y al periodo evaluado.
Priorizar B como candidata a implementación, revisando las condiciones de asignación y la estabilidad del resultado.
USD 61,09 en A frente a USD 68,75 en B. Welch: t = −9,481011; p < 0,001.
El diferencial es +USD 7,66, equivalente a +12,54 % relativo. Solo se comparan usuarios que convirtieron.
Contrastar el valor observado con costos de desarrollo, operación y margen. No asumir que gasto equivale a beneficio neto.
Fuente–conversión: χ² = 8,662; p = 0,034; V = 0,014716.
Hay evidencia al nivel 0,05, pero la magnitud es pequeña. Organic aporta mayor volumen; Email tiene la mayor tasa observada: 14,99 %.
No redistribuir presupuesto basándose solo en este contraste. Incorporar costo de adquisición, ingresos por canal y atribución.
Tipo–conversión: χ² = 0,513; p = 0,474; V = 0,003583.
Nuevo registra 14,36 % y Recurrente 14,09 %. El análisis no rechaza independencia.
No justificar una estrategia diferenciada únicamente con este resultado; otras necesidades o métricas pueden respaldar segmentación.
| Pregunta | Prueba | Estadístico | p documentado | Decisión (α = 0,05) |
|---|---|---|---|---|
| Varianzas del gasto | Levene | 29,176465 | < 0,001 | Evidencia de varianzas distintas |
| Gasto medio entre convertidos | t de Welch | −9,481011 | < 0,001 | Rechazar H₀ |
| Conversión de A frente a B | z de dos proporciones | −9,67736 | < 0,001 | Rechazar H₀ |
| Fuente de tráfico y conversión | χ² de independencia | 8,662 | 0,034 | Rechazar H₀ |
| Tipo de usuario y conversión | χ² de independencia | 0,513 | 0,474 | No rechazar H₀ |
Las pruebas principales son bilaterales o de independencia. Las cifras se reproducen con la precisión guardada; las salidas nulas por redondeo se muestran como p < 0,001.
| Métrica descriptiva derivada | Página A | Página B | Diferencial |
|---|---|---|---|
| Gasto por usuario expuesto | USD 7,68 | USD 10,97 | +USD 3,29 |
| Cambio relativo frente a A | Referencia | +42,83 % | No es ROI ni beneficio neto |
Cálculo complementario: tasa de conversión × gasto medio entre convertidos. Incluye a no convertidos con gasto cero. Resume los resultados del periodo y no constituye una nueva prueba estadística.
Las 15 asociaciones exploratorias de V de Cramér
| Variable 1 | Variable 2 | V de Cramér |
|---|---|---|
converted | landing | 0,048315 |
converted | region | 0,010278 |
converted | dispositivo | 0,041011 |
converted | traffic_source | 0,014716 |
converted | user_type | 0,003583 |
landing | region | 0,014058 |
landing | dispositivo | 0,000739 |
landing | traffic_source | 0,009446 |
landing | user_type | 0,001289 |
region | dispositivo | 0,009504 |
region | traffic_source | 0,008943 |
region | user_type | 0,006017 |
dispositivo | traffic_source | 0,006810 |
dispositivo | user_type | 0,000414 |
traffic_source | user_type | 0,012103 |
La función calcula todos los pares entre seis variables categóricas. Esta tabla reporta magnitud; no aporta por sí sola causalidad, relevancia económica o pruebas de significancia para cada par.
Alcance metodológico y límites de interpretación
- Las conclusiones se refieren a 40.000 usuarios durante el 1–28 de enero de 2026; no garantizan estabilidad en otros periodos o poblaciones.
- Los grupos tienen tamaños próximos, pero eso no demuestra aleatorización ni balance en todas las variables. El código documentado no muestra una auditoría formal de asignación, contaminación o sample ratio mismatch.
- La t de Welch compara gasto condicionado a converted = 1. Como la conversión también puede depender de la versión, este contraste no debe interpretarse como un efecto causal aislado del diseño sobre el gasto de un comprador fijo.
- El estadístico negativo de t o z refleja que las funciones reciben A antes que B; no implica que B tenga un efecto negativo.
- Los p-values impresos como 0,000000 o 0,0000000 están redondeados, no son probabilidades exactamente cero. Se presentan como p < 0,001.
- Rechazar H₀ no significa que una diferencia sea imposible por azar. No rechazar H₀ tampoco prueba igualdad o ausencia de cualquier efecto.
- V de Cramér es un coeficiente entre 0 y 1, no un porcentaje de conversión. V = 0,0147 no equivale a una mejora de 1,47 %.
- Se utiliza α = 0,05 en varios contrastes sin ajuste documentado por comparaciones múltiples. La asociación por fuente debe leerse como resultado exploratorio, especialmente por su p = 0,034.
- El notebook no reporta intervalos de confianza, análisis de potencia, efecto mínimo detectable ni una validación financiera de implementación.
- Los contrastes globales no establecen que B mejore por igual en cada región, dispositivo, fuente o tipo de usuario. Falta evaluar interacciones y heterogeneidad del resultado.
- El gasto por expuesto es una métrica descriptiva derivada de conteos y medias guardadas. No se ha realizado una prueba adicional sobre esa métrica ni se ha estimado margen, ROI o valor de vida del cliente.
Prioridades antes de escalar
Una implementación informada combina la señal experimental con controles operativos y criterios financieros.
Documentar aleatorización, equilibrio de grupos y exposición consistente. Analizar la estabilidad por día y la heterogeneidad entre segmentos.
Contrastar conversiones y gasto con margen, costos de implementación y operación. Definir indicadores de riesgo y seguimiento.
Priorizar B de forma controlada y monitorear conversión, gasto por expuesto y experiencia de usuario. Conservar criterios para revertir si el desempeño cambia.
Visualizaciones del experimento
La galería conserva las once figuras distintas del análisis: seis exploraciones de frecuencia y cinco comparaciones de conteos con tasas. Cada figura mantiene su contenido, colores y ejes originales.
Las figuras pueden ampliarse para revisar etiquetas y proporciones. Las comparaciones de dos paneles ocupan el ancho completo; el fondo blanco mantiene el contraste en ambos modos.
Conversión global por versión
Barras de converted, diferenciadas por landing. La figura muestra el número de usuarios con y sin conversión para las dos versiones.
El total convertido no es una tasa: cada versión requiere su propio denominador.
Distribución de usuarios entre A y B
Barras de landing, diferenciadas por converted. Los grupos tienen tamaños próximos y B registra 3.194 conversiones frente a 2.512 en A.
Tamaños cercanos no bastan para demostrar aleatorización o balance en características.
Regiones y composición de conversión
Conteos por region, diferenciados por converted. Complementa la revisión de categorías y el análisis exploratorio de asociaciones.
La figura expresa volumen. No se presenta una prueba específica de efecto de B dentro de cada región.
Dispositivos y composición de conversión
Conteos por dispositivo, diferenciados por converted. La distribución permite examinar la composición de usuarios y conversiones.
El coeficiente describe asociación global con conversión; no confirma causalidad ni significancia para subgrupos.
Fuente de tráfico: conteos de conversión
Barras de traffic_source, diferenciadas por converted. Organic concentra 17.987 usuarios y 2.480 conversiones, más volumen que los otros canales.
Organic no presenta la mayor tasa: su volumen absoluto debe separarse de la proporción que convierte.
Tipo de usuario: conteos de conversión
Conteos de user_type, diferenciados por converted. La base contiene 26.033 usuarios nuevos y 13.967 recurrentes.
Más conversiones nuevas reflejan también un segmento de mayor tamaño, no una tasa significativamente superior.
Tráfico: volumen frente a tasa
Figura de dos paneles: conteos agrupados y porcentajes dentro de cada fuente. Ads convierte al 14,74 %, Email al 14,99 %, Organic al 13,79 % y Referral al 13,88 %.
χ² = 8,662; p = 0,034; V = 0,014716. Asociación pequeña; el gráfico no determina rentabilidad del canal.
Historial: volumen frente a tasa
Conteos absolutos y barras apiladas normalizadas por tipo de usuario. La mayor cantidad de compradores nuevos contrasta con tasas próximas entre grupos.
χ² = 0,513; p = 0,474. No hay evidencia suficiente de asociación en el contraste global.
Landing A/B: volumen frente a tasa
Los dos paneles comparan la conversión entre versiones. La ventaja de B se observa en número de conversiones y en proporción de usuarios convertidos.
Diferencial B − A: +3,38 pp. La prueba z global reporta p < 0,001; no se muestran aquí intervalos de confianza.
Región: volumen frente a tasa
Conteos agrupados y porcentajes normalizados dentro de cada region. Permite distinguir el tamaño de cada segmento de su composición de conversión.
V de Cramér global = 0,010278. No se calculó una prueba de interacción región × landing en el notebook.
Dispositivo: volumen frente a tasa
Conteos absolutos y barras apiladas por dispositivo. Cada barra porcentual utiliza como denominador los usuarios de esa categoría.
V de Cramér global = 0,041011. Las decisiones de experiencia por dispositivo requieren análisis adicional.
Notebook y documentación
El notebook reúne la validación de datos, el código de las pruebas, las visualizaciones y el insight ejecutivo del experimento A/B.