Proyecto 8 · Sprint 9 · Experimento A/B

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.

PythonPandasNumPySciPyStatsmodelsSeabornMatplotlib
Explorar las gráficas

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.

Alcance del análisis
  • 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.
Aporte analítico

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.

El p-value responde a la evidencia frente a una hipótesis. El tamaño del diferencial, los costos y los riesgos determinan su utilidad para el negocio.
+26,92 %
mejora relativa de conversión
15,96 % frente a 12,57 %
+12,54 %
gasto medio entre convertidos
USD 68,75 frente a USD 61,09
5.706
conversiones observadas
14,265 % de los 40.000 usuarios
11 figuras
visualizaciones originales distintas
Dos repeticiones de ejecución consolidadas
Página A frente a página B
IndicadorPágina APágina BDiferencia B − A
Usuarios expuestos19.98220.018Grupos próximos a 50/50
Conversiones2.5123.194682 observadas
Tasa de conversión12,57 %15,96 %+3,38 pp
Gasto medio entre convertidosUSD 61,09USD 68,75+USD 7,66
No convertidos17.47016.824No 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
CampoDefiniciónTipo
user_idIdentificador único del usuario.Identificador
dateFecha de exposición a la página; convertida a datetime.Temporal
landingVersión A o B mostrada al usuario.Categórica
regionRegión geográfica del usuario.Categórica
dispositivoTipo de dispositivo utilizado.Categórica
traffic_sourceCanal de llegada: Ads, Email, Organic o Referral.Categórica
user_typeHistorial previo: Nuevo o Recurrente.Categórica
converted1 si el usuario convirtió; 0 en caso contrario.Binaria
gastoMonto 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.

01

Paso 1. Cargar y validar los datos

40.000 usuarios únicos · 9 columnas · 0 duplicados

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.

ControlResultado
Filas / columnas40.000 / 9
Usuarios duplicados0
Valores nulos0 en las nueve columnas
Periodo01–28 de enero de 2026
Conversión global5.706 de 40.000 · 14,265 %
Gasto global medioUSD 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 levene

Celda 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 Email 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
02

Paso 2. Comparar el gasto promedio entre A y B

Welch · USD 7,66 más por usuario convertido

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 convertidosnGasto medio
A2.512USD 61,09
B3.194USD 68,75
Diferencia B − A—+USD 7,66 · +12,54 % relativo
Se rechaza H₀ al nivel 0,05. B presenta mayor gasto observado entre quienes convierten. La decisión de implementación requiere contrastar este diferencial con costos.
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
03

Paso 3. Comparar las tasas de conversión

Prueba z · +3,38 puntos porcentuales a favor de B

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.

Se rechaza H₀. El aumento es +3,38 pp en términos absolutos y +26,92 % relativo a la tasa de A. No son dos formas intercambiables de expresar el mismo porcentaje.
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%
04

Paso 4. Relacionar fuente de tráfico y conversión

χ² = 8,662 · p = 0,034 · V = 0,0147

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.

FuenteUsuariosConversionesTasa
Ads11.9351.75914,74 %
Email6.12391814,99 %
Organic17.9872.48013,79 %
Referral3.95554913,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.

Significancia estadística no equivale a impacto comercial. Estos resultados no justifican por sí solos redistribuir presupuesto: faltan costos de adquisición, valor por canal y rentabilidad.
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
_________________________________
05

Paso 5. Relacionar tipo de usuario y conversión

χ² = 0,513 · p = 0,474 · no se rechaza H₀

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.

TipoUsuariosConversionesTasa
Nuevo26.0333.73814,36 %
Recurrente13.9671.96814,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.

No rechazar H₀ significa que no hay evidencia suficiente de asociación en este análisis. No demuestra igualdad exacta entre segmentos ni que toda personalización sea innecesaria.
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
06

Paso 6. Visualizar los resultados categóricos

Conteos absolutos y porcentajes dentro de cada categoría

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')
07

Paso 7. Traducir resultados en decisiones de negocio

B reúne mayor conversión y gasto entre convertidos

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.

Decisión propuesta: priorizar B con seguimiento de conversión, gasto por expuesto, margen y estabilidad por segmento. Los resultados observados no son una garantía de desempeño futuro.
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.

B convierte a una mayor proporción de usuarios
Evidencia

12,57 % en A frente a 15,96 % en B. Prueba z: −9,67736; p < 0,001.

Interpretación

La diferencia favorece a B: +3,38 pp y +26,92 % relativo. El resultado corresponde a la muestra y al periodo evaluado.

Implicación

Priorizar B como candidata a implementación, revisando las condiciones de asignación y la estabilidad del resultado.

B presenta mayor gasto entre convertidos
Evidencia

USD 61,09 en A frente a USD 68,75 en B. Welch: t = −9,481011; p < 0,001.

Interpretación

El diferencial es +USD 7,66, equivalente a +12,54 % relativo. Solo se comparan usuarios que convirtieron.

Implicación

Contrastar el valor observado con costos de desarrollo, operación y margen. No asumir que gasto equivale a beneficio neto.

El canal muestra asociación pequeña
Evidencia

Fuente–conversión: χ² = 8,662; p = 0,034; V = 0,014716.

Interpretación

Hay evidencia al nivel 0,05, pero la magnitud es pequeña. Organic aporta mayor volumen; Email tiene la mayor tasa observada: 14,99 %.

Implicación

No redistribuir presupuesto basándose solo en este contraste. Incorporar costo de adquisición, ingresos por canal y atribución.

El historial no aporta evidencia suficiente
Evidencia

Tipo–conversión: χ² = 0,513; p = 0,474; V = 0,003583.

Interpretación

Nuevo registra 14,36 % y Recurrente 14,09 %. El análisis no rechaza independencia.

Implicación

No justificar una estrategia diferenciada únicamente con este resultado; otras necesidades o métricas pueden respaldar segmentación.

Mapa de contrastes estadísticos
PreguntaPruebaEstadísticop documentadoDecisión (α = 0,05)
Varianzas del gastoLevene29,176465< 0,001Evidencia de varianzas distintas
Gasto medio entre convertidost de Welch−9,481011< 0,001Rechazar H₀
Conversión de A frente a Bz de dos proporciones−9,67736< 0,001Rechazar H₀
Fuente de tráfico y conversiónχ² de independencia8,6620,034Rechazar H₀
Tipo de usuario y conversiónχ² de independencia0,5130,474No 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.

Lectura económica por usuario expuesto
Métrica descriptiva derivadaPágina APágina BDiferencial
Gasto por usuario expuestoUSD 7,68USD 10,97+USD 3,29
Cambio relativo frente a AReferencia+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 1Variable 2V de Cramér
convertedlanding0,048315
convertedregion0,010278
converteddispositivo0,041011
convertedtraffic_source0,014716
converteduser_type0,003583
landingregion0,014058
landingdispositivo0,000739
landingtraffic_source0,009446
landinguser_type0,001289
regiondispositivo0,009504
regiontraffic_source0,008943
regionuser_type0,006017
dispositivotraffic_source0,006810
dispositivouser_type0,000414
traffic_sourceuser_type0,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.

Validar el experimento

Documentar aleatorización, equilibrio de grupos y exposición consistente. Analizar la estabilidad por día y la heterogeneidad entre segmentos.

Cuantificar el beneficio neto

Contrastar conversiones y gasto con margen, costos de implementación y operación. Definir indicadores de riesgo y seguimiento.

Desplegar con 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.

11 de 11 figuras
Figura 01 / 11 · celda 24

Conversión global por versión

5.706 conversiones · 34.294 no conversiones

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.

Figura 02 / 11 · celda 24

Distribución de usuarios entre A y B

A: 19.982 · B: 20.018 usuarios

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.

Figura 03 / 11 · celda 24

Regiones y composición de conversión

Exploración geográfica · V = 0,010278

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.

Figura 04 / 11 · celda 24

Dispositivos y composición de conversión

Exploración por dispositivo · V = 0,041011

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.

Figura 05 / 11 · celda 24

Fuente de tráfico: conteos de conversión

Organic: 2.480 conversiones observadas

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.

Figura 06 / 11 · celda 24

Tipo de usuario: conteos de conversión

Nuevo: 3.738 · Recurrente: 1.968 conversiones

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.

Figura 07 / 11 · celda 54

Tráfico: volumen frente a tasa

Email: 14,99 % · Organic: 13,79 %

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.

Figura 08 / 11 · celda 57

Historial: volumen frente a tasa

Nuevo: 14,36 % · Recurrente: 14,09 %

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.

Figura 09 / 11 · celda 59

Landing A/B: volumen frente a tasa

B: 15,96 % · A: 12,57 %

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.

Figura 10 / 11 · celda 59

Región: volumen frente a tasa

Comparación descriptiva de categorías regionales

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.

Figura 11 / 11 · celda 59

Dispositivo: volumen frente a tasa

Comparación descriptiva de categorías de dispositivo

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.

Proyecto 8 · Validando hipótesis de negocio con pruebas estadísticas

S9_Version_Student_Proyecto_Landing_Experiment.ipynb
Andrés Bahamon · TripleTen Data Analytics · Sprint 9

Fuente analítica: landing_experiment.csv, resultados y código documentados en el notebook del Sprint 9. Periodo: 1–28 de enero de 2026. Base: 40.000 usuarios únicos. Las mejoras relativas y el gasto por expuesto son cálculos descriptivos derivados de los conteos y promedios reportados. Las figuras repetidas de fuente e historial se presentan una sola vez. No se agregan pruebas inferenciales nuevas. Portafolio preparado el 5 de octubre de 2026.

Figura del experimento

Figura ampliada

↑