In [ ]:
import pandas as pd
# Cargar un archivo CSV en un DataFrame
df1 = pd.read_csv('registros_titulos.csv',low_memory=False)
df2 = pd.read_csv('catastro_carreras_20241009.csv',low_memory=False)
Función para imprimir los shape de todos los df en memoria¶
In [ ]:
import pandas as pd
def print_df_shapes():
# Obtener todos los nombres de los DataFrames en memoria
df_names = [name for name in globals() if isinstance(globals()[name], pd.DataFrame)]
# Iterar sobre los nombres de los DataFrames y imprimir su shape
for name in df_names:
df = globals()[name]
print(f"Shape del DataFrame {name}: {df.shape}")
Función para imprimir los valores únicos de todos los df¶
In [ ]:
import pandas as pd
def print_unique_values():
# Obtener todos los nombres de los DataFrames en memoria
df_names = [name for name in globals() if isinstance(globals()[name], pd.DataFrame)]
# Iterar sobre los nombres de los DataFrames y imprimir los valores únicos de cada columna
for name in df_names:
df = globals()[name]
unique_counts = df.nunique()
print(f"Valores únicos del DataFrame {name}:")
print(unique_counts)
print()
Función de limpieza¶
In [ ]:
pip install unidecode
In [ ]:
import unidecode
import re
# Función para limpiar y estandarizar los nombres
def clean_and_standardize(name):
if isinstance(name, str):
# Eliminar espacios en blanco al inicio y al final
name = name.strip()
# Convertir a minúsculas
name = name.lower()
# Eliminar acentos
name = unidecode.unidecode(name)
# Eliminar comillas simples, dobles, guiones, barras, puntos, dos puntos, paréntesis y comas
name = re.sub(r"['\":(),.-]", "", name)
# Eliminar dobles espacios en el medio de los nombres
name = re.sub(r'\s+', ' ', name)
return name
# Diccionario ampliado de abreviaturas comunes en el contexto educativo
ABREVIATURAS = {
"dr": "doctor",
"dra": "doctora",
"gral": "general",
"cnel": "coronel",
"tte": "teniente",
"tec": "tecnico",
"mcal": "mariscal",
"educ": "educacion",
"inst": "instituto",
"nac": "nacional",
"dir": "direccion",
"prof": "profesor",
"priv": "privado",
"reg": "regional",
"univ": "universidad",
"esc": "escuela",
"adm": "administracion",
# Agrega más términos según las necesidades específicas del dataset
}
def expand_abbreviations(name):
# Reemplazo de abreviaturas por sus equivalentes completos
for abbr, full in ABREVIATURAS.items():
name = re.sub(rf"\b{abbr}\b", full, name)
return name
Aplicar limpieza a todos los campos¶
In [ ]:
from tqdm import tqdm
print("valores únicos antes de la limpieza")
print_unique_values()
# Aplicar la función de limpieza a todos los valores de df1
tqdm.pandas(desc="Limpiando df1")
df1 = df1.map(clean_and_standardize)
# Expandir abreviaturas solo en columnas de tipo string
tqdm.pandas(desc="expandiendo abreviaturas en df1")
df1[df1.select_dtypes(include="object").columns] = df1.select_dtypes(include="object").map(
lambda x: expand_abbreviations(x) if isinstance(x, str) else x
)
# Aplicar la función de limpieza a todos los valores de df2
tqdm.pandas(desc="Limpiando df2")
df2 = df2.map(clean_and_standardize)
# Expandir abreviaturas solo en columnas de tipo string
tqdm.pandas(desc="expandiendo abreviaturas en df2")
df2[df2.select_dtypes(include="object").columns] = df2.select_dtypes(include="object").map(
lambda x: expand_abbreviations(x) if isinstance(x, str) else x
)
print("valores únicos después de la limpieza")
print_unique_values()
MAYUSCULAS¶
In [ ]:
# Convertir todos los textos a mayúsculas en df1 y df2
df1 = df1.applymap(lambda x: x.upper() if isinstance(x, str) else x)
df2 = df2.applymap(lambda x: x.upper() if isinstance(x, str) else x)
# Ahora, revisa si se han convertido correctamente
print(df1.head())
print(df2.head())
Trabajar con valores únicos¶
In [ ]:
# Obtener valores únicos y ordenados alfabéticamente de las columnas de instituciones en ambos DataFrames
unique_institutions_df1 = pd.DataFrame(sorted(df1['institucion'].unique()), columns=['institucion'])
unique_institutions_df2 = pd.DataFrame(sorted(df2['nombre_institucion'].unique()), columns=['nombre_institucion'])
# Exportar a archivos Excel
unique_institutions_df1.to_excel("unique_institutions_df1.xlsx", index=False)
unique_institutions_df2.to_excel("unique_institutions_df2.xlsx", index=False)
print("Archivos exportados: 'unique_institutions_df1.xlsx' y 'unique_institutions_df2.xlsx'")
In [ ]:
import re
import unidecode
from fuzzywuzzy import fuzz, process
# Configuración de coincidencia aproximada con umbral de similitud
similarity_threshold = 90 # Nivel de similitud; puedes ajustar este valor según los resultados
# Encontrar coincidencias aproximadas
def find_fuzzy_matches(df1, df2, col1, col2, threshold):
matches = []
for name in df1[col1]:
match = process.extractOne(name, df2[col2], scorer=fuzz.ratio)
if match and match[1] >= threshold:
matches.append((name, match[0]))
else:
matches.append((name, "#N/D"))
return matches
# DataFrames con coincidencias
coincidencias_df1 = pd.DataFrame(find_fuzzy_matches(unique_institutions_df1, unique_institutions_df2, 'institucion', 'nombre_institucion', similarity_threshold), columns=['institucion', 'coincidencia_en_nombre'])
coincidencias_df2 = pd.DataFrame(find_fuzzy_matches(unique_institutions_df2, unique_institutions_df1, 'nombre_institucion', 'institucion', similarity_threshold), columns=['nombre_institucion', 'coincidencia_en_institucion'])
# Exportar resultados a Excel
coincidencias_df1.to_excel('coincidencias_df1.xlsx', index=False)
coincidencias_df2.to_excel('coincidencias_df2.xlsx', index=False)
In [ ]:
print_df_shapes()
In [ ]:
import re
import unidecode
from fuzzywuzzy import fuzz, process
import pandas as pd
from tqdm import tqdm
# Umbral de similitud (ajústalo según los resultados que obtengas)
similarity_threshold = 85
# Función para realizar coincidencias complejas con columna de nombre unificado
def complex_fuzzy_matching(df1, df2, col1, col2, threshold):
matches = []
for name in tqdm(df1[col1], desc="Comparando instituciones..."):
# Encuentra la coincidencia más cercana en el otro DataFrame usando `token_set_ratio` y `partial_ratio`
match = process.extractOne(name, df2[col2], scorer=fuzz.token_set_ratio)
if match and match[1] >= threshold:
refined_score = fuzz.partial_ratio(name, match[0])
# Asegúrate de que ambas puntuaciones cumplan el umbral o alguna esté cerca
if refined_score >= threshold or (refined_score + match[1]) / 2 >= threshold:
unified_name = match[0] # Aquí puedes decidir qué nombre usar como unificado
matches.append((name, match[0], unified_name)) # Añade la tercera columna con el nombre unificado
else:
matches.append((name, "#N/D", "#N/D")) # No alcanzó el umbral refinado
else:
matches.append((name, "#N/D", "#N/D")) # No alcanzó ningún umbral
return matches
# Aplica la función y guarda los resultados en DataFrames
coincidencias_df1 = pd.DataFrame(
complex_fuzzy_matching(unique_institutions_df1, unique_institutions_df2, 'institucion', 'nombre_institucion', similarity_threshold),
columns=['institucion', 'coincidencia_en_nombre', 'nombre_unificado']
)
coincidencias_df2 = pd.DataFrame(
complex_fuzzy_matching(unique_institutions_df2, unique_institutions_df1, 'nombre_institucion', 'institucion', similarity_threshold),
columns=['nombre_institucion', 'coincidencia_en_institucion', 'nombre_unificado']
)
# Exporta los resultados a Excel
coincidencias_df1.to_excel('coincidencias_complejas_df1.xlsx', index=False)
coincidencias_df2.to_excel('coincidencias_complejas_df2.xlsx', index=False)
print("Archivos exportados: 'coincidencias_complejas_df1.xlsx' y 'coincidencias_complejas_df2.xlsx'")
Reemplazando en ambos dataset por nombre unificado de institucion¶
In [ ]:
import numpy as np
# Merge de los nombres unificados en df1
df1 = df1.merge(coincidencias_df1[['institucion', 'nombre_unificado']], on='institucion', how='left')
# Reemplaza los nombres en df1 por el nombre unificado, pero sólo si no es #N/D y no es NaN
df1['institucion'] = df1.apply(lambda x: x['nombre_unificado'] if pd.notna(x['nombre_unificado']) and x['nombre_unificado'] != '#N/D' else x['institucion'], axis=1)
# Elimina la columna 'nombre_unificado' de df1 si ya no es necesaria
df1 = df1.drop(columns=['nombre_unificado'])
# Merge de los nombres unificados en df2 (utilizando 'coincidencia_en_nombre' para buscar)
df2 = df2.merge(coincidencias_df1[['coincidencia_en_nombre', 'nombre_unificado']],
left_on='nombre_institucion', right_on='coincidencia_en_nombre', how='left')
# Reemplaza los nombres en df2 por el nombre unificado, pero sólo si no es #N/D y no es NaN
df2['nombre_institucion'] = df2.apply(lambda x: x['nombre_unificado'] if pd.notna(x['nombre_unificado']) and x['nombre_unificado'] != '#N/D' else x['nombre_institucion'], axis=1)
# Elimina las columnas innecesarias ('nombre_unificado' y 'coincidencia_en_nombre')
df2 = df2.drop(columns=['nombre_unificado', 'coincidencia_en_nombre'])
TIPO DE INSTITUCION LIMPIEZA¶
In [ ]:
# Función para corregir los tipos de institución según el nombre
def corregir_tipo_institucion(nombre, tipo_actual):
# Asegurarse de que el nombre es una cadena de texto
if isinstance(nombre, str):
nombre = nombre.upper()
else:
nombre = "" # Convertir NaN o valores no string en cadena vacía
# Caso especial: INSTITUTO DE FORMACION TECNICA SUPERIOR INFORTES
if 'INSTITUTO DE FORMACION TECNICA SUPERIOR INFORTES DEPENDIENTE DE LA UNIVERSIDAD EVANGELICA DEL PARAGUAY' in nombre:
return 'INSTITUTO TECNICO SUPERIOR' # Devolver el tipo correcto
if 'INSTITUTO PEDAGOGICO NIHON GAKKO' in nombre:
return 'INSTITUTO DE FORMACION DOCENTE'
# Lista de posibles tipos de institución en orden de prioridad
tipos_institucion = [
('INSTITUTO TECNICO SUPERIOR', 'INSTITUTO TECNICO SUPERIOR'),
('INSTITUTO SUPERIOR', 'INSTITUTO SUPERIOR'),
('INSTITUTO DE FORMACION DOCENTE', 'INSTITUTO DE FORMACION DOCENTE'),
('CENTRO REGIONAL DE EDUCACION', 'CENTRO REGIONAL DE EDUCACION'),
('UNIVERSIDAD', 'UNIVERSIDAD')
]
# Verificar la primera coincidencia en el nombre
for tipo, keyword in tipos_institucion:
if keyword in nombre:
return tipo # Devolver el tipo de institución basado en la primera coincidencia
# Si no hay coincidencias, devolver el tipo actual
return tipo_actual
# Aplicar la corrección en df1
df1['tipo_institucion_corregido'] = df1.apply(lambda x: corregir_tipo_institucion(x['institucion'], x['tipo_institucion']), axis=1)
# Aplicar la corrección en df2
df2['tipo_institucion_corregido'] = df2.apply(lambda x: corregir_tipo_institucion(x['nombre_institucion'], x['tipo_institucion']), axis=1)
# Reemplazar las columnas originales si todo es correcto
df1['tipo_institucion'] = df1['tipo_institucion_corregido']
df2['tipo_institucion'] = df2['tipo_institucion_corregido']
# Eliminar las columnas temporales
df1.drop(columns=['tipo_institucion_corregido'], inplace=True)
df2.drop(columns=['tipo_institucion_corregido'], inplace=True)
DUPLICADOS¶
In [ ]:
# Ver el shape de df1 y df2 antes de eliminar duplicados
print("Shape de df1 antes de eliminar duplicados:", df1.shape)
print("Shape de df2 antes de eliminar duplicados:", df2.shape)
# Eliminar duplicados en df1 y df2
df1=df1.drop_duplicates()
df2=df2.drop_duplicates()
# Ver el shape de df1 y df2 después de eliminar duplicados
print("Shape de df1 después de eliminar duplicados:", df1.shape)
print("Shape de df2 después de eliminar duplicados:", df2.shape)
TITULOS DOCUMENTOS¶
Cantidad de documentos con más de un nombre asociado¶
In [ ]:
df=df1
# Agrupar por 'documento' y contar la cantidad de nombres únicos asociados a cada documento después de la limpieza
conteo_nombres_por_documento_despues = df.groupby('documento')['nombre_completo'].nunique()
# Filtrar los documentos que tienen más de un nombre asociado después de la limpieza
documentos_con_mas_de_un_nombre_despues = conteo_nombres_por_documento_despues[conteo_nombres_por_documento_despues > 1]
# Mostrar la cantidad de documentos con más de un nombre asociado después de la limpieza
cantidad_documentos_con_mas_de_un_nombre_despues = len(documentos_con_mas_de_un_nombre_despues)
print("Cantidad de documentos con más de un nombre asociado después de la limpieza:", cantidad_documentos_con_mas_de_un_nombre_despues)
# Mostrar la lista de documentos con más de un nombre asociado después de la limpieza y la cantidad de nombres diferentes para cada uno
print("\nDocumentos con más de un nombre asociado después de la limpieza y cantidad de nombres diferentes:")
print(documentos_con_mas_de_un_nombre_despues)
# Obtener los nombres asociados a cada documento con más de un nombre después de la limpieza
nombres_por_documento_despues = df[df['documento'].isin(documentos_con_mas_de_un_nombre_despues.index)][['documento', 'nombre_completo']]
# Exportar la lista de documentos con más de un nombre asociado después de la limpieza y los nombres asociados a un archivo Excel
nombres_por_documento_despues.to_excel("nombres_por_documento_despues.xlsx", index=False)
Listar nombres de documentos con mas de un nombre¶
In [ ]:
# Obtener los nombres únicos asociados a cada documento con más de un nombre después de la limpieza y ordenar por documento
nombres_unicos_por_documento_despues = nombres_por_documento_despues.drop_duplicates().sort_values(by='documento')
# Mostrar los nombres únicos asociados a cada documento con más de un nombre después de la limpieza
print("Nombres únicos asociados a cada documento con más de un nombre después de la limpieza:")
print(nombres_unicos_por_documento_despues)
# Exportar los nombres únicos asociados a cada documento con más de un nombre a un archivo Excel, ordenado por documento
nombres_unicos_por_documento_despues.to_excel("nombres_unicos_por_documento_despues.xlsx", index=False)
Detectar similitudes entre los nombres con el mismo documento¶
In [ ]:
import pandas as pd
from difflib import SequenceMatcher
import re
# Cargar el dataframe
dfx = nombres_unicos_por_documento_despues
# Función para calcular la similitud de Levenshtein
def similar(a, b):
return SequenceMatcher(None, a, b).ratio()
# Función para clasificar nombres
def classify_names(group):
results = []
names = group['nombre_completo'].tolist()
for i in range(len(names)):
for j in range(i + 1, len(names)):
similarity = similar(names[i], names[j])
if similarity > 0.8: # Umbral de similitud
results.append((group['documento'].iloc[0], names[i], names[j], 'misma persona'))
else:
results.append((group['documento'].iloc[0], names[i], names[j], 'distintas personas'))
return results
# Aplicar la función a grupos de documentos
results = []
grouped = dfx.groupby('documento')
for name, group in grouped:
if len(group) > 1:
results.extend(classify_names(group))
# Convertir los resultados en un DataFrame
result_df = pd.DataFrame(results, columns=['documento', 'nombre1', 'nombre2', 'clasificacion'])
# Guardar los resultados en un archivo Excel
result_df.to_excel('nombres_clasificados.xlsx', index=False)
# Imprimir la cantidad de clasificaciones por tipo
print(result_df['clasificacion'].value_counts())
result_df.head()
Unificar datos para tener una lista de personas iguales o distintos¶
In [ ]:
import pandas as pd
# Supongamos que los dataframes ya están cargados como nombres_unicos_por_documento_despues y result_df
# Crear una lista para almacenar los diccionarios
aux_list = []
for index, row in result_df.iterrows():
aux_list.append({'documento': row['documento'], 'nombre_completo': row['nombre1'], 'clasificacion': row['clasificacion']})
aux_list.append({'documento': row['documento'], 'nombre_completo': row['nombre2'], 'clasificacion': row['clasificacion']})
# Convertir la lista en un dataframe
aux_df = pd.DataFrame(aux_list)
# Eliminar duplicados
aux_df = aux_df.drop_duplicates()
# Unir nombres_unicos_por_documento_despues con aux_df para agregar la columna de clasificación
merged_df = nombres_unicos_por_documento_despues.merge(aux_df, how='left', left_on=['documento', 'nombre_completo'], right_on=['documento', 'nombre_completo'])
# Verificar el resultado
print(merged_df)
# Guardar el resultado en un archivo de Excel
merged_df.to_excel('nombres_unicos_con_clasificacion.xlsx', index=False)
print("Archivo exportado a 'nombres_unicos_con_clasificacion.xlsx'")
Crear id para documentos con mas de un nombre asociado¶
In [ ]:
# Diccionario para contar ocurrencias por documento
counter = {}
# Función para generar el ID según la clasificación
def generar_id(row):
documento = row['documento']
clasificacion = row['clasificacion']
if clasificacion == 'distintas personas':
if documento not in counter:
counter[documento] = 1
else:
counter[documento] += 1
return f"{documento}_{counter[documento]}"
else:
return documento
# Aplicar la función para generar la columna 'id'
merged_df['id'] = merged_df.apply(generar_id, axis=1)
# Mostrar el dataframe resultante
print(merged_df)
merged_df.to_excel('documentos_duplicados_con_id.xlsx', index=False)
Agregar el nuevo campo persona_id a todo el dataset, ordenar y exportar¶
In [ ]:
import pandas as pd
from tqdm import tqdm
# Configurar tqdm para que funcione con pandas
tqdm.pandas()
# Crear un backup del DataFrame original
df_backup = df1.copy()
# Hacer un merge entre df y merged_df basado en 'documento' y 'nombre_completo'
df_titulos_v2 = pd.merge(df1, merged_df[['documento', 'nombre_completo', 'id']].progress_apply(lambda x: x), on=['documento', 'nombre_completo'], how='left')
# Crear la columna 'persona_id' basada en las condiciones especificadas
df_titulos_v2['persona_id'] = df_titulos_v2['id'].combine_first(df_titulos_v2['documento'])
# Eliminar la columna 'id' ya que no la necesitamos en el DataFrame final
df_titulos_v2 = df_titulos_v2.drop(columns=['id'])
# Ordenar el DataFrame por 'documento' y 'persona_id'
df_titulos_v2 = df_titulos_v2.sort_values(by=['documento', 'persona_id'])
# Mostrar el DataFrame resultante
print(df_titulos_v2.shape)
# Exportar todo el DataFrame a un solo archivo CSV
df_titulos_v2.to_csv('df_titulos_v2.csv', index=False)
print('df exportado a CSV en un solo archivo')
Fragmentando dataset para facilitar trabajar sobre secciones¶
In [ ]:
import pandas as pd
# Función para limpiar las variantes de ciclos y espacios dobles
def limpiar_ciclos(texto):
if isinstance(texto, str): # Asegurarse de que el texto sea una cadena
# Reemplazar variantes de "TERCER CICLO"
texto = re.sub(r'\b(3(O|0)?|TERCERO|TERCER|3(DEG)?|30)\s*CICLO(S)?\b', 'TERCER CICLO', texto, flags=re.IGNORECASE)
# Reemplazar variantes de "SEGUNDO CICLO"
texto = re.sub(r'\b(2(O|0)?|SEGUNDO|2(DEG)?|20)\s*CICLO(S)?\b', 'SEGUNDO CICLO', texto, flags=re.IGNORECASE)
# Reemplazar variantes de "PRIMER CICLO"
texto = re.sub(r'\b(1(O|0)?|PRIMERO|PRIMER|1(DEG)?|10)\s*CICLO(S)?\b', 'PRIMER CICLO', texto, flags=re.IGNORECASE)
# Eliminar espacios dobles
texto = re.sub(r'\s+', ' ', texto).strip()
return texto
# Filtrar las columnas necesarias de df1 y df2
titulos = df_titulos_v2[['anio', 'mes', 'documento', 'persona_id', 'nombre_completo', 'tipo_institucion', 'institucion', 'carrera', 'titulo', 'sexo']]
carreras = df2[['tipo_institucion', 'nombre_institucion', 'denominacion_carrera', 'denominacion_titulo', 'tipo_gestion', 'nivel_titulacion', 'clasificacion_campo_amplio']]
carreras.rename(columns={
'nombre_institucion': 'institucion',
'denominacion_carrera': 'carrera',
'denominacion_titulo': 'titulo'
}, inplace=True)
# Aplicar la función de limpieza a las columnas 'carrera' y 'titulo' en ambos DataFrames
titulos.loc[:, 'carrera'] = titulos['carrera'].apply(limpiar_ciclos)
titulos.loc[:, 'titulo'] = titulos['titulo'].apply(limpiar_ciclos)
carreras.loc[:, 'carrera'] = carreras['carrera'].apply(limpiar_ciclos)
carreras.loc[:, 'titulo'] = carreras['titulo'].apply(limpiar_ciclos)
# Diccionario para los tipos de institución y sus siglas
tipo_institucion_siglas = {
'CENTRO REGIONAL DE EDUCACION': 'CRE',
'INSTITUTO DE FORMACION DOCENTE': 'IFD',
'INSTITUTO SUPERIOR': 'IS',
'INSTITUTO TECNICO SUPERIOR': 'ITS',
'UNIVERSIDAD': 'U'
}
# Dividir titulos en fragmentos según el tipo de institución
for tipo, sigla in tipo_institucion_siglas.items():
fragmento = titulos[titulos['tipo_institucion'] == tipo]
fragmento.to_excel(f'titulos_{sigla}.xlsx', index=False)
# Dividir carreras en fragmentos según el tipo de institución
for tipo, sigla in tipo_institucion_siglas.items():
fragmento = carreras[carreras['tipo_institucion'] == tipo]
fragmento.to_excel(f'carreras_{sigla}.xlsx', index=False)
print("Archivos exportados según tipo de institución.")
CARGAR DATAFRAMES¶
In [ ]:
import pandas as pd
# Cargar los archivos Excel que contienen los DataFrames por tipo de institución
titulos_cre = pd.read_excel('titulos_CRE.xlsx')
titulos_ifd = pd.read_excel('titulos_IFD.xlsx')
titulos_is = pd.read_excel('titulos_IS.xlsx')
titulos_its = pd.read_excel('titulos_ITS.xlsx')
titulos_u = pd.read_excel('titulos_U.xlsx')
carreras_cre = pd.read_excel('carreras_CRE.xlsx')
carreras_ifd = pd.read_excel('carreras_IFD.xlsx')
carreras_is = pd.read_excel('carreras_IS.xlsx')
carreras_its = pd.read_excel('carreras_ITS.xlsx')
carreras_u = pd.read_excel('carreras_U.xlsx')
# Verificar la carga de los DataFrames
print("Títulos CRE shape:", titulos_cre.shape)
print("Títulos IFD shape:", titulos_ifd.shape)
print("Títulos IS shape:", titulos_is.shape)
print("Títulos ITS shape:", titulos_its.shape)
print("Títulos U shape:", titulos_u.shape)
print("Carreras CRE shape:", carreras_cre.shape)
print("Carreras IFD shape:", carreras_ifd.shape)
print("Carreras IS shape:", carreras_is.shape)
print("Carreras ITS shape:", carreras_its.shape)
print("Carreras U shape:", carreras_u.shape)
CENTRO REGIONAL DE EDUCACION¶
In [ ]:
print("Correcciones realizadas en titulos_cre:")
print(titulos_cre['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
print("\nCorrecciones realizadas en carreras_cre:")
print(carreras_cre['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
instituciones que solo estan en un lugar¶
In [ ]:
# Valores únicos en 'titulos_cre'
instituciones_titulos = set(titulos_cre['institucion'].unique())
# Valores únicos en 'carreras_cre'
instituciones_carreras = set(carreras_cre['institucion'].unique())
# Instituciones que están en 'titulos_cre' pero no en 'carreras_cre'
solo_en_titulos = instituciones_titulos - instituciones_carreras
print("Instituciones que están solo en titulos_cre:")
print(solo_en_titulos)
# Instituciones que están en 'carreras_cre' pero no en 'titulos_cre'
solo_en_carreras = instituciones_carreras - instituciones_titulos
print("\nInstituciones que están solo en carreras_cre:")
print(solo_en_carreras)
limpieza de inconsistencias¶
In [ ]:
import pandas as pd
# Función para limpiar nombres específicos
def limpiar_instituciones(nombre, es_carreras=False):
nombre = nombre.upper()
# Correcciones generales para ambos datasets
if "DRRAUL PENA" in nombre:
nombre = nombre.replace("DRRAUL PENA", "DOCTOR RAUL PENA")
# Correcciones específicas para carreras_cre
if es_carreras:
if "GENERAL PATRICIO ESCOBAR DE ENCARNACION" in nombre:
nombre = nombre.replace("GENERAL PATRICIO ESCOBAR DE ENCARNACION", "GENERAL PATRICIO ESCOBAR")
if "DOCTOR RAUL PENA DE PEDRO JUAN CABALLERO" in nombre:
nombre = nombre.replace("DOCTOR RAUL PENA DE PEDRO JUAN CABALLERO", "DOCTOR RAUL PENA")
return nombre
# Aplicar la limpieza en ambos datasets
titulos_cre['institucion'] = titulos_cre['institucion'].apply(limpiar_instituciones)
carreras_cre['institucion'] = carreras_cre['institucion'].apply(lambda x: limpiar_instituciones(x, es_carreras=True))
# Limpiar el nombre de la carrera reemplazando solo la parte específica
carreras_cre['carrera'] = carreras_cre['carrera'].replace(
'PROFESIONALIZADONANEMOARANDUKE',
'PROFESIONALIZADO',
regex=True # Esto permite la búsqueda con expresiones regulares
)
# Limpiar el nombre de la carrera reemplazando solo la parte específica
titulos_cre['carrera'] = titulos_cre['carrera'].replace(
'PROFESIONALIZADONANEMOARANDUKE',
'PROFESIONALIZADO',
regex=True # Esto permite la búsqueda con expresiones regulares
)
# Mostrar las instituciones corregidas
print("Instituciones corregidas en titulos_cre:")
print(titulos_cre['institucion'].unique())
print("\nInstituciones corregidas en carreras_cre:")
print(carreras_cre['institucion'].unique())
verificar valores nulos¶
In [ ]:
import pandas as pd
# Contar valores nulos en cada columna de titulos_cre
print("Valores nulos en titulos_cre:")
print(titulos_cre.isnull().sum())
# Contar valores nulos en cada columna de carreras_cre
print("\nValores nulos en carreras_cre:")
print(carreras_cre.isnull().sum())
campos vacios en carreras cre¶
clasificacion de campo amplio¶
In [ ]:
import pandas as pd
# Contar los valores vacíos (NaN) en cada columna
valores_vacios = carreras_cre.isna().sum()
# Mostrar el total de valores vacíos por columna antes de completar
print("Total de valores vacíos por columna antes de completar:")
print(valores_vacios)
# Llenar los campos vacíos en clasificacion_campo_amplio con 'EDUCACION'
carreras_cre['clasificacion_campo_amplio'] = carreras_cre['clasificacion_campo_amplio'].fillna('EDUCACION')
# Volver a contar los valores vacíos después de la operación
valores_vacios_actualizados = carreras_cre.isna().sum()
# Mostrar el total de valores vacíos por columna después de completar
print("\nTotal de valores vacíos por columna después de completar:")
print(valores_vacios_actualizados)
# Exportar el dataframe resultante a un archivo Excel
carreras_cre.to_excel('carreras_cre_actualizado.xlsx', index=False)
tipo de gestion¶
In [ ]:
import pandas as pd
# Contar el tipo de gestión más frecuente para cada institución
def completar_tipo_gestion(grupo):
if grupo['tipo_gestion'].isnull().all():
return grupo
# Obtener el tipo de gestión más frecuente (eliminando los nulos)
tipo_frecuente = grupo['tipo_gestion'].dropna().mode()[0]
# Rellenar los valores nulos con el tipo de gestión más frecuente
grupo['tipo_gestion'] = grupo['tipo_gestion'].fillna(tipo_frecuente)
return grupo
# Aplicar la función a cada grupo de instituciones sin convertirlo en índice
carreras_cre = carreras_cre.groupby('institucion', as_index=False).apply(completar_tipo_gestion)
# Guardar el resultado en un archivo Excel
carreras_cre.to_excel('carreras_cre_actualizado.xlsx', index=False)
# Verificar si aún hay valores nulos en la columna 'tipo_gestion'
print(carreras_cre['tipo_gestion'].isnull().sum())
unificacion carreras y titulos cre¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_cre_copy = titulos_cre.copy()
carreras_cre_copy = carreras_cre.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_cre_copy = titulos_cre_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_cre_copy = carreras_cre_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_cre_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_cre_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_CRE.xlsx', index=False)
carreras_cre_copy = carreras_cre_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_cre_copy = carreras_cre_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
LIMPIANDO INCONSISTENCIAS¶
In [ ]:
from fuzzywuzzy import fuzz
import pandas as pd
# Función para extraer la parte posterior a 'AREA'
def extract_after_area(text):
if 'AREA' in text:
return text.split('AREA', 1)[-1].strip() # Extraer lo que sigue después de 'AREA'
return None # Si no tiene 'AREA', devolver None
# Función de coincidencia difusa optimizada
def fuzzy_unify(row, carreras_cre, threshold=75):
best_match = None
best_score = 0
carreras_cre_filtered = carreras_cre[
(carreras_cre['institucion_carrera'] == row['institucion']) &
(carreras_cre['tipo_institucion'] == row['tipo_institucion'])
]
row['carrera_carreras'] = None
row['titulo_carrera'] = None
row['institucion_carrera'] = None
for _, carrera_row in carreras_cre_filtered.iterrows():
# Extraer partes después de 'AREA'
area_row_1 = extract_after_area(row['carrera'])
area_row_2 = extract_after_area(carrera_row['carrera_carreras'])
if area_row_1 and area_row_2: # Ambas tienen 'AREA'
area_score = fuzz.ratio(area_row_1, area_row_2)
if area_score < 70: # Si la parte después de 'AREA' no es similar
avg_score = 60 # Asignar un puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
elif area_row_1 or area_row_2: # Solo una de las partes tiene 'AREA'
avg_score = 60 # Puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
if avg_score > best_score:
best_score = avg_score
best_match = carrera_row
if best_score >= threshold and best_match is not None:
row['carrera_unificada'] = best_match['carrera_carreras']
row['titulo_unificado'] = best_match['titulo_carrera']
row['institucion_unificada'] = best_match['institucion_carrera']
row['carrera_carreras'] = best_match['carrera_carreras']
row['titulo_carrera'] = best_match['titulo_carrera']
row['institucion_carrera'] = best_match['institucion_carrera']
else:
row['carrera_unificada'] = row['carrera']
row['titulo_unificado'] = row['titulo_titulos']
row['institucion_unificada'] = row['institucion']
row['similarity_score'] = best_score
return row
# Aplicar la coincidencia difusa
left_only_unified = left_only.apply(fuzzy_unify, args=(carreras_cre_copy,), axis=1)
left_only_unified = left_only_unified[[
'carrera',
'carrera_carreras',
'carrera_unificada',
'titulo_titulos', # Cambiado de posición
'titulo_carrera',
'titulo_unificado',
'institucion',
'institucion_carrera',
'institucion_unificada',
'similarity_score'
]]
# Exportar a un archivo Excel
left_only_unified.to_excel('unificacion_left_only_cre.xlsx', index=False)
print("Archivo exportado: 'unificacion_left_only_cre.xlsx'")
Reemplazando campos¶
In [ ]:
# Paso 1: Filtrar las filas de left_only_unified con un puntaje mayor o igual a 90
left_only_unified_filtered = left_only_unified[left_only_unified['similarity_score'] >= 90]
# Paso 2: Reemplazar los valores en titulos_cre
for _, row in left_only_unified_filtered.iterrows():
# Buscar coincidencias en titulos_cre
condition = (
(titulos_cre['carrera'] == row['carrera']) &
(titulos_cre['titulo'] == row['titulo_titulos']) &
(titulos_cre['institucion'] == row['institucion'])
)
# Reemplazar los valores en titulos_cre si hay coincidencias
titulos_cre.loc[condition, 'carrera'] = row['carrera_unificada']
titulos_cre.loc[condition, 'titulo'] = row['titulo_unificado']
# Puedes guardar el DataFrame actualizado en un archivo Excel o CSV si lo deseas
titulos_cre.to_excel('titulos_cre_actualizado.xlsx', index=False)
print("Archivo exportado: 'titulos_cre_actualizado.xlsx'")
Segunda revision #OPCIONAL¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_cre_copy = titulos_cre.copy()
carreras_cre_copy = carreras_cre.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_cre_copy = titulos_cre_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_cre_copy = carreras_cre_copy.rename(columns={'titulo': 'titulo_carreras'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_cre_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_cre_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carreras']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carreras'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_CRE.xlsx', index=False)
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
UNIFICACION FINAL CRE¶
In [ ]:
import pandas as pd
# Paso 1: Eliminar duplicados en las columnas clave de carreras
df_merged_unique = carreras_cre.drop_duplicates(
subset=['carrera', 'institucion', 'tipo_institucion', 'titulo']
)
# Paso 2: Realizar el merge con las combinaciones únicas
merged_final_CRE = pd.merge(
titulos_cre,
df_merged_unique,
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
how='left' # Aquí cambiamos a 'left' para mantener todas las filas de df_titulos_v2_limpio_CRE
)
# Paso 2: Guardar el DataFrame en un archivo Excel
merged_final_CRE.to_excel('CRE_FINAL.xlsx', index=False)
# Imprimir la forma y los encabezados de df_titulos_v2_limpio_CRE
print("Shape del DataFrame TITULOS V2:", titulos_cre.shape)
# Imprimir la forma y los encabezados del DataFrame final
print("Shape del DataFrame final:", merged_final_CRE.shape)
INSTITUTO DE FORMACION DOCENTE¶
In [ ]:
print("Correcciones realizadas en titulos_ifd:")
print(titulos_ifd['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
print("\nCorrecciones realizadas en carreras_ifd:")
print(carreras_ifd['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
INSTITUCIONES QUE SOLO ESTAN EN UN LUGAR¶
In [ ]:
# Valores únicos en 'titulos_cre'
instituciones_titulos = set(titulos_ifd['institucion'].unique())
# Valores únicos en 'carreras_cre'
instituciones_carreras = set(carreras_ifd['institucion'].unique())
# Instituciones que están en 'titulos_cre' pero no en 'carreras_cre'
solo_en_titulos = instituciones_titulos - instituciones_carreras
print("Instituciones que están solo en titulos_ifd:")
print(solo_en_titulos)
# Instituciones que están en 'carreras_cre' pero no en 'titulos_cre'
solo_en_carreras = instituciones_carreras - instituciones_titulos
print("\nInstituciones que están solo en carreras_ifd:")
print(solo_en_carreras)
VERIFICANDO CAMPOS VACIOS¶
In [ ]:
import pandas as pd
# Contar valores nulos en cada columna de titulos_cre
print("Valores nulos en titulos_ifd:")
print(titulos_ifd.isnull().sum())
# Contar valores nulos en cada columna de carreras_cre
print("\nValores nulos en carreras_ifd:")
print(carreras_ifd.isnull().sum())
campos vacios en ifd¶
campo amplio¶
In [ ]:
import pandas as pd
# Contar los valores vacíos (NaN) en cada columna
valores_vacios = carreras_ifd.isna().sum()
# Mostrar el total de valores vacíos por columna antes de completar
print("Total de valores vacíos por columna antes de completar:")
print(valores_vacios)
# Llenar los campos vacíos en clasificacion_campo_amplio con 'EDUCACION'
carreras_ifd['clasificacion_campo_amplio'] = carreras_ifd['clasificacion_campo_amplio'].fillna('EDUCACION')
# Volver a contar los valores vacíos después de la operación
valores_vacios_actualizados = carreras_ifd.isna().sum()
# Mostrar el total de valores vacíos por columna después de completar
print("\nTotal de valores vacíos por columna después de completar:")
print(valores_vacios_actualizados)
# Exportar el dataframe resultante a un archivo Excel
carreras_ifd.to_excel('carreras_ifd_actualizado.xlsx', index=False)
tipo de gestion¶
In [ ]:
import pandas as pd
# Contar el tipo de gestión más frecuente para cada institución
def completar_tipo_gestion(grupo):
if grupo['tipo_gestion'].isnull().all():
return grupo
# Obtener el tipo de gestión más frecuente (eliminando los nulos)
tipo_frecuente = grupo['tipo_gestion'].dropna().mode()[0]
# Rellenar los valores nulos con el tipo de gestión más frecuente
grupo['tipo_gestion'] = grupo['tipo_gestion'].fillna(tipo_frecuente)
return grupo
# Aplicar la función a cada grupo de instituciones sin convertirlo en índice
carreras_ifd = carreras_ifd.groupby('institucion', as_index=False).apply(completar_tipo_gestion)
# Guardar el resultado en un archivo Excel
carreras_ifd.to_excel('carreras_ifd_actualizado.xlsx', index=False)
# Verificar si aún hay valores nulos en la columna 'tipo_gestion'
print(carreras_cre['tipo_gestion'].isnull().sum())
Verificando inconsistencias¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_ifd_copy = titulos_ifd.copy()
carreras_ifd_copy = carreras_ifd.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_ifd_copy = titulos_ifd_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_ifd_copy = carreras_ifd_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_ifd_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_ifd_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_IFD.xlsx', index=False)
carreras_ifd_copy = carreras_ifd_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_ifd_copy = carreras_ifd_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
limpiando inconsistencias ifd¶
In [ ]:
from fuzzywuzzy import fuzz
import pandas as pd
# Función para extraer la parte posterior a 'AREA'
def extract_after_area(text):
if 'AREA' in text:
return text.split('AREA', 1)[-1].strip() # Extraer lo que sigue después de 'AREA'
return None # Si no tiene 'AREA', devolver None
# Función de coincidencia difusa optimizada
def fuzzy_unify(row, carreras_ifd, threshold=75):
best_match = None
best_score = 0
carreras_ifd_filtered = carreras_ifd[
(carreras_ifd['institucion_carrera'] == row['institucion']) &
(carreras_ifd['tipo_institucion'] == row['tipo_institucion'])
]
row['carrera_carreras'] = None
row['titulo_carrera'] = None
row['institucion_carrera'] = None
for _, carrera_row in carreras_ifd_filtered.iterrows():
# Extraer partes después de 'AREA'
area_row_1 = extract_after_area(row['carrera'])
area_row_2 = extract_after_area(carrera_row['carrera_carreras'])
if area_row_1 and area_row_2: # Ambas tienen 'AREA'
area_score = fuzz.ratio(area_row_1, area_row_2)
if area_score < 70: # Si la parte después de 'AREA' no es similar
avg_score = 60 # Asignar un puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
elif area_row_1 or area_row_2: # Solo una de las partes tiene 'AREA'
avg_score = 60 # Puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
if avg_score > best_score:
best_score = avg_score
best_match = carrera_row
if best_score >= threshold and best_match is not None:
row['carrera_unificada'] = best_match['carrera_carreras']
row['titulo_unificado'] = best_match['titulo_carrera']
row['institucion_unificada'] = best_match['institucion_carrera']
row['carrera_carreras'] = best_match['carrera_carreras']
row['titulo_carrera'] = best_match['titulo_carrera']
row['institucion_carrera'] = best_match['institucion_carrera']
else:
row['carrera_unificada'] = row['carrera']
row['titulo_unificado'] = row['titulo_titulos']
row['institucion_unificada'] = row['institucion']
row['similarity_score'] = best_score
return row
# Aplicar la coincidencia difusa
left_only_unified = left_only.apply(fuzzy_unify, args=(carreras_ifd_copy,), axis=1)
# Seleccionar las columnas en el orden deseado
left_only_unified = left_only_unified[[
'carrera',
'carrera_carreras',
'carrera_unificada',
'titulo_titulos',
'titulo_carrera',
'titulo_unificado',
'institucion',
'institucion_carrera',
'institucion_unificada',
'similarity_score'
]]
# Exportar a un archivo Excel con el nombre modificado
left_only_unified.to_excel('unificacion_left_only_ifd.xlsx', index=False)
print("Archivo exportado: 'unificacion_left_only_ifd.xlsx'")
REEMPLAZAR CAMPOS¶
In [ ]:
# Paso 1: Filtrar las filas de left_only_unified con un puntaje mayor o igual a 90
left_only_unified_filtered = left_only_unified[left_only_unified['similarity_score'] >= 89.5]
# Paso 2: Reemplazar los valores en titulos_cre
for _, row in left_only_unified_filtered.iterrows():
# Buscar coincidencias en titulos_cre
condition = (
(titulos_ifd['carrera'] == row['carrera']) &
(titulos_ifd['titulo'] == row['titulo_titulos']) &
(titulos_ifd['institucion'] == row['institucion'])
)
# Reemplazar los valores en titulos_cre si hay coincidencias
titulos_ifd.loc[condition, 'carrera'] = row['carrera_unificada']
titulos_ifd.loc[condition, 'titulo'] = row['titulo_unificado']
# Puedes guardar el DataFrame actualizado en un archivo Excel o CSV si lo deseas
titulos_ifd.to_excel('titulos_ifd_actualizado.xlsx', index=False)
print("Archivo exportado: 'titulos_ifd_actualizado.xlsx'")
SEGUNDA REVISION #OPCIONAL¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_ifd_copy = titulos_ifd.copy()
carreras_ifd_copy = carreras_ifd.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_ifd_copy = titulos_ifd_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_ifd_copy = carreras_ifd_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_ifd_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_ifd_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_IFD.xlsx', index=False)
carreras_ifd_copy = carreras_ifd_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_ifd_copy = carreras_ifd_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
UNIFICACION FINAL IFD¶
In [ ]:
import pandas as pd
# Paso 1: Eliminar duplicados en las columnas clave de carreras
df_merged_unique = carreras_ifd.drop_duplicates(
subset=['carrera', 'institucion', 'tipo_institucion', 'titulo']
)
# Paso 2: Realizar el merge con las combinaciones únicas
merged_final_IFD = pd.merge(
titulos_ifd,
df_merged_unique,
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
how='left' # Aquí cambiamos a 'left' para mantener todas las filas de df_titulos_v2_limpio_CRE
)
# Paso 2: Guardar el DataFrame en un archivo Excel
merged_final_IFD.to_excel('IFD_FINAL.xlsx', index=False)
# Imprimir la forma y los encabezados de df_titulos_v2_limpio_CRE
print("Shape del DataFrame TITULOS V2:", titulos_ifd.shape)
# Imprimir la forma y los encabezados del DataFrame final
print("Shape del DataFrame final:", merged_final_IFD.shape)
INSTITUTO SUPERIOR¶
In [ ]:
print("Correcciones realizadas en titulos_is:")
print(titulos_is['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
print("\nCorrecciones realizadas en carreras_is:")
print(carreras_is['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
INSTITUCIONES QUE ESTAN EN UN SOLO LUGAR¶
In [ ]:
# Valores únicos en 'titulos_cre'
instituciones_titulos = set(titulos_is['institucion'].unique())
# Valores únicos en 'carreras_cre'
instituciones_carreras = set(carreras_is['institucion'].unique())
# Instituciones que están en 'titulos_cre' pero no en 'carreras_cre'
solo_en_titulos = instituciones_titulos - instituciones_carreras
print("Instituciones que están solo en titulos_is:")
print(solo_en_titulos)
# Instituciones que están en 'carreras_cre' pero no en 'titulos_cre'
solo_en_carreras = instituciones_carreras - instituciones_titulos
print("\nInstituciones que están solo en carreras_is:")
print(solo_en_carreras)
Limpieza general¶
In [ ]:
titulos_is['institucion'] = titulos_is['institucion'].replace(
'INSTITUTO SUPERIOR VIA PRO DESARROLLO',
'INSTITUTO SUPERIOR DE EDUCACION VIA PRO DESARROLLO'
)
campos vacios¶
In [ ]:
import pandas as pd
# Contar valores nulos en cada columna de titulos_cre
print("Valores nulos en titulos_is:")
print(titulos_is.isnull().sum())
# Contar valores nulos en cada columna de carreras_cre
print("\nValores nulos en carreras_is:")
print(carreras_is.isnull().sum())
campos vacios¶
campos amplio¶
In [ ]:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from tqdm import tqdm
# Concatenar los dataframes
df_combined = pd.concat([carreras_is, carreras_ifd, carreras_cre], ignore_index=True)
# Filtrar filas donde clasificacion_campo_amplio no esté vacío para entrenamiento
df_train = df_combined.dropna(subset=['clasificacion_campo_amplio'])
# Filtrar filas donde clasificacion_campo_amplio esté vacío para predicción
df_predict = df_combined[df_combined['clasificacion_campo_amplio'].isna()]
# Vectorización de la columna 'carrera' utilizando TF-IDF
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(df_train['carrera'])
X_predict = vectorizer.transform(df_predict['carrera'])
# Definir el target
y_train = df_train['clasificacion_campo_amplio']
# División de los datos de entrenamiento para validación
X_train_split, X_val_split, y_train_split, y_val_split = train_test_split(X_train, y_train, test_size=0.2, random_state=42)
# Entrenar el modelo
model = LogisticRegression(max_iter=1000)
model.fit(X_train_split, y_train_split)
# Validar el modelo
y_val_pred = model.predict(X_val_split)
print("Reporte de clasificación para datos de validación:")
print(classification_report(y_val_split, y_val_pred, zero_division=0))
# Predecir los valores faltantes en df_merged_IS y añadir el asterisco
def predecir_y_marcar(row, model, vectorizer):
if pd.isna(row['clasificacion_campo_amplio']):
prediccion = model.predict(vectorizer.transform([row['carrera']]))[0]
return f"{prediccion}*"
else:
return row['clasificacion_campo_amplio']
carreras_is['clasificacion_campo_amplio'] = carreras_is.apply(
lambda row: predecir_y_marcar(row, model, vectorizer),
axis=1
)
# Exportar el dataframe resultante a Excel
carreras_is.to_excel('carreras_is_actualizado.xlsx', index=False)
# Conteo de valores vacíos por columna después de la predicción
valores_vacios_post_prediccion = carreras_is.isna().sum()
print("Total de valores vacíos por columna después de la predicción:")
print(valores_vacios_post_prediccion)
tipo de gestion¶
In [ ]:
import pandas as pd
# Contar el tipo de gestión más frecuente para cada institución
def completar_tipo_gestion(grupo):
if grupo['tipo_gestion'].isnull().all():
return grupo
# Obtener el tipo de gestión más frecuente (eliminando los nulos)
tipo_frecuente = grupo['tipo_gestion'].dropna().mode()[0]
# Rellenar los valores nulos con el tipo de gestión más frecuente
grupo['tipo_gestion'] = grupo['tipo_gestion'].fillna(tipo_frecuente)
return grupo
# Aplicar la función a cada grupo de instituciones sin convertirlo en índice
carreras_is = carreras_is.groupby('institucion', as_index=False).apply(completar_tipo_gestion)
# Guardar el resultado en un archivo Excel
carreras_is.to_excel('carreras_is_actualizado.xlsx', index=False)
# Verificar si aún hay valores nulos en la columna 'tipo_gestion'
print(carreras_is['tipo_gestion'].isnull().sum())
Verificacion de inconsistencias¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_is_copy = titulos_is.copy()
carreras_is_copy = carreras_is.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_is_copy = titulos_is_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_is_copy = carreras_is_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_is_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_is_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_IS.xlsx', index=False)
carreras_is_copy = carreras_is_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_is_copy = carreras_is_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
Limpieza de inconsistencias IS¶
In [ ]:
from fuzzywuzzy import fuzz
import pandas as pd
# Función para extraer la parte posterior a 'AREA'
def extract_after_area(text):
if 'AREA' in text:
return text.split('AREA', 1)[-1].strip() # Extraer lo que sigue después de 'AREA'
return None # Si no tiene 'AREA', devolver None
# Función para verificar palabras clave
def check_keywords(text1, text2):
keywords = ['CIENCIAS BASICAS', 'CIENCIAS SOCIALES', 'FISIOTERAPIA', 'KINESIOLOGIA', 'PSICOLOGIA', 'EDUCACION FISICA']
for keyword in keywords:
if keyword in text1 and keyword not in text2:
return True
if keyword in text2 and keyword not in text1:
return True
return False
# Función de coincidencia difusa optimizada
def fuzzy_unify(row, carreras_is, threshold=75):
best_match = None
best_score = 0
carreras_is_filtered = carreras_is[
(carreras_is['institucion_carrera'] == row['institucion']) &
(carreras_is['tipo_institucion'] == row['tipo_institucion'])
]
row['carrera_carreras'] = None
row['titulo_carrera'] = None
row['institucion_carrera'] = None
for _, carrera_row in carreras_is_filtered.iterrows():
# Extraer partes después de 'AREA'
area_row_1 = extract_after_area(row['carrera'])
area_row_2 = extract_after_area(carrera_row['carrera_carreras'])
if area_row_1 and area_row_2: # Ambas tienen 'AREA'
area_score = fuzz.ratio(area_row_1, area_row_2)
if area_score < 70: # Si la parte después de 'AREA' no es similar
avg_score = 60 # Asignar un puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
elif area_row_1 or area_row_2: # Solo una de las partes tiene 'AREA'
avg_score = 60 # Puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
# Verificar palabras clave
if check_keywords(row['carrera'], carrera_row['carrera_carreras']):
avg_score *= 0.5 # Reducir el puntaje a la mitad si hay un desajuste en las palabras clave
if avg_score > best_score:
best_score = avg_score
best_match = carrera_row
if best_score >= threshold and best_match is not None:
row['carrera_unificada'] = best_match['carrera_carreras']
row['titulo_unificado'] = best_match['titulo_carrera']
row['institucion_unificada'] = best_match['institucion_carrera']
row['carrera_carreras'] = best_match['carrera_carreras']
row['titulo_carrera'] = best_match['titulo_carrera']
row['institucion_carrera'] = best_match['institucion_carrera']
else:
row['carrera_unificada'] = row['carrera']
row['titulo_unificada'] = row['titulo_titulos']
row['institucion_unificada'] = row['institucion']
row['similarity_score'] = best_score
return row
# Aplicar la coincidencia difusa
left_only_unified = left_only.apply(fuzzy_unify, args=(carreras_is_copy,), axis=1)
# Seleccionar las columnas en el orden deseado
left_only_unified = left_only_unified[[
'carrera',
'carrera_carreras',
'carrera_unificada',
'titulo_titulos',
'titulo_carrera',
'titulo_unificado',
'institucion',
'institucion_carrera',
'institucion_unificada',
'similarity_score'
]]
# Exportar a un archivo Excel con el nombre modificado
left_only_unified.to_excel('unificacion_left_only_is.xlsx', index=False)
print("Archivo exportado: 'unificacion_left_only_is.xlsx'")
REEMPLAZAR CAMPOS¶
In [ ]:
# Paso 1: Filtrar las filas de left_only_unified con un puntaje mayor o igual a 90
left_only_unified_filtered = left_only_unified[left_only_unified['similarity_score'] >= 89.5]
# Paso 2: Reemplazar los valores en titulos_cre
for _, row in left_only_unified_filtered.iterrows():
# Buscar coincidencias en titulos_cre
condition = (
(titulos_is['carrera'] == row['carrera']) &
(titulos_is['titulo'] == row['titulo_titulos']) &
(titulos_is['institucion'] == row['institucion'])
)
# Reemplazar los valores en titulos_cre si hay coincidencias
titulos_is.loc[condition, 'carrera'] = row['carrera_unificada']
titulos_is.loc[condition, 'titulo'] = row['titulo_unificado']
# Puedes guardar el DataFrame actualizado en un archivo Excel o CSV si lo deseas
titulos_is.to_excel('titulos_is_actualizado.xlsx', index=False)
print("Archivo exportado: 'titulos_is_actualizado.xlsx'")
SEGUNDA VERIFICACION¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_is_copy = titulos_is.copy()
carreras_is_copy = carreras_is.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_is_copy = titulos_is_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_is_copy = carreras_is_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_is_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_is_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_IS.xlsx', index=False)
carreras_is_copy = carreras_is_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_is_copy = carreras_is_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
UNION FINAL IS¶
In [ ]:
import pandas as pd
# Paso 1: Eliminar duplicados en las columnas clave de carreras
df_merged_unique = carreras_is.drop_duplicates(
subset=['carrera', 'institucion', 'tipo_institucion', 'titulo']
)
# Paso 2: Realizar el merge con las combinaciones únicas
merged_final_IS = pd.merge(
titulos_is,
df_merged_unique,
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
how='left' # Aquí cambiamos a 'left' para mantener todas las filas de df_titulos_v2_limpio_CRE
)
# Paso 2: Guardar el DataFrame en un archivo Excel
merged_final_IS.to_excel('IS_FINAL.xlsx', index=False)
# Imprimir la forma y los encabezados de df_titulos_v2_limpio_CRE
print("Shape del DataFrame TITULOS V2:", titulos_is.shape)
# Imprimir la forma y los encabezados del DataFrame final
print("Shape del DataFrame final:", merged_final_IS.shape)
INSTITUTO TECNICO SUPERIOR¶
In [ ]:
print("Correcciones realizadas en titulos_its:")
print(titulos_its['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
print("\nCorrecciones realizadas en carreras_its:")
print(carreras_its['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
INSTITUTOS QUE ESTAN EN UN SOLO LUGAR¶
In [ ]:
# Valores únicos en 'titulos_cre'
instituciones_titulos = set(titulos_its['institucion'].unique())
# Valores únicos en 'carreras_cre'
instituciones_carreras = set(carreras_its['institucion'].unique())
# Instituciones que están en 'titulos_cre' pero no en 'carreras_cre'
solo_en_titulos = instituciones_titulos - instituciones_carreras
print("Instituciones que están solo en titulos_its:")
print(solo_en_titulos)
# Instituciones que están en 'carreras_cre' pero no en 'titulos_cre'
solo_en_carreras = instituciones_carreras - instituciones_titulos
print("\nInstituciones que están solo en carreras_its:")
print(solo_en_carreras)
verificar valores nulos¶
In [ ]:
import pandas as pd
# Contar valores nulos en cada columna de titulos_cre
print("Valores nulos en titulos_its:")
print(titulos_its.isnull().sum())
# Contar valores nulos en cada columna de carreras_cre
print("\nValores nulos en carreras_its:")
print(carreras_its.isnull().sum())
campo amplio¶
In [ ]:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from tqdm import tqdm
# Concatenar los dataframes
df_combined = pd.concat([carreras_is, carreras_ifd, carreras_cre, carreras_its], ignore_index=True)
# Filtrar filas donde clasificacion_campo_amplio no esté vacío para entrenamiento
df_train = df_combined.dropna(subset=['clasificacion_campo_amplio'])
# Filtrar filas donde clasificacion_campo_amplio esté vacío para predicción
df_predict = df_combined[df_combined['clasificacion_campo_amplio'].isna()]
# Vectorización de la columna 'carrera' utilizando TF-IDF
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(df_train['carrera'])
X_predict = vectorizer.transform(df_predict['carrera'])
# Definir el target
y_train = df_train['clasificacion_campo_amplio']
# División de los datos de entrenamiento para validación
X_train_split, X_val_split, y_train_split, y_val_split = train_test_split(X_train, y_train, test_size=0.2, random_state=42)
# Entrenar el modelo
model = LogisticRegression(max_iter=1000)
model.fit(X_train_split, y_train_split)
# Validar el modelo
y_val_pred = model.predict(X_val_split)
print("Reporte de clasificación para datos de validación:")
print(classification_report(y_val_split, y_val_pred, zero_division=0))
# Predecir los valores faltantes en df_merged_IS y añadir el asterisco
def predecir_y_marcar(row, model, vectorizer):
if pd.isna(row['clasificacion_campo_amplio']):
prediccion = model.predict(vectorizer.transform([row['carrera']]))[0]
return f"{prediccion}*"
else:
return row['clasificacion_campo_amplio']
carreras_its['clasificacion_campo_amplio'] = carreras_its.apply(
lambda row: predecir_y_marcar(row, model, vectorizer),
axis=1
)
# Exportar el dataframe resultante a Excel
carreras_its.to_excel('carreras_its_actualizado.xlsx', index=False)
# Conteo de valores vacíos por columna después de la predicción
valores_vacios_post_prediccion = carreras_its.isna().sum()
print("Total de valores vacíos por columna después de la predicción:")
print(valores_vacios_post_prediccion)
tipo gestion¶
In [ ]:
import pandas as pd
# Contar el tipo de gestión más frecuente para cada institución
def completar_tipo_gestion(grupo):
if grupo['tipo_gestion'].isnull().all():
return grupo
# Obtener el tipo de gestión más frecuente (eliminando los nulos)
tipo_frecuente = grupo['tipo_gestion'].dropna().mode()[0]
# Rellenar los valores nulos con el tipo de gestión más frecuente
grupo['tipo_gestion'] = grupo['tipo_gestion'].fillna(tipo_frecuente)
return grupo
# Aplicar la función a cada grupo de instituciones sin convertirlo en índice
carreras_its = carreras_its.groupby('institucion', as_index=False).apply(completar_tipo_gestion)
# Guardar el resultado en un archivo Excel
carreras_its.to_excel('carreras_its_actualizado.xlsx', index=False)
# Verificar si aún hay valores nulos en la columna 'tipo_gestion'
print(carreras_is['tipo_gestion'].isnull().sum())
VERIFICACION DE INCONSISTENCIAS¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_its_copy = titulos_its.copy()
carreras_its_copy = carreras_its.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_its_copy = titulos_its_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_its_copy = carreras_its_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_its_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_its_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_ITS.xlsx', index=False)
carreras_its_copy = carreras_its_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_its_copy = carreras_its_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
LIMPIEZA DE INCONSISTENCIAS¶
In [ ]:
from fuzzywuzzy import fuzz
import pandas as pd
# Función para extraer la parte posterior a 'AREA'
def extract_after_area(text):
if 'AREA' in text:
return text.split('AREA', 1)[-1].strip() # Extraer lo que sigue después de 'AREA'
return None # Si no tiene 'AREA', devolver None
# Función para verificar palabras clave
def check_keywords(text1, text2):
keywords = ['CIENCIAS BASICAS', 'CIENCIAS SOCIALES', 'FISIOTERAPIA', 'KINESIOLOGIA', 'PSICOLOGIA', 'EDUCACION FISICA']
for keyword in keywords:
if keyword in text1 and keyword not in text2:
return True
if keyword in text2 and keyword not in text1:
return True
return False
# Función de coincidencia difusa optimizada
def fuzzy_unify(row, carreras_its, threshold=75):
best_match = None
best_score = 0
carreras_its_filtered = carreras_its[
(carreras_its['institucion_carrera'] == row['institucion']) &
(carreras_its['tipo_institucion'] == row['tipo_institucion'])
]
row['carrera_carreras'] = None
row['titulo_carrera'] = None
row['institucion_carrera'] = None
for _, carrera_row in carreras_its_filtered.iterrows():
# Extraer partes después de 'AREA'
area_row_1 = extract_after_area(row['carrera'])
area_row_2 = extract_after_area(carrera_row['carrera_carreras'])
if area_row_1 and area_row_2: # Ambas tienen 'AREA'
area_score = fuzz.ratio(area_row_1, area_row_2)
if area_score < 70: # Si la parte después de 'AREA' no es similar
avg_score = 60 # Asignar un puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
elif area_row_1 or area_row_2: # Solo una de las partes tiene 'AREA'
avg_score = 60 # Puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
# Verificar palabras clave
if check_keywords(row['carrera'], carrera_row['carrera_carreras']):
avg_score *= 0.5 # Reducir el puntaje a la mitad si hay un desajuste en las palabras clave
if avg_score > best_score:
best_score = avg_score
best_match = carrera_row
if best_score >= threshold and best_match is not None:
row['carrera_unificada'] = best_match['carrera_carreras']
row['titulo_unificado'] = best_match['titulo_carrera']
row['institucion_unificada'] = best_match['institucion_carrera']
row['carrera_carreras'] = best_match['carrera_carreras']
row['titulo_carrera'] = best_match['titulo_carrera']
row['institucion_carrera'] = best_match['institucion_carrera']
else:
row['carrera_unificada'] = row['carrera']
row['titulo_unificada'] = row['titulo_titulos']
row['institucion_unificada'] = row['institucion']
row['similarity_score'] = best_score
return row
# Aplicar la coincidencia difusa
left_only_unified = left_only.apply(fuzzy_unify, args=(carreras_its_copy,), axis=1)
# Seleccionar las columnas en el orden deseado
left_only_unified = left_only_unified[[
'carrera',
'carrera_carreras',
'carrera_unificada',
'titulo_titulos',
'titulo_carrera',
'titulo_unificado',
'institucion',
'institucion_carrera',
'institucion_unificada',
'similarity_score'
]]
# Exportar a un archivo Excel con el nombre modificado
left_only_unified.to_excel('unificacion_left_only_its.xlsx', index=False)
print("Archivo exportado: 'unificacion_left_only_its.xlsx'")
reemplazar campos¶
In [ ]:
# Paso 1: Filtrar las filas de left_only_unified con un puntaje mayor o igual a 90
left_only_unified_filtered = left_only_unified[left_only_unified['similarity_score'] >= 100]
# Paso 2: Reemplazar los valores en titulos_cre
for _, row in left_only_unified_filtered.iterrows():
# Buscar coincidencias en titulos_cre
condition = (
(titulos_its['carrera'] == row['carrera']) &
(titulos_its['titulo'] == row['titulo_titulos']) &
(titulos_its['institucion'] == row['institucion'])
)
# Reemplazar los valores en titulos_cre si hay coincidencias
titulos_its.loc[condition, 'carrera'] = row['carrera_unificada']
titulos_its.loc[condition, 'titulo'] = row['titulo_unificado']
# Puedes guardar el DataFrame actualizado en un archivo Excel o CSV si lo deseas
titulos_its.to_excel('titulos_its_actualizado.xlsx', index=False)
print("Archivo exportado: 'titulos_its_actualizado.xlsx'")
Segunda verificacion¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_its_copy = titulos_its.copy()
carreras_its_copy = carreras_its.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_its_copy = titulos_its_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_its_copy = carreras_its_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_its_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_its_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_ITS.xlsx', index=False)
carreras_its_copy = carreras_its_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_its_copy = carreras_its_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
Union final ITS¶
In [ ]:
import pandas as pd
# Paso 1: Eliminar duplicados en las columnas clave de carreras
df_merged_unique = carreras_its.drop_duplicates(
subset=['carrera', 'institucion', 'tipo_institucion', 'titulo']
)
# Paso 2: Realizar el merge con las combinaciones únicas
merged_final_ITS = pd.merge(
titulos_its,
df_merged_unique,
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
how='left' # Aquí cambiamos a 'left' para mantener todas las filas de df_titulos_v2_limpio_CRE
)
# Paso 2: Guardar el DataFrame en un archivo Excel
merged_final_ITS.to_excel('ITS_FINAL.xlsx', index=False)
# Imprimir la forma y los encabezados de df_titulos_v2_limpio_CRE
print("Shape del DataFrame TITULOS V2:", titulos_its.shape)
# Imprimir la forma y los encabezados del DataFrame final
print("Shape del DataFrame final:", merged_final_ITS.shape)
UNIVERSIDAD¶
In [ ]:
print("Correcciones realizadas en titulos_u:")
print(titulos_u['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
print("\nCorrecciones realizadas en carreras_u:")
print(carreras_u['institucion'].unique()) # Imprime los valores únicos para ver las correcciones
INSTITUTOS QUE ESTAN EN UN SOLO LUGAR¶
In [ ]:
# Valores únicos en 'titulos_cre'
instituciones_titulos = set(titulos_u['institucion'].unique())
# Valores únicos en 'carreras_cre'
instituciones_carreras = set(carreras_u['institucion'].unique())
# Instituciones que están en 'titulos_cre' pero no en 'carreras_cre'
solo_en_titulos = instituciones_titulos - instituciones_carreras
print("Instituciones que están solo en titulos_u:")
print(solo_en_titulos)
# Instituciones que están en 'carreras_cre' pero no en 'titulos_cre'
solo_en_carreras = instituciones_carreras - instituciones_titulos
print("\nInstituciones que están solo en carreras_u:")
print(solo_en_carreras)
campo amplio¶
In [ ]:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from tqdm import tqdm
# Concatenar los dataframes
df_combined = pd.concat([carreras_is, carreras_ifd, carreras_cre, carreras_its, carreras_u], ignore_index=True)
# Filtrar filas donde clasificacion_campo_amplio no esté vacío para entrenamiento
df_train = df_combined.dropna(subset=['clasificacion_campo_amplio'])
# Filtrar filas donde clasificacion_campo_amplio esté vacío para predicción
df_predict = df_combined[df_combined['clasificacion_campo_amplio'].isna()]
# Vectorización de la columna 'carrera' utilizando TF-IDF
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(df_train['carrera'])
X_predict = vectorizer.transform(df_predict['carrera'])
# Definir el target
y_train = df_train['clasificacion_campo_amplio']
# División de los datos de entrenamiento para validación
X_train_split, X_val_split, y_train_split, y_val_split = train_test_split(X_train, y_train, test_size=0.2, random_state=42)
# Entrenar el modelo
model = LogisticRegression(max_iter=1000)
model.fit(X_train_split, y_train_split)
# Validar el modelo
y_val_pred = model.predict(X_val_split)
print("Reporte de clasificación para datos de validación:")
print(classification_report(y_val_split, y_val_pred, zero_division=0))
# Predecir los valores faltantes en df_merged_IS y añadir el asterisco
def predecir_y_marcar(row, model, vectorizer):
if pd.isna(row['clasificacion_campo_amplio']):
prediccion = model.predict(vectorizer.transform([row['carrera']]))[0]
return f"{prediccion}*"
else:
return row['clasificacion_campo_amplio']
carreras_u['clasificacion_campo_amplio'] = carreras_u.apply(
lambda row: predecir_y_marcar(row, model, vectorizer),
axis=1
)
# Exportar el dataframe resultante a Excel
carreras_u.to_excel('carreras_u_actualizado.xlsx', index=False)
# Conteo de valores vacíos por columna después de la predicción
valores_vacios_post_prediccion = carreras_u.isna().sum()
print("Total de valores vacíos por columna después de la predicción:")
print(valores_vacios_post_prediccion)
verificar inconsistencias¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_u_copy = titulos_u.copy()
carreras_u_copy = carreras_u.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_u_copy = titulos_u_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_u_copy = carreras_u_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_u_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_u_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_U.xlsx', index=False)
carreras_u_copy = carreras_u_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_u_copy = carreras_u_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
Limpieza de inconsistencias¶
In [ ]:
import pandas as pd
from fuzzywuzzy import fuzz
from tqdm import tqdm # Importar tqdm
# Lista de palabras clave que quieres analizar
keywords = [
'CIENCIAS AMBIENTALES', 'CIENCIAS DE LA COMUNICACION','CIENCIAS DE LA EDUCACION',
'CIENCIAS INFORMATICAS','CIENCIAS ODONTOLOGICAS','CIENCIAS CONTABLES','DIDACTICA SUPERIOR','DIDACTICA UNIVERSITARIA','DOCENCIA UNIVERSITARIA',
'EDUCACION','MATEMATICA','MEDICINA','METODOLOGIA','PSICOLOGIA','ODONTOLOGIA','RELACIONES PUBLICAS',
'ADMINISTRACION', 'INGENIERIA', 'GESTION', 'CIENCIAS', 'TECNOLOGIA',
'DIDACTICA', 'ENFASIS', 'MENCION','CIRUGIA','GERENCIA'
]
# Función para extraer la parte anterior y posterior a cualquier palabra clave
def extract_around_keywords(text, keywords):
text_upper = text.upper() # Convertir a mayúsculas para comparación
for keyword in keywords:
if keyword in text_upper:
# Dividir el texto en la parte anterior y posterior a la palabra clave
before, after = text_upper.split(keyword, 1)
return before.strip(), after.strip() # Devolver ambas partes
return None, None # Si no se encuentra ninguna palabra clave, devolver None para ambas partes
# Función para verificar palabras clave
def check_keywords(text1, text2):
keywords2 = ['CIENCIAS SOCIALES', 'CIENCIAS NATURALES', 'CIENCIAS BASICAS', 'CIENCIAS DE LA EDUCACION','DIDACTICA UNIVERSITARIA',
'MARKETING Y PUBLICIDAD','FARMACIA','QUIMICA','ODONTOLOGIA','NEUMOLOGIA','GINECOLOGIA',
'SOCIAL', 'AGROPECUARIA', 'AGROPECUARIAS','ENFASIS', 'PROFUNDIZADO', 'RECONSTRUCTIVA',
'FISIOTERAPIA', 'KINESIOLOGIA', 'PSICOLOGIA', 'EDUCACION FISICA','DERECHO','METODOLOGIA']
for keyword in keywords2:
if keyword in text1 and keyword not in text2:
return True
if keyword in text2 and keyword not in text1:
return True
return False
# Función de coincidencia difusa optimizada para múltiples palabras clave 75
def fuzzy_unify(row, carreras_u, keywords, threshold=85):
best_match = None
best_score = 0
# Filtrar las filas de carreras_u por institución y tipo de institución
carreras_u_filtered = carreras_u[
(carreras_u['institucion_carrera'] == row['institucion']) &
(carreras_u['tipo_institucion'] == row['tipo_institucion'])
]
# Inicializar las columnas para las coincidencias
row['carrera_carreras'] = None
row['titulo_carrera'] = None
row['institucion_carrera'] = None
for _, carrera_row in carreras_u_filtered.iterrows():
# Extraer partes antes y después de palabras clave
before_row_1, after_row_1 = extract_around_keywords(row['carrera'], keywords)
before_row_2, after_row_2 = extract_around_keywords(carrera_row['carrera_carreras'], keywords)
if before_row_1 is not None and after_row_1 is not None and before_row_2 is not None and after_row_2 is not None: # Ambas tienen alguna palabra clave
# Comparar tanto la parte antes como después de la palabra clave
before_score = fuzz.ratio(before_row_1, before_row_2)
after_score = fuzz.ratio(after_row_1, after_row_2)
keyword_score = (before_score + after_score) / 2 # Promedio de ambas partes
if keyword_score < 70: # Si la parte antes o después de la palabra clave no es similar
avg_score = 50 # Asignar un puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
elif before_row_1 or before_row_2 or after_row_1 or after_row_2: # Solo una de las partes tiene la palabra clave
avg_score = 50 # Puntaje bajo
else:
carrera_score = fuzz.partial_ratio(row['carrera'], carrera_row['carrera_carreras'])
titulo_score = fuzz.partial_ratio(row['titulo_titulos'], carrera_row['titulo_carrera'])
avg_score = (carrera_score + titulo_score) / 2
# Verificar palabras clave
if check_keywords(row['carrera'], carrera_row['carrera_carreras']):
avg_score *= 0.5 # Reducir el puntaje a la mitad si hay un desajuste en las palabras clave
# Guardar la mejor coincidencia
if avg_score > best_score:
best_score = avg_score
best_match = carrera_row
# Si el mejor puntaje es suficiente, asignar la coincidencia
if best_score >= threshold and best_match is not None:
row['carrera_unificada'] = best_match['carrera_carreras']
row['titulo_unificado'] = best_match['titulo_carrera']
row['institucion_unificada'] = best_match['institucion_carrera']
row['carrera_carreras'] = best_match['carrera_carreras']
row['titulo_carrera'] = best_match['titulo_carrera']
row['institucion_carrera'] = best_match['institucion_carrera']
else:
# Si no hay buena coincidencia, mantener los valores originales
row['carrera_unificada'] = row['carrera']
row['titulo_unificada'] = row['titulo_titulos']
row['institucion_unificada'] = row['institucion']
# Asignar el puntaje de similitud
row['similarity_score'] = best_score
return row
# Aplicar la coincidencia difusa con las palabras clave en el DataFrame
tqdm.pandas(desc="Procesando filas") # Inicializar tqdm
left_only_unified = left_only.progress_apply(fuzzy_unify, args=(carreras_u_copy, keywords), axis=1)
# Seleccionar las columnas en el orden deseado para exportar a Excel
left_only_unified = left_only_unified[[
'carrera',
'carrera_carreras',
'carrera_unificada',
'titulo_titulos',
'titulo_carrera',
'titulo_unificado',
'institucion',
'institucion_carrera',
'institucion_unificada',
'similarity_score'
]]
# Exportar a un archivo Excel
left_only_unified.to_excel('unificacion_left_only_u.xlsx', index=False)
print("Archivo exportado: 'unificacion_left_only_u.xlsx'")
reemplazando campos¶
In [ ]:
# Paso 1: Filtrar las filas de left_only_unified con un puntaje mayor o igual a 90
left_only_unified_filtered = left_only_unified[left_only_unified['similarity_score'] >= 90]
# Paso 2: Reemplazar los valores en titulos_cre
for _, row in left_only_unified_filtered.iterrows():
# Buscar coincidencias en titulos_cre
condition = (
(titulos_u['carrera'] == row['carrera']) &
(titulos_u['titulo'] == row['titulo_titulos']) &
(titulos_u['institucion'] == row['institucion'])
)
# Reemplazar los valores en titulos_cre si hay coincidencias
titulos_u.loc[condition, 'carrera'] = row['carrera_unificada']
titulos_u.loc[condition, 'titulo'] = row['titulo_unificado']
# Puedes guardar el DataFrame actualizado en un archivo Excel o CSV si lo deseas
titulos_u.to_excel('titulos_u_actualizado.xlsx', index=False)
print("Archivo exportado: 'titulos_u_actualizado.xlsx'")
segunda verificacion¶
In [ ]:
import pandas as pd
# Crear copias de los DataFrames originales
titulos_u_copy = titulos_u.copy()
carreras_u_copy = carreras_u.copy()
# Renombrar la columna 'titulo' en cada copia para diferenciarlas
titulos_u_copy = titulos_u_copy.rename(columns={'titulo': 'titulo_titulos'})
carreras_u_copy = carreras_u_copy.rename(columns={'titulo': 'titulo_carrera'})
# Paso 1: Crear un DataFrame combinado que indica de dónde provienen las filas
merged_df = pd.merge(
titulos_u_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos']],
carreras_u_copy[['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera']],
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_titulos'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo_carrera'],
how='outer',
indicator=True
)
# Paso 2: Filtrar las filas que están solo en uno de los DataFrames
not_matching = merged_df[merged_df['_merge'] != 'both']
# Paso 3: Exportar a Excel
not_matching.to_excel('valores_no_coincidentes_U.xlsx', index=False)
carreras_u_copy = carreras_u_copy.rename(columns={'institucion': 'institucion_carrera'})
carreras_u_copy = carreras_u_copy.rename(columns={'carrera': 'carrera_carreras'})
# Filtrar el DataFrame para obtener solo las filas donde _merge es 'left_only'
left_only = merged_df[merged_df['_merge'] == 'left_only']
# Imprimir el número de filas left_only
print("Número de filas left_only:", left_only.shape[0])
Union final U¶
In [ ]:
import pandas as pd
# Paso 1: Eliminar duplicados en las columnas clave de carreras
df_merged_unique = carreras_u.drop_duplicates(
subset=['carrera', 'institucion', 'tipo_institucion', 'titulo']
)
# Paso 2: Realizar el merge con las combinaciones únicas
merged_final_U = pd.merge(
titulos_u,
df_merged_unique,
left_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
right_on=['carrera', 'institucion', 'tipo_institucion', 'titulo'],
how='left' # Aquí cambiamos a 'left' para mantener todas las filas de df_titulos_v2_limpio_CRE
)
# Paso 2: Guardar el DataFrame en un archivo Excel
merged_final_U.to_excel('U_FINAL.xlsx', index=False)
# Imprimir la forma y los encabezados de df_titulos_v2_limpio_CRE
print("Shape del DataFrame TITULOS V2:", titulos_u.shape)
# Imprimir la forma y los encabezados del DataFrame final
print("Shape del DataFrame final:", merged_final_U.shape)
Union final dataset¶
In [ ]:
import pandas as pd
from tqdm import tqdm
# merged_final_U, merged_final_CRE, merged_final_IS, merged_final_IFD, merged_final_ITS
# Concatenar los DataFrames
merged_final_combinado = pd.concat([
merged_final_U,
merged_final_CRE,
merged_final_IS,
merged_final_IFD,
merged_final_ITS
], ignore_index=True)
# Usar tqdm para mostrar una barra de progreso al exportar a Excel
with tqdm(total=1, desc="Exportando a Excel") as pbar:
# Exportar a un archivo Excel
merged_final_combinado.to_excel('DATASET_FINAL.xlsx', index=False)
pbar.update(1) # Actualizar la barra de progreso
print("Archivo exportado: 'DATASET_FINAL.xlsx'")