<jupyter_start><jupyter_text>Lectura:<jupyter_code>df1.head()
df2.head(3)
#Lectura de datos
numeric_features = list(df2.dtypes[df2.dtypes != "object"].index)
n_show = len(numeric_features)-1

fig, axes = plt.subplots(figsize=(16,4*int(np.ceil(n_show/4))), nrows=int(np.ceil(n_show/4)), ncols=3)
sns.distplot(df2[numeric_features[0]], hist=True, rug=False, kde=True, ax=axes[1//4,1%3],label=numeric_features[0])
for i in range(2,7):
    sns.distplot(df2[numeric_features[i]], hist=True, rug=False, kde=True, ax=axes[i//4,i%3],label=numeric_features[i])
plt.suptitle("Figura 1",fontsize=18, weight='bold', y = 0.05)
plt.show()
fig, axes = plt.subplots(figsize=(16,3.5*int(np.ceil(n_show/4))), nrows=int(np.ceil(n_show/4)), ncols=3)
numeric_features = ['budget',
 'popularity',
 'revenue',
 'runtime',
 'vote_average',
 'vote_count']

for i, column in enumerate(numeric_features[:n_show]):
    sns.boxplot(x=df2[column],ax=axes[i//3,i%3])
    axes[i//3,i%3].set_xlabel("")
    axes[i//3,i%3].set_title(column)
fig.tight_layout()
plt.suptitle("Figura 2",fontsize=18, weight='bold', y = 0)
plt.show()
categorical_var = ["original_language","status"]
idiomas = df2["original_language"]
data_count = df2.groupby("original_language")["original_language"].count()
print("Hay",len(idiomas.unique()),"idiomas diferentes:\n", idiomas.unique())
print("La mayoria de las peliculas son en ingles y son",data_count["en"])
data_count.plot.bar(legend = True,title = "Frecuencia de idiomas")
plt.show()
data_sin_ingles = data_count[data_count.values != 4505]
data_sin_ingles.plot.bar(legend = True,title = "Frecuencia de idiomas sin inglés")
plt.show()
data_count = df2.groupby("status")["status"].count()
print("Hay",len(data_count),"status diferentes")
print("\n status posibles:",df2["status"].unique())
fig = plt.figure(figsize= (15,5))
plt.subplot(1,2,1)
data_count[["Post Production","Rumored"]].plot.bar(legend = True,title = "Status")
plt.subplot(1,2,2)
data_count[["Released"]].plot.bar(legend = True,title = "Status")
corrmat = df2[numeric_features[:n_show]].corr()
fig, ax = plt.subplots(figsize=(8, 8))
sns.heatmap(corrmat, vmax=.9, square=True, ax=ax,cmap="Blues",annot=True)
ax.set_title("Matriz de correlación")
plt.show()<jupyter_output><empty_output><jupyter_text>Preprocesamiento :  Como podemos notar ambos datasets poseen en común la columna id - movie_id por lo que juntaremos todo debido a que la información del dataset con los créditos complementa la información del otro dataset<jupyter_code>#Primero cambiamos el nombre de las columnas de df1 para que coincida movie_id con id
df1.columns = ["id","title","cast","crew"]
df = df2.merge(df1,on = "id")
df.head(3)<jupyter_output><empty_output><jupyter_text>Hay presencia de valores perdidos por lo que se procesan eliminandose y viendo que tanta información se pierde con esto:<jupyter_code>tamanio_original = len(df)
df_without_nan = df.dropna()
tamanio_nuevo = len(df_without_nan)
print("Al procesar los valores NaN pasamos de ",tamanio_original,"datos, a tener",tamanio_nuevo,"datos")<jupyter_output>Al procesar los valores NaN pasamos de  4803 datos, a tener 1493 datos<jupyter_text>Por esto no es conveniente eliminar directamente, vamos a analizar cada columna y visualizar valores perdidos, esto pues por ejemplo, no todas las peliculas tienen una pagina web por lo que homepage es propensa a tener valores NaN<jupyter_code>columnas = df.columns
valores_perdidos = []
for col in columnas:
    if df[col].isnull().sum()>0:
        valores_perdidos.append([col,df[col].isnull().sum()])
data_perdida = pd.DataFrame(data = valores_perdidos,columns = ["Nombre Columna","Valores Perdidos"])
data_perdida
ax = sns.barplot(x="Nombre Columna", y="Valores Perdidos", data=data_perdida)
plt.show()<jupyter_output><empty_output><jupyter_text>Podemos notar que las columnas homepage y tagline poseen muchos valores perdidos por lo que no consideraremos estas columnas para nuestro análisis<jupyter_code>df = df.drop(["homepage","tagline"],axis=1)
df = df.dropna()
tamanio_nuevo = len(df)
print("Al procesar los valores NaN pasamos de tener",tamanio_original,"datos, a tener",tamanio_nuevo,"datos")<jupyter_output>Al procesar los valores NaN pasamos de tener 4803 datos, a tener 4799 datos<jupyter_text>Al observar la información sobre los datos y su estructura podemos notar que hay columnas de tipo objeto que son listas y que contienen listas vacias sin información por lo que debemos limpiar esto:<jupyter_code>df.info()
def plot_missing_values(df):
    '''
    plot_missing_values(df)

    Grafica los valores perdidos por columna

    Parameters
    ----------
    df : DataFrame
        DataFrame sin editar. 

    Returns
    -------
    output : None
        No retorna nada

    '''
    #Se definen variables auxiliares
    columns = df.columns
    valores_perdidos = []

    #Iteramos sobre las columnas y si encuuentra al menos un "[]" y "" los almacena en la lista de valores perdidos.
    for col in columns:
        if sum(df[col] == "[]")>0:
            valores_perdidos.append([col,sum(df[col] == "[]")])
        if sum(df[col] == ""):
            valores_perdidos.append([col,sum(df[col] == "")])
    data_perdida = pd.DataFrame(data = valores_perdidos,columns = ["Nombre Columna","Valores Perdidos"])

    #Grafico

    figure(figsize=(15,6))
    ax = sns.barplot(x="Nombre Columna", y="Valores Perdidos", data=data_perdida)
    ax.set_title("Cantidad de valores perdidos por columna")<jupyter_output><empty_output><jupyter_text>Mediante una vista directa al csv de los datos notamos que habían elementos que si bien no eran NaN, seguían siendo "vacíos", por ejemplo en la columna keywords:<jupyter_code>df[df["keywords"]=="[]"].head(2)
plot_missing_values(df)
data = df[df["popularity"]>=df["popularity"].mean()]
plot_missing_values(data)<jupyter_output><empty_output><jupyter_text>Podemos ver que la cantidad de valores perdidos (los cuales consideramos como "[]") son pocos relativo a la cantidad de datos que se tienen por lo que eliminaremos los datos que tienen keyword sin datos ya que poseen baja popularidad (bajo la media). Utilizando el mismo método, comprobamos que las películas que tienen valores perdidos (en forma de "[]") en la columna keywords no son populares.<jupyter_code>plot_missing_values(df)<jupyter_output><empty_output><jupyter_text>En nuestro modelo vamos a utilizar todas las columnas mostradas menos "spoken_languages", por lo cual es necesario eliminar las filas que contengan datos vacios en forma de "[]" de las columnas a utilizar.<jupyter_code>data_copy = df
data_copy = data_copy.reset_index()
def drop_row(df,columna):

    '''
    drop_row(df)

    Elimina las filas con valores "NaN" y "[]"

    Parameters
    ----------
    df : DataFrame
        DataFrame al que se le eliminará la fila.

    columna: panda.Series
         Columna que contiene valores "NaN" y  "[]"

    Returns
    -------
    output : df
        Retorna el DataFrame editado

    '''
    #Definición de variables

    col = df[columna]
    L = []

    #Iteramos sobre las columnas y almacenamos en la lista L los valores "[]" para luego ser reemplazados por "NaN"
    for element in col:
        if element == "[]":
            L.append(float("NaN"))
        else:
            L.append(element)
    df[columna] = L

    #Se dropean las filas que contengan los valores "NaN"
    df = df.dropna()
    return df

data_copy = drop_row(df,"keywords")
data_copy
plot_missing_values(data_copy)
data_copy = drop_row(data_copy,"genres")
plot_missing_values(data_copy)
data_copy = drop_row(data_copy,"production_companies")
plot_missing_values(data_copy)
data_copy = drop_row(data_copy,"cast")
plot_missing_values(data_copy)
data_copy = drop_row(data_copy,"crew")
plot_missing_values(data_copy)
data_copy = drop_row(data_copy,"production_countries")<jupyter_output><empty_output><jupyter_text>Luego de limpiar el dataset se puede observar que solo quedan valores vacíos e la columna "spoken_languages":<jupyter_code>plot_missing_values(data_copy)
data_copy["title"] = data_copy["title_x"]
data_copy = data_copy.drop(["original_title","title_x","title_y"],axis = 1)
data_copy<jupyter_output><empty_output><jupyter_text>Modelo: Luego de tener ya preprocesado el dataset, utilizaremos el modelo propuesto en [Getting Started with a Movie Recommendation System](https://www.kaggle.com/ibtesama/getting-started-with-a-movie-recommendation-system/data?select=tmdb_5000_credits.csv), en particular "Content Based Filtering", para proponer un sistema de recomendación de películas. Usaremos similaridad coseno para determinar una cantidad numérica que represente la similaridad entre dos peliculas. $$cos(\theta) = \frac{\textbf{A}\cdot\textbf{B}}{\Vert \textbf{A}\Vert \cdot \Vert \textbf{B}\Vert}$$ Se realizan funciones para determinar el director y en este caso se seleccionaron las 3 principales de cada tipo de los siguientes atributos: * Géneros (genre) * Actores (cast) * Compañias de producción (Production Companies) * Países de producción (Production Countries) * Palabras clave (keywords)<jupyter_code>def get_director(x):
    """
    
    get_director(x)

    Toma una series con diccionarios, y comprueba si existe el trabajo director, si es así retorna el director.

    Parameters
    ----------
    x : Pd.Series
        Serie con los elementos para obtener el director

    Returns
    --------
    output : String
             Retorna un string con el nombre del director si el trabajo existe.
             Si el no existe el trabajo director, retorna nan.

    
    """
    for i in x:
        if i['job'] == 'Director':
            return i['name']
    return np.nan
# Retorna una lista con los 3 primeros elemntos o la lista entera, la que tenga más elementos.
def get_list(x):
    """

    get_list(x)

    Retorna una lista con los 3 primeros elemntos o la lista entera, la que tenga más elementos.

    Parameters
    -----------
    x : List
        Lista

    Returns
    --------
    output : List
             Retorna una lista con los primeros 3 elementos de la lista, o la lista entera.
    
    """
    if isinstance(x, list):
        names = [i['name'] for i in x]
        #Si la lista tiene más de tres elementos, entonces se retornan los primeros tres nombres, si no, se retorna la lista entera.
        if len(names) > 3:
            names = names[:3]
        return names

    #Retorna una lista vacia en caso de que hayan datos perdidos o corruptos.
    return []
data_copy["cast"] = data_copy["cast"].apply(literal_eval)
data_copy["keywords"] = data_copy["keywords"].apply(literal_eval)
data_copy["genres"] = data_copy["genres"].apply(literal_eval)
data_copy["production_companies"] = data_copy["production_companies"].apply(literal_eval)
data_copy["production_countries"] = data_copy["production_countries"].apply(literal_eval)
data_copy["crew"] = data_copy["crew"].apply(literal_eval)

data_copy
data_copy['director'] = data_copy['crew'].apply(get_director)
features = ["production_countries","production_companies",'cast', 'keywords', 'genres']
for feature in features:
    data_copy[feature] = data_copy[feature].apply(get_list)
data_copy[['title' ,"production_countries","production_companies",'cast', 'director', 'keywords', 'genres']].head(3)<jupyter_output><empty_output><jupyter_text>Luego, se convierten todos los strings a minúscula y se quitan los espacios de los nombres en todas las columnas de interés:<jupyter_code>def clean_data(x):
    """
    clean_data(x)

    convierte todos los string a minúscula y quita los espacios de los nombres

    Parameters
    -----------
    x : List
        Lista que se quiere limpiar
    
    Returns
    --------
    output : Retorna una lista con strings en minúsculas sín espacios si el elemento es una lista.
             Si el elemento no es una lista, pero es un string, se retorna el string en minúscula y sín espacios.
             Si no es ni una lista ni un string, se retorna un string vacio. 

    """
    if isinstance(x, list):
        return [str.lower(i.replace(" ", "")) for i in x]
    else:
        #Revisa si el director existe. Si no, retorna un string vacio
        if isinstance(x, str):
            return str.lower(x.replace(" ", ""))
        else:
            return ''
# Se aplica la función clean_data a tus cualidades.
features = ["production_countries","production_companies",'cast', 'keywords', 'director', 'genres']

for feature in features:
    data_copy[feature] = data_copy[feature].apply(clean_data)
data_copy.head(3)
# Se crea un vector en formato String con las características de la pélicula
def create_soup(x):
    """
    create_soup(x)

    Se crea  un vector en formato String con las características de la pélicula

    Parameters
    -----------
    x : Dataframe
        Dataframe principal con las películas a analizar 
    
    Returns
    ----------
    output: string
            string con las características de la película
    """
    return ' '.join(x['keywords']) + ' ' + ' '.join(x['cast']) + ' ' + x['director'] + ' ' + ' '.join(x['genres'])+' '+' '.join(x["production_companies"])+' '+' '.join(x["production_countries"])
data_copy['soup'] = data_copy.apply(create_soup, axis=1)
data_copy["soup"][0]
# Se importa CountVectorizer y se crea la matriz de conteo
from sklearn.feature_extraction.text import CountVectorizer

count = CountVectorizer(stop_words='english')
count_matrix = count.fit_transform(data_copy['soup'])
# Se calcula la matriz de similaridad por coseno basada en la matriz de conteo
from sklearn.metrics.pairwise import cosine_similarity

cosine_sim2 = cosine_similarity(count_matrix, count_matrix)<jupyter_output><empty_output><jupyter_text>Resultados (gráficos)<jupyter_code># Se restablece el indice del DataFrame principal y se construye un mapeo inverso como se realizo anteriormente
data_copy = data_copy.reset_index()
indices = pd.Series(data_copy.index, index=data_copy['title'])
def get_recommendations(title, cosine_sim=cosine_sim2):
    """
    get_recommendations(tittle,cosine_sim=cosine_sim)

    Función que toma el título de películas como input y entrega como resultado las 10 películas más similares 

    Parameters
    -----------
    title : String
            Título de la película
    
    Returns
    ---------
    output : Object
             Serie con las 10 películas más similares
    """
    # Obtiene el índice de la película que calza con el título
    idx = indices[title]

    # Obtiene la puntuación de similaridad de a pares con todas las películas del dataset
    sim_scores = list(enumerate(cosine_sim[idx]))

    # Ordena las películas respecto a la puntuación de similaridad
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

    # Obtiene la puntuación de las 10 películas con mejor puntuación
    sim_scores = sim_scores[1:11]

    # obtiene el índice de las películas
    movie_indices = [i[0] for i in sim_scores]

    # retorna las 10 películas más similares
    return data_copy['title'].iloc[movie_indices]
# Función que toma el título de películas como input y entrega como resultado las 10 películas más similares 
def get_recommendations_graphics(title, cosine_sim=cosine_sim2):
    # Obtiene el índice de la película que calza con el título
    idx = indices[title]

    # Obtiene la puntuación de similaridad de a pares con todas las películas del dataset
    sim_scores = list(enumerate(cosine_sim[idx]))

    # Ordena las películas respecto a la puntuación de similaridad
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

    # Obtiene la puntuación de las 10 películas con mejor puntuación
    sim_scores = sim_scores[1:11]

    # obtiene el índice de las películas
    movie_indices = [i[0] for i in sim_scores]

    # Las 10 películas más similares
    movies = data_copy['title'].iloc[movie_indices]
    sim_scores = [i[1] for i in sim_scores]

    fig = plt.figure(figsize = (10,5))
    plt.bar(movies,sim_scores)
    plt.xticks(rotation = 90)
    return
get_recommendations('The Avengers', cosine_sim2)
get_recommendations_graphics('The Avengers', cosine_sim2)
get_recommendations('The Godfather', cosine_sim2)
get_recommendations_graphics('The Godfather', cosine_sim2)
get_recommendations('Minions', cosine_sim2)
get_recommendations_graphics('Minions', cosine_sim2)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11390233441578335, 'fraction_numerical': 0.009251884075730655, 'mean_word_length': 4.369078947368421, 'pattern_counts': {'":': 2, '<': 51, '<?xml version=': 0, '>': 55, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '531444', 'license': 'MIT', 'n_tokens_mistral': 5649, 'n_tokens_neox': 5507, 'n_words': 1507, 'path': 'project/Proyecto_final.ipynb', 'repo_name': 'Cristobal314/repositorio_MAT281_Cristobal_Alvarez', 'size': '16321'}