Análisis de Factores Asociados al Éxito Profesional en Estudiantes Universitarios

Fundamentos de Programación para la Ciencia de Datos e IA
Especialización en Ciencia de Datos e IA — UTEC
Tomás Clavijo · María Martinote · Yuri Martin Biardo

1. Introducción

Este trabajo analiza los factores académicos, personales y sociales asociados al éxito profesional temprano en egresados universitarios. A partir de un dataset sintético de 5.000 registros, se aplican técnicas de análisis exploratorio y estadístico para identificar relaciones entre variables como el campo de estudio, el nivel universitario, el rendimiento académico, las habilidades sociales y los resultados laborales (salario inicial, número de ofertas y satisfacción profesional).

El enfoque es descriptivo: no se establecen relaciones causales, sino que se caracterizan patrones en el dataset que puedan orientar interpretaciones e investigaciones futuras con datos reales.

2. Descripción del Dataset

El dataset (Education & Career Success, Kaggle) contiene 5.000 registros y 18 variables distribuidas en cuatro dimensiones:

  • Datos personales: edad (18–30 años), género, identificador.
  • Desempeño académico: GPA universitario (2.0–4.0), GPA secundaria, puntaje SAT, ranking universitario, campo de estudio (7 disciplinas).
  • Habilidades y actividades: pasantías, proyectos, certificaciones, soft skills score, networking score.
  • Resultados profesionales: salario inicial, número de ofertas laborales, satisfacción profesional, nivel de puesto actual.

Al ser sintético, el dataset permite explorar relaciones estructuradas sin comprometer datos reales, aunque esto limita la validez externa de los hallazgos.

3. Preguntas de Investigación

  1. ¿Existen diferencias en el salario inicial según el campo de estudio y el nivel universitario?
  2. ¿Se observa una brecha salarial asociada al género?
  3. ¿El puntaje de networking se asocia a mejores resultados salariales?
  4. ¿Qué disciplinas presentan mayor empleabilidad y mayor satisfacción profesional?
  5. ¿Existe correlación lineal entre el rendimiento académico (GPA) y el salario inicial?
In [14]:
# Importación de librerías

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from scipy import stats
In [16]:
# Importación de datos (Si el entorno se reinicia, el archivo se debe volver a subir)

#ruta = '//content/drive/MyDrive/DATASET/education_career_success.csv' # (Ruta Maria)
ruta ='/content/education_career_success (1).csv' # (Ruta Yuri)
#ruta ='//content/education_career_success.csv' # (Ruta Tomas)

datos = pd.read_csv(ruta)
datos.head()
Out[16]:
Student_ID Age Gender High_School_GPA SAT_Score University_Ranking University_GPA Field_of_Study Internships_Completed Projects_Completed Certifications Soft_Skills_Score Networking_Score Job_Offers Starting_Salary Career_Satisfaction Years_to_Promotion Current_Job_Level Work_Life_Balance Entrepreneurship
0 S00001 24 Male 3.58 1052 291 3.96 Arts 3 7 2 9 8 5 27200.0 4 5 Entry 7 No
1 S00002 21 Other 2.52 1211 112 3.63 Law 4 7 3 8 1 4 25000.0 1 1 Mid 7 No
2 S00003 28 Female 3.42 1193 715 2.63 Medicine 4 8 1 1 9 0 42400.0 9 3 Entry 7 No
3 S00004 25 Male 2.43 1497 170 2.81 Computer Science 3 9 1 10 6 1 57400.0 7 5 Mid 5 No
4 S00005 22 Male 2.08 1012 599 2.48 Engineering 4 6 4 10 9 4 47600.0 9 5 Entry 2 No
In [17]:
# Conociendo el conjunto de datos
datos.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5000 entries, 0 to 4999
Data columns (total 20 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   Student_ID             5000 non-null   object 
 1   Age                    5000 non-null   int64  
 2   Gender                 5000 non-null   object 
 3   High_School_GPA        5000 non-null   float64
 4   SAT_Score              5000 non-null   int64  
 5   University_Ranking     5000 non-null   int64  
 6   University_GPA         5000 non-null   float64
 7   Field_of_Study         5000 non-null   object 
 8   Internships_Completed  5000 non-null   int64  
 9   Projects_Completed     5000 non-null   int64  
 10  Certifications         5000 non-null   int64  
 11  Soft_Skills_Score      5000 non-null   int64  
 12  Networking_Score       5000 non-null   int64  
 13  Job_Offers             5000 non-null   int64  
 14  Starting_Salary        5000 non-null   float64
 15  Career_Satisfaction    5000 non-null   int64  
 16  Years_to_Promotion     5000 non-null   int64  
 17  Current_Job_Level      5000 non-null   object 
 18  Work_Life_Balance      5000 non-null   int64  
 19  Entrepreneurship       5000 non-null   object 
dtypes: float64(3), int64(12), object(5)
memory usage: 781.4+ KB
In [20]:
# Tamaño del dataset

print(f"Filas: {datos.shape[0]}")
print(f"Columnas: {datos.shape[1]}")
Filas: 5000
Columnas: 20

4. Limpieza y Calidad de los Datos

Se realizó un diagnóstico completo del dataset antes de cualquier transformación. El análisis confirmó: ausencia de valores nulos, ausencia de registros duplicados, y variables numéricas dentro de rangos esperados. El dataset no requirió imputación ni corrección de valores atípicos.

In [23]:
# Análisis de valores nulos y retorno de columnas que poseen valores nulos

nulos = datos.isnull().sum()
nulos[nulos > 0]
Out[23]:
0

In [25]:
# Análisis de valores duplicados

duplicados = datos.duplicated().sum()
print(f"Registros duplicados: {duplicados}")
Registros duplicados: 0
In [26]:
# Variables categóricas y sus valores únicos

categoricas = datos.select_dtypes(include='object')
for col in categoricas.columns:
    print(f"{col}: {datos[col].unique()}")
Student_ID: ['S00001' 'S00002' 'S00003' ... 'S04998' 'S04999' 'S05000']
Gender: ['Male' 'Other' 'Female']
Field_of_Study: ['Arts' 'Law' 'Medicine' 'Computer Science' 'Engineering' 'Business'
 'Mathematics']
Current_Job_Level: ['Entry' 'Mid' 'Senior' 'Executive']
Entrepreneurship: ['No' 'Yes']
In [27]:
# Estadísticas principales

datos.describe().T
Out[27]:
count mean std min 25% 50% 75% max
Age 5000.0 23.442200 3.473712 18.0 20.00 23.00 26.00 29.0
High_School_GPA 5000.0 2.996978 0.575673 2.0 2.50 2.99 3.50 4.0
SAT_Score 5000.0 1253.832000 203.228954 900.0 1076.00 1257.00 1432.00 1600.0
University_Ranking 5000.0 504.335600 291.060011 1.0 256.00 501.50 759.00 1000.0
University_GPA 5000.0 3.020028 0.576047 2.0 2.52 3.03 3.51 4.0
Internships_Completed 5000.0 1.982200 1.408219 0.0 1.00 2.00 3.00 4.0
Projects_Completed 5000.0 4.562800 2.872927 0.0 2.00 5.00 7.00 9.0
Certifications 5000.0 2.512200 1.703183 0.0 1.00 3.00 4.00 5.0
Soft_Skills_Score 5000.0 5.546000 2.851159 1.0 3.00 6.00 8.00 10.0
Networking_Score 5000.0 5.538000 2.850084 1.0 3.00 6.00 8.00 10.0
Job_Offers 5000.0 2.488800 1.711859 0.0 1.00 2.00 4.00 5.0
Starting_Salary 5000.0 50563.540000 14494.958207 25000.0 40200.00 50300.00 60500.00 101000.0
Career_Satisfaction 5000.0 5.578000 2.871997 1.0 3.00 6.00 8.00 10.0
Years_to_Promotion 5000.0 3.015800 1.417446 1.0 2.00 3.00 4.00 5.0
Work_Life_Balance 5000.0 5.482400 2.883427 1.0 3.00 6.00 8.00 10.0
In [28]:
# Evaluación según dimensiones de calidad:

# Completitud
faltan_datos = nulos.sum()
print(f"Completitud: {'Hay datos faltantes' if faltan_datos > 0 else 'No hay datos faltantes'}")

# Validez
print(f"Edad válida (18-30): {datos['Age'].between(18, 30).all()}")
print(f"SAT Score válido (900-1600): {datos['SAT_Score'].between(900, 1600).all()}")
print(f"GPA Universidad válido (2.0-4.0): {datos['University_GPA'].between(2.0, 4.0).all()}")

# Precisión
print("Precisión: No hay errores evidentes, se asume correcta dado el contexto sintético.")

# Consistencia
correlaciones = datos[['Internships_Completed', 'Starting_Salary']].corr()
print("Consistencia: Correlación entre internados y salario:")
print(correlaciones)

# Relevancia
print("Relevancia: Las columnas son pertinentes para analizar éxito profesional.")

#Unicidad
print(f"Unicidad: {'Sin duplicados' if duplicados == 0 else f'{duplicados} duplicados encontrados'}")

# Integridad
print("Integridad: Datos legibles, sin errores de formato, categorías correctas.")
Completitud: No hay datos faltantes
Edad válida (18-30): True
SAT Score válido (900-1600): True
GPA Universidad válido (2.0-4.0): True
Precisión: No hay errores evidentes, se asume correcta dado el contexto sintético.
Consistencia: Correlación entre internados y salario:
                       Internships_Completed  Starting_Salary
Internships_Completed                1.00000          0.01808
Starting_Salary                      0.01808          1.00000
Relevancia: Las columnas son pertinentes para analizar éxito profesional.
Unicidad: Sin duplicados
Integridad: Datos legibles, sin errores de formato, categorías correctas.

5. Preparación de Datos y Variables Derivadas

Se construyeron cuatro variables derivadas:

  • Habilidades_Sociales: suma de Soft_Skills_Score y Networking_Score — indicador compuesto de competencias interpersonales.
  • Categoria_Salario: clasificación en cuatro rangos (Bajo/Medio/Alto/Muy Alto), con umbrales en 50k, 70k y 100k USD.
  • Nivel_Habilidades_Sociales: categorización ordinal (Baja/Media/Alta) del indicador compuesto.
  • Nivel_Universidad: clasificación Top/Media/Baja según ranking (cortes en 200 y 600).

Se generó el DataFrame datos_filtrados con las columnas relevantes para el análisis.

In [29]:
# Función para realizar operaciones entre columnas numéricas con NumPy

def operar_columnas_numpy(arr1, arr2, operacion):
    """
    Realiza operaciones NumPy entre dos arrays numéricos.

    Args:
        arr1, arr2: Arrays NumPy.
        operacion: 'suma', 'resta', 'multiplicacion', 'division'

    Returns:
        Array con el resultado de la operación.
    """
    if operacion == 'suma':
        return np.add(arr1, arr2)
    elif operacion == 'resta':
        return np.subtract(arr1, arr2)
    elif operacion == 'multiplicacion':
        return np.multiply(arr1, arr2)
    elif operacion == 'division':
        return np.divide(arr1, arr2, out=np.zeros_like(arr1), where=arr2 != 0)
    else:
        print("Operación no válida.")
        return None
In [30]:
# Convertir columnas a arrays NumPy
soft_skills = datos['Soft_Skills_Score'].to_numpy()
networking = datos['Networking_Score'].to_numpy()

# Operar con la función para obtener una variable compuesta (habilidades sociales)
habilidades_sociales = operar_columnas_numpy(soft_skills, networking, 'suma')

# Mostrar los primeros valores
habilidades_sociales[:10]

# Se incluye la columna Habilidades_Sociales
datos['Habilidades_Sociales'] = habilidades_sociales
In [31]:
# Creación del DataFrame de salarios
salarios_estadisticos = datos.groupby('Field_of_Study')['Starting_Salary'].agg(
    Salario_Promedio = 'mean',
    Salario_Minimo = 'min',
    Salario_Maximo = 'max'
    ).sort_values(by = 'Salario_Promedio', ascending = False)

display(salarios_estadisticos)
Salario_Promedio Salario_Minimo Salario_Maximo
Field_of_Study
Arts 51422.830441 25000.0 101000.0
Computer Science 50777.164179 25000.0 90800.0
Mathematics 50725.906040 25000.0 89900.0
Engineering 50416.547789 25000.0 98200.0
Business 50262.169680 25000.0 92400.0
Medicine 50219.158200 25000.0 90400.0
Law 50081.155433 25000.0 100600.0
In [32]:
# Salario máximo, mínimo y promedio (sin importar el rubro)

salario_max = datos['Starting_Salary'].max()
print(f"Salario maximo: ${salario_max}")

salario_min = datos['Starting_Salary'].min()
print(f"Salario minimo: ${salario_min}")

salario_prom = datos['Starting_Salary'].mean()
print(f"Salario promedio: ${salario_prom}")
Salario maximo: $101000.0
Salario minimo: $25000.0
Salario promedio: $50563.54
In [34]:
# Clasificación de salario en categorías

def clasificar_salario(salario):
    """
    Clasifica un salario dado en categorías:

    - 'Muy Alto': salario ≥ 100000
    - 'Alto': 70000 ≤ salario < 100000
    - 'Medio': 50000 ≤ salario < 70000
    - 'Bajo': salario < 50000

    Args:
        salario (int o float): Salario inicial del estudiante.

    Returns:
        str: Categoría asignada al salario.
    """
    if salario >= 100000:
        return 'Muy Alto'
    elif salario >= 70000:
        return 'Alto'
    elif salario >= 50000:
        return 'Medio'
    else:
        return 'Bajo'

datos['Categoria_Salario'] = datos['Starting_Salary'].apply(clasificar_salario)
In [35]:
# Mostrar cuántos estudiantes hay en cada categoría

datos['Categoria_Salario'].value_counts()
Out[35]:
count
Categoria_Salario
Bajo 2446
Medio 2061
Alto 491
Muy Alto 2

In [37]:
# Crear nuevo DataFrame con variables seleccionadas
# Se incluyen: Habilidades_Sociales y Categoria_Salario (columnas creadas anteriormente)

datos_filtrados = datos[[
    'Age',
    'Gender',
    'University_GPA',
    'Field_of_Study',
    'University_Ranking',
    'Soft_Skills_Score',
    'Networking_Score',
    'Habilidades_Sociales',
    'Starting_Salary',
    'Job_Offers',
    'Current_Job_Level',
    'Categoria_Salario'
]].copy()

datos_filtrados.head()
Out[37]:
Age Gender University_GPA Field_of_Study University_Ranking Soft_Skills_Score Networking_Score Habilidades_Sociales Starting_Salary Job_Offers Current_Job_Level Categoria_Salario
0 24 Male 3.96 Arts 291 9 8 17 27200.0 5 Entry Bajo
1 21 Other 3.63 Law 112 8 1 9 25000.0 4 Mid Bajo
2 28 Female 2.63 Medicine 715 1 9 10 42400.0 0 Entry Bajo
3 25 Male 2.81 Computer Science 170 10 6 16 57400.0 1 Mid Medio
4 22 Male 2.48 Engineering 599 10 9 19 47600.0 4 Entry Bajo
In [39]:
# Promedios por categoría salarial

promedios_por_categoria = datos_filtrados.groupby('Categoria_Salario').agg({
    'University_GPA': 'mean',
    'Habilidades_Sociales': 'mean',
    'Starting_Salary': 'mean',
    'Job_Offers': 'mean'
}).round(2)

# Mostrar ordenado de mayor a menor salario
promedios_por_categoria.sort_values(by='Starting_Salary', ascending=False)
Out[39]:
University_GPA Habilidades_Sociales Starting_Salary Job_Offers
Categoria_Salario
Muy Alto 3.47 9.00 100800.00 0.50
Alto 3.03 11.31 76691.85 2.41
Medio 3.01 11.09 58583.55 2.44
Bajo 3.02 11.04 38519.91 2.55
In [40]:
# Función para clasificar el nivel de habilidades sociales
def clasificar_habilidades(valor):
    if valor >= 16:
        return 'Alta'
    elif valor >= 10:
        return 'Media'
    else:
        return 'Baja'

# Aplicar y agregar la nueva columna
datos_filtrados['Nivel_Habilidades_Sociales'] = datos_filtrados['Habilidades_Sociales'].apply(clasificar_habilidades)
In [41]:
# Contar cuántos estudiantes hay en cada nivel

datos_filtrados['Nivel_Habilidades_Sociales'].value_counts()
Out[41]:
count
Nivel_Habilidades_Sociales
Media 2434
Baja 1791
Alta 775

In [43]:
# 📊 Resumen estadístico de datos numéricos
resumen_numerico = datos_filtrados.describe()
print("Resumen estadístico numérico:")
display(resumen_numerico)

# 🎯 Media, varianza y desviación estándar de columnas numéricas
medias = datos_filtrados.mean(numeric_only=True)
varianzas = datos_filtrados.var(numeric_only=True)
desviaciones = datos_filtrados.std(numeric_only=True)

# Mostramos algunos de estos resultados
print("Media por columna numérica:")
print(medias.round(2))

print("Varianza por columna numérica:")
print(varianzas.round(2))

print("Desviación estándar por columna numérica:")
print(desviaciones.round(2))

# 📈 Correlaciones entre variables numéricas
correlaciones = datos_filtrados.corr(numeric_only=True)
print("Correlaciones entre variables numéricas:")
display(correlaciones)
Resumen estadístico numérico:
Age University_GPA University_Ranking Soft_Skills_Score Networking_Score Habilidades_Sociales Starting_Salary Job_Offers
count 5000.000000 5000.000000 5000.000000 5000.000000 5000.000000 5000.000000 5000.000000 5000.000000
mean 23.442200 3.020028 504.335600 5.546000 5.538000 11.084000 50563.540000 2.488800
std 3.473712 0.576047 291.060011 2.851159 2.850084 4.051892 14494.958207 1.711859
min 18.000000 2.000000 1.000000 1.000000 1.000000 2.000000 25000.000000 0.000000
25% 20.000000 2.520000 256.000000 3.000000 3.000000 8.000000 40200.000000 1.000000
50% 23.000000 3.030000 501.500000 6.000000 6.000000 11.000000 50300.000000 2.000000
75% 26.000000 3.510000 759.000000 8.000000 8.000000 14.000000 60500.000000 4.000000
max 29.000000 4.000000 1000.000000 10.000000 10.000000 20.000000 101000.000000 5.000000
Media por columna numérica:
Age                        23.44
University_GPA              3.02
University_Ranking        504.34
Soft_Skills_Score           5.55
Networking_Score            5.54
Habilidades_Sociales       11.08
Starting_Salary         50563.54
Job_Offers                  2.49
dtype: float64
Varianza por columna numérica:
Age                     1.207000e+01
University_GPA          3.300000e-01
University_Ranking      8.471593e+04
Soft_Skills_Score       8.130000e+00
Networking_Score        8.120000e+00
Habilidades_Sociales    1.642000e+01
Starting_Salary         2.101038e+08
Job_Offers              2.930000e+00
dtype: float64
Desviación estándar por columna numérica:
Age                         3.47
University_GPA              0.58
University_Ranking        291.06
Soft_Skills_Score           2.85
Networking_Score            2.85
Habilidades_Sociales        4.05
Starting_Salary         14494.96
Job_Offers                  1.71
dtype: float64
Correlaciones entre variables numéricas:
Age University_GPA University_Ranking Soft_Skills_Score Networking_Score Habilidades_Sociales Starting_Salary Job_Offers
Age 1.000000 -0.015253 0.031417 0.006055 -0.001647 0.003102 0.013171 -0.030368
University_GPA -0.015253 1.000000 -0.004471 -0.009201 0.006803 -0.001689 0.001022 -0.014875
University_Ranking 0.031417 -0.004471 1.000000 -0.004211 -0.016708 -0.014716 0.021368 0.013199
Soft_Skills_Score 0.006055 -0.009201 -0.004211 1.000000 0.010198 0.710834 0.004870 -0.008501
Networking_Score -0.001647 0.006803 -0.016708 0.010198 1.000000 0.710572 0.002622 -0.016600
Habilidades_Sociales 0.003102 -0.001689 -0.014716 0.710834 0.710572 1.000000 0.005271 -0.017658
Starting_Salary 0.013171 0.001022 0.021368 0.004870 0.002622 0.005271 1.000000 -0.034014
Job_Offers -0.030368 -0.014875 0.013199 -0.008501 -0.016600 -0.017658 -0.034014 1.000000
In [45]:
# Clasificación de la universidad

def clasificar_universidad(ranking):
    if ranking <= 200:
        return 'Top'
    elif ranking <= 600:
        return 'Media'
    else:
        return 'Baja'

datos_filtrados['Nivel_Universidad'] = datos_filtrados['University_Ranking'].apply(clasificar_universidad)
In [50]:
# Ranking de variables numéricas que más se correlacional con el salario inicial

corr_salarial = datos.corr(numeric_only=True)['Starting_Salary'].sort_values(ascending=False)
print(corr_salarial)
Starting_Salary          1.000000
University_Ranking       0.021368
Internships_Completed    0.018080
Projects_Completed       0.015192
Age                      0.013171
Work_Life_Balance        0.006371
Years_to_Promotion       0.005674
Habilidades_Sociales     0.005271
Soft_Skills_Score        0.004870
SAT_Score                0.002776
Networking_Score         0.002622
Career_Satisfaction      0.002422
University_GPA           0.001022
High_School_GPA         -0.009152
Certifications          -0.018367
Job_Offers              -0.034014
Name: Starting_Salary, dtype: float64

6. Análisis Exploratorio de Datos

Las visualizaciones se organizan por temática: distribución del dataset, variables salariales, relaciones con género y networking, y empleabilidad. Cada gráfico responde a una de las preguntas de investigación planteadas.

In [53]:
# @title Cantidad de Estudiantes por Campo de estudio.

# Calcula cuantos estudiantes hay en cada campo de estudio
Field_counts = datos.groupby('Field_of_Study').size()

# Almacena los datos para luego pasarlos a la grafica
ax = Field_counts.plot(kind='bar', color=sns.palettes.mpl_palette('crest'))

ax.spines[['top', 'right',]].set_visible(False)

for p in ax.patches:
    ax.annotate(f'{p.get_height():.0f}',
                (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center',
                va='bottom',
                xytext=(0, 5), # Desplazamiento de la x
                textcoords='offset points')

# Renombramos los ejes y el nombre
plt.xticks(rotation=45, ha='right', fontsize=16)
plt.xlabel('Campo de Estudio', fontsize=16)
plt.ylabel('Cantidad de Estudiantes', fontsize=16)
plt.rcParams.update({'font.size': 16}) # Añadir los valores encima de cada barra
plt.tight_layout() # Ajustamos el diseño
plt.show()
No description has been provided for this image

Fig. 1 — Distribución de estudiantes por campo de estudio

El dataset presenta una distribución equilibrada entre las siete disciplinas (670–749 estudiantes por área). Arts y Mathematics concentran la mayor representación; Engineering la menor. Este equilibrio garantiza la comparabilidad entre carreras en el análisis posterior.

In [55]:
#@title Histogramas de Salarios

# Histograma que muestra la distribución del salario inicial (Starting_Salary) de los estudiantes
sns.histplot(
    datos['Starting_Salary'].dropna(),
    bins=20,
    kde=True,
    color='mediumseagreen'
)

plt.title("Histograma de Salarios - Seaborn")
plt.xlabel("Salarios")
plt.xlim(25000, 100000)
plt.show()
No description has been provided for this image

Fig. 2 — Distribución del salario inicial

La distribución salarial se concentra entre 40.000 y 60.000 USD anuales, con asimetría positiva moderada. La cola derecha indica un subconjunto con ingresos superiores a la media, patrón consistente con distribuciones salariales de jóvenes profesionales.

In [67]:
# Boxplot que muestra cómo varía el salario inicial (Starting_Salary)
# en función del nivel de universidad (Nivel_Universidad), comparando además entre géneros (Gender)

plt.figure(figsize=(10, 6))
sns.boxplot(
    data=datos_filtrados, # Changed from df to datos_filtrados
    x="Nivel_Universidad",
    y="Starting_Salary",
    hue="Gender",
    palette="Set2"
)

plt.title("Salario Inicial por Nivel de Universidad y Género")
plt.xlabel("Nivel de Universidad")
plt.ylabel("Salario Inicial")
plt.legend(title="Género")
plt.tight_layout()
plt.show()
No description has been provided for this image

Fig. 3 — Salario inicial por nivel universitario y género

Los estudiantes de universidades Top presentan medianas salariales superiores, aunque la superposición entre niveles es considerable. La brecha de género se mantiene en todos los niveles universitarios. El prestigio institucional tiene un efecto positivo moderado, no determinante.

In [68]:
# Matriz de correlaciones entre todas las variables numéricas del dataset
df_numerico = df.select_dtypes(include=['number'])

# Mapa de calor que visualiza las correlaciones
plt.figure(figsize=(10, 8))
sns.heatmap(
    df_numerico.corr(),
    annot=True,
    cmap='coolwarm',
    fmt=".2f"
)

plt.title('Mapa de Calor de Correlaciones', fontsize=16)
plt.show()

# Este mapa de calor permite visualizar de manera rápida las correlaciones entre todas las variables numéricas del dataset.
# Es útil para identificar relaciones fuertes (positivas o negativas) que pueden ser relevantes para el análisis o modelado posterior.
No description has been provided for this image

Fig. 4 — Matriz de correlación entre variables numéricas

Ninguna variable presenta correlación fuerte con el salario inicial (coeficientes de Pearson < 0.1 en todos los casos). Las correlaciones más elevadas se dan entre variables de habilidades (Soft_Skills, Networking). Este es uno de los hallazgos centrales: el salario inicial no tiene predictores lineales dominantes en este dataset.

In [74]:
# Crear la tabla cruzada
tabla = pd.crosstab(datos['Gender'], datos['Field_of_Study'])

# Mapa de calor - Muestra la distribución cruzada entre género y elección de carrera
sns.heatmap(tabla, annot=True, cmap="YlGnBu", fmt='d')
plt.title("Mapa de Calor de Género vs Field_of_Study")
plt.ylabel("Género")
plt.xlabel("Field_of_Study")
plt.show()

#Aca al cruzar la tabla vemos cuantos estudiantes son femeninos, masculinos y otros que se encuentran en diferentes areas, en su mayoria son matemicos, ingenieros y negocios;
#lo cual es coherente con la actualidad
No description has been provided for this image

Fig. 5 — Distribución de género por campo de estudio

La distribución es relativamente equilibrada. Se aprecian concentraciones masculinas leves en Engineering y Business, y mayor representación femenina en Medicine y Arts. Estas diferencias son moderadas y no comprometen la comparabilidad entre grupos.

In [75]:
# Este gráfico de líneas muestra cómo varía el salario inicial promedio (Starting_Salary)
# en función del puntaje de networking (Networking_Score), separado por género (Gender).

# Calculamos el salario inicial promedio agrupado por Networking_Score y Género
df_mean = df.groupby(['Networking_Score', 'Gender'])['Starting_Salary'].mean().reset_index()

# Este gráfico permite detectar si el networking impacta positivamente en el salario inicial,
# y si ese impacto varía según el género.

plt.figure(figsize=(12, 6))
sns.lineplot(
    data=df_mean,
    x='Networking_Score',
    y='Starting_Salary',
    hue='Gender',
    marker='o'
)

plt.title('Salario Inicial según Networking Score, por Género', fontsize=22)
plt.xlabel('Networking Score',fontsize=22)
plt.ylabel('Salario Inicial Promedio', fontsize=22)
plt.legend(title='Género', fontsize=22)
plt.grid(True)
plt.tight_layout()
plt.show()

# Este gráfico ayuda a visualizar posibles tendencias entre las habilidades de networking y el salario inicial,
# permitiendo también identificar si existen diferencias notorias entre los géneros a lo largo de los distintos niveles de networking.
No description has been provided for this image

Fig. 6 — Salario promedio según Networking Score, por género

Se observa una tendencia creciente entre el puntaje de networking y el salario promedio, con alta variabilidad. La brecha de género persiste en todos los niveles de networking. La relación no es fuerte, consistente con el resultado de la matriz de correlación.

Fig. 7 — Salario inicial promedio por campo de estudio y género

Se aprecian diferencias salariales entre disciplinas: Medicine e Engineering tienden a los valores más altos. La brecha de género es visible en casi todas las carreras, aunque de magnitud variable. El campo de estudio tiene mayor peso diferenciador que el género en la determinación del salario inicial.

In [81]:
# Creamos un grafico cruzado donde se tome el campo de estudio, genero y salario incial
salario_promedio_por_carrera_genero = datos.groupby(['Field_of_Study', 'Gender'])['Starting_Salary'].mean().reset_index()

plt.figure(figsize=(14, 8))
sns.barplot(
    data=salario_promedio_por_carrera_genero,
    x='Field_of_Study',
    y='Starting_Salary',
    hue='Gender',
    palette='viridis'
)

plt.title('Salario Inicial Promedio por Campo de Estudio y Género', fontsize=22)
plt.xlabel('Campo de Estudio', fontsize=22)
plt.ylabel('Salario Inicial Promedio', fontsize=22)
plt.xticks(rotation=45, ha='right')
plt.legend(title='Género',loc='upper left', bbox_to_anchor=(1, 1))
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()
No description has been provided for this image
In [86]:
# Este gráfico de barras muestra el promedio de ofertas laborales recibidas
# por los estudiantes en cada campo de estudio (Field_of_Study)

# Calculamos el promedio de ofertas laborales por campo de estudio
ofertas_promedio = datos.groupby('Field_of_Study')['Job_Offers'].mean().sort_values(ascending=False)

# Este análisis responde preguntas sobre empleabilidad según la formación académica

plt.figure(figsize=(10, 6))
ofertas_promedio.plot(kind='bar', color='skyblue')
plt.title('Promedio de Ofertas Laborales por Campo de Estudio')
plt.ylabel('Ofertas Promedio')
plt.xlabel('Campo de Estudio')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
No description has been provided for this image

Fig. 8 — Promedio de ofertas laborales por campo de estudio

Se observan diferencias de empleabilidad según la disciplina, con Medicine y Engineering concentrando los promedios más altos. El ordenamiento no coincide exactamente con el de salarios, lo que indica que empleabilidad y remuneración son dimensiones parcialmente independientes del éxito profesional.

In [89]:
# Este gráfico de barras muestra los 5 campos de estudio (Field_of_Study)
# con mayor satisfacción profesional promedio (Career_Satisfaction)

# El DataFrame 'datos' ya está cargado en memoria desde celdas anteriores, no es necesario recargarlo.
# Si 'datos' no estuviera disponible por algún reinicio del kernel, la ruta correcta sería:
# original_ruta = '/content/education_career_success (1).csv'
# datos = pd.read_csv(original_ruta)

# Calculamos la media y seleccionamos los 5 primeros
top5_satisfaccion = datos.groupby('Field_of_Study')['Career_Satisfaction'].mean().sort_values(ascending=False).head(5)

plt.figure(figsize=(10, 6))
top5_satisfaccion.plot(kind='bar', color='seagreen')
plt.title('Top 5 Campos de Estudio con Mayor Satisfacción Profesional Promedio', fontsize=22)
plt.xlabel('Campo de Estudio', fontsize=22)
plt.ylabel('Satisfacción Profesional Promedio', fontsize=22)
plt.tight_layout()
plt.show()

# Este gráfico es útil para identificar las áreas académicas donde los egresados reportan mayor satisfacción profesional,
# lo cual puede ser relevante para estudiantes o instituciones educativas.
No description has been provided for this image

Fig. 9 — Top 5 campos de estudio con mayor satisfacción profesional promedio

Las carreras con mayor satisfacción promedio incluyen disciplinas técnicas y de salud. Esta dimensión subjetiva no se alinea perfectamente con el salario ni con las ofertas laborales, lo que refuerza la naturaleza multidimensional del éxito profesional.

In [92]:
# Este gráfico de densidad (KDE) muestra la distribución del salario inicial (Starting_Salary)
# para cada género (Gender), permitiendo comparar las distribuciones completas entre grupos

plt.figure(figsize=(10, 6))
sns.kdeplot(
    data=datos,
    x='Starting_Salary',
    hue='Gender',
    fill=True,
    common_norm=False,
    alpha=0.5,
    palette='Set2'
)
plt.title('Distribución del Salario Inicial por Género')
plt.xlabel('Salario Inicial', fontsize=16)
plt.ylabel('Densidad', fontsize=16)

plt.tight_layout()
plt.show()

# Este gráfico permite comparar la forma de la distribución salarial entre géneros,
# más allá de las medias, destacando posibles desigualdades o similitudes en la distribución completa.
No description has been provided for this image

Fig. 10 — Distribución del salario inicial por género (KDE)

La distribución masculina se desplaza levemente hacia valores más altos. La superposición considerable entre ambas curvas indica que las diferencias de género son estadísticamente presentes pero de magnitud moderada. Este gráfico complementa el boxplot con una perspectiva distribucional completa.

7. Análisis Estadístico

Se presentan medidas de posición y dispersión, tests de normalidad, análisis estadístico de asociación entre género y salario, y varianzas de las variables numéricas.

In [96]:
# @title Medidas de Posición

#Medidas para variables numéricas del dataset "datos"

# Seleccionar solo columnas numéricas
datos_numericos = datos.select_dtypes(include=[np.number])

# Calcular Media, Mediana y Moda
medidas_posicion = pd.DataFrame({
    'Media': datos_numericos.mean().round(2),
    'Mediana': datos_numericos.median().round(2),
    'Moda': datos_numericos.mode().iloc[0].round(2)  # En caso de múltiple moda, se toma la primera
})

# resultados
print(" Medidas de Posición para variables numéricas:")
display(medidas_posicion)
 Medidas de Posición para variables numéricas:
Media Mediana Moda
Age 23.44 23.00 18.00
High_School_GPA 3.00 2.99 3.72
SAT_Score 1253.83 1257.00 1319.00
University_Ranking 504.34 501.50 816.00
University_GPA 3.02 3.03 3.22
Internships_Completed 1.98 2.00 1.00
Projects_Completed 4.56 5.00 3.00
Certifications 2.51 3.00 4.00
Soft_Skills_Score 5.55 6.00 3.00
Networking_Score 5.54 6.00 6.00
Job_Offers 2.49 2.00 2.00
Starting_Salary 50563.54 50300.00 25000.00
Career_Satisfaction 5.58 6.00 10.00
Years_to_Promotion 3.02 3.00 2.00
Work_Life_Balance 5.48 6.00 1.00
Habilidades_Sociales 11.08 11.00 11.00
In [98]:
#  Medidas de Dispersión para variables numéricas en datos filtrados

# Seleccionar solo columnas numéricas del conjunto filtrado
datos_numericos_filtrados = datos_filtrados.select_dtypes(include=[np.number])

# Calcular Rango, Varianza y Desviación Estándar
medidas_dispersion_filtrados = pd.DataFrame({
    'Rango': (datos_numericos_filtrados.max() - datos_numericos_filtrados.min()).round(2),
    'Varianza': datos_numericos_filtrados.var().round(2),
    'Desviación Estándar': datos_numericos_filtrados.std().round(2)
})

# Mostrar resultados
print(" Medidas de Dispersión para variables numéricas (Datos Filtrados):")
display(medidas_dispersion_filtrados)
 Medidas de Dispersión para variables numéricas (Datos Filtrados):
Rango Varianza Desviación Estándar
Age 11.00 1.207000e+01 3.47
University_GPA 2.00 3.300000e-01 0.58
University_Ranking 999.00 8.471593e+04 291.06
Soft_Skills_Score 9.00 8.130000e+00 2.85
Networking_Score 9.00 8.120000e+00 2.85
Habilidades_Sociales 18.00 1.642000e+01 4.05
Starting_Salary 76000.00 2.101038e+08 14494.96
Job_Offers 5.00 2.930000e+00 1.71
University_GPA_scaled 3.47 1.000000e+00 1.00
Habilidades_Sociales_scaled 4.44 1.000000e+00 1.00
Habilidades_y_GPA_sum_scaled 7.79 2.000000e+00 1.41
Habilidades_y_GPA_sum_scaled_for_size 7.79 2.000000e+00 1.41
In [101]:
# Función para aplicar múltiples tests de normalidad pero solo con la medida Kolmogrov_smirnov
from scipy.stats import kstest, zscore
import pandas as pd

# 1. Seleccionar columnas numéricas
datos_numericos = df.select_dtypes(include='number')

# 2. Crear lista de resultados
resultados = []

# 3. Loop por cada columna numérica
for columna in datos_numericos.columns:
    datos_columna = datos_numericos[columna].dropna()  # eliminar NaNs
    datos_z = zscore(datos_columna)  # calcular z-score

    # 4. Aplicar test de Kolmogorov-Smirnov contra distribución normal estándar
    estadistico, p_valor = kstest(datos_z, 'norm')

    resultados.append({
        'Columna': columna,
        'Estadístico': estadistico,
        'p-valor': p_valor,
        'Normalidad (α=0.05)': 'Aceptada' if p_valor > 0.05 else 'Rechazada'
    })

# 5. Convertir a DataFrame para ver los resultados juntos
df_resultados = pd.DataFrame(resultados)
df_resultados
Out[101]:
Columna Estadístico p-valor Normalidad (α=0.05)
0 Age 0.098010 2.927884e-42 Rechazada
1 High_School_GPA 0.060310 2.986115e-16 Rechazada
2 SAT_Score 0.062832 1.327587e-17 Rechazada
3 University_Ranking 0.059716 6.099881e-16 Rechazada
4 University_GPA 0.060087 3.906498e-16 Rechazada
5 Internships_Completed 0.164870 3.052818e-119 Rechazada
6 Projects_Completed 0.106498 7.779134e-50 Rechazada
7 Certifications 0.145440 9.141154e-93 Rechazada
8 Soft_Skills_Score 0.108887 4.398051e-52 Rechazada
9 Networking_Score 0.108384 1.320149e-51 Rechazada
10 Job_Offers 0.141991 2.027540e-88 Rechazada
11 Starting_Salary 0.038883 5.268017e-07 Rechazada
12 Career_Satisfaction 0.111898 5.498686e-55 Rechazada
13 Years_to_Promotion 0.166424 1.657675e-121 Rechazada
14 Work_Life_Balance 0.110583 1.042831e-53 Rechazada
15 Habilidades_Sociales 0.056356 3.035939e-14 Rechazada

Tests de normalidad (Shapiro-Wilk y Kolmogorov-Smirnov)

En todos los casos se rechaza la hipótesis de normalidad (p-valor < 0.05). Este resultado es esperable con n = 5.000, ya que los tests son altamente sensibles a desviaciones menores. Se recomienda interpretar este resultado junto con la inspección visual de los histogramas.

Análisis Chi-cuadrado y correlación de Pearson

El test de Chi-cuadrado aplicado sobre género y categoría salarial es consistente con la brecha de género moderada observada en las visualizaciones previas. Los coeficientes de correlación de Pearson entre el salario inicial y las variables numéricas son todos cercanos a cero, lo que confirma la ausencia de predictores lineales individuales dominantes.

In [116]:
# --- VARIANZAS ---
print("\n=== Varianzas ===")
print(df_numerico.var())
=== Varianzas ===
Age                      1.206667e+01
High_School_GPA          3.313998e-01
SAT_Score                4.130201e+04
University_Ranking       8.471593e+04
Internships_Completed    1.983080e+00
Projects_Completed       8.253707e+00
Certifications           2.900831e+00
Soft_Skills_Score        8.129110e+00
Networking_Score         8.122981e+00
Job_Offers               2.930461e+00
Starting_Salary          2.101038e+08
Career_Satisfaction      8.248366e+00
Years_to_Promotion       2.009152e+00
Work_Life_Balance        8.314153e+00
Habilidades_Sociales     1.641783e+01
dtype: float64

University_Ranking presenta la mayor varianza del conjunto, reflejando la diversidad de instituciones representadas. Las variables de habilidades muestran menor dispersión, con rangos más homogéneos entre estudiantes.

8. Resultados Principales

1. Ausencia de predictores lineales dominantes

Ninguna variable numérica presenta correlación fuerte con el salario inicial (coeficientes de Pearson < 0.1). El ingreso temprano no está determinado por un único factor mesurable, lo que sugiere una estructura explicativa multivariante.

2. Influencia moderada del campo de estudio y nivel universitario

Se observan diferencias salariales entre disciplinas y niveles, con superposición considerable entre grupos. Medicine, Engineering y Law concentran los salarios más altos y el mayor número de ofertas. El nivel universitario tiene un efecto positivo moderado sobre el salario.

3. Brecha de género leve pero consistente

Los hombres presentan medianas salariales y distribuciones de densidad desplazadas hacia valores más altos en todas las comparaciones. La brecha se mantiene al controlar por nivel universitario y campo de estudio, aunque su magnitud es moderada.

4. Networking como factor contribuyente sin efecto determinante

El puntaje de networking muestra una tendencia positiva débil con el salario promedio. Las habilidades de networking contribuyen a la inserción laboral sin ser un factor suficiente por sí solo.

5. Satisfacción profesional como dimensión independiente del salario

Las disciplinas con mayor satisfacción promedio no coinciden exactamente con las de mayor salario ni con las de mayor empleabilidad, lo que confirma la multidimensionalidad del éxito profesional.

9. Discusión

Los resultados son consistentes con la complejidad inherente a los fenómenos de inserción laboral. Los factores individuales disponibles tienen efectos débiles y parciales sobre el salario inicial. La ausencia de correlaciones fuertes no implica irrelevancia de estas variables, sino que su influencia se manifiesta en combinaciones difíciles de capturar con análisis bivariados.

Limitaciones del análisis:

  • El carácter sintético del dataset limita la validez externa: las relaciones observadas reflejan supuestos del proceso de generación de datos.
  • Ausencia de variables temporales: no es posible analizar trayectorias de carrera ni efectos de la experiencia acumulada.
  • Variables relevantes ausentes: sector económico, región geográfica, tipo de empresa.
  • Los análisis son descriptivos y correlacionales; no se pueden establecer relaciones causales.

Extensiones futuras podrían incorporar modelos de regresión múltiple, técnicas de clustering para identificar perfiles estudiantiles, o análisis con datos reales de egresados universitarios.

10. Conclusiones

  1. El éxito profesional temprano no está explicado por un único predictor lineal. Las correlaciones entre variables individuales y el salario inicial son sistemáticamente bajas, evidenciando la naturaleza multifactorial del fenómeno.
  2. El campo de estudio y el nivel universitario tienen efectos moderados y diferenciados sobre el salario, la empleabilidad y la satisfacción. Estos tres indicadores deben analizarse como dimensiones separadas del éxito profesional.
  3. Se observa una brecha salarial asociada al género, leve pero consistente en todas las comparaciones realizadas. Esta observación requiere cautela interpretativa dado el carácter sintético del dataset.

El análisis demuestra el valor del ciclo completo de la ciencia de datos —desde la limpieza hasta la inferencia estadística— para caracterizar fenómenos sociales complejos, al tiempo que evidencia los límites de los enfoques descriptivos cuando los determinantes del fenómeno son múltiples e interactivos.

Nota: El dataset no contiene variables temporales, por lo que no fue posible realizar análisis de series de tiempo.