Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Enriquecimiento de los datos

31/38
Recursos

Podemos enriquecer nuestra tabla con información adicional, un poco de información numérica para realizar análisis posterior.

Usaremos nltk es una librería dentro del stack de Ciencia de Datos de Python que nos va a permitir tokenizar, separar palabras dentro del título y nos permitirá contar la frecuencia de cuántas palabras existen en nuestro título y body

Aportes 80

Preguntas 8

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Sería muy útil que antes de empezar a escribir código, sin prácticamente ningún contexto, se explicará gráficamente que es lo que vamos a hacer. Así no estaríamos simplemente replicando el código que vemos en pantalla y sabríamos la finalidad de cada cosa…

Sentí que casi copié y pegué, pero aquí está el reto:

Al principio

import nltk
from nltk.corpus import stopwords
stop_words = set(stopwords.words('spanish'))

En la función main

    df['n_tokens_title'] = _tokenize_column(df, 'title')
    df['n_tokens_body'] = _tokenize_column(df, 'body')

Y la implementación de la nueva función

def _tokenize_column(df, column_name):
    return(df 
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis = 1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
          )

Para grabar el DataFrame en csv se usa la siguiente línea de código,

 df.to_csv('eluniversal_limpio.csv', encoding="utf-8", sep = ';')

La deben escribir como la última línea, así:

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename',
                        help='The path to the dirty data',
                        type=str)

    args = parser.parse_args()

    df = main(args.filename)
    print(df)
    df.to_csv('eluniversal_limpio.csv', encoding="utf-8", sep = ';')

y para stop_words en windows. ojo en Jupyter simplemente es ejecutar

nltk.download('punkt')
nltk.download('stopwords')




pip install stop-words```

yo he tenido que poner

conda install -c anaconda nltk 

y luego activarlo desde Anaconda–>Environments

Buenas
Tengo este error en el jupyter notebook, si alguien me puede colaborar porfa =).

<<ipython-input-24-4763809eb575> in <lambda>(tokens)
     14              .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
     15              .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
---> 16              .apply(lambda tokens: map(lambda token: token.lower(), tokens))
     17              .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
     18              .apply(lambda valid_word_list: len(valid_word_list))

TypeError: 'float' object is not iterable>

para que me funcionara desde el ambiente viritual tuve que agregar en el import el siguiente codigo

import nltk
from nltk.corpus import stopwords
nltk.download('stopwords')
nltk.download('punkt')

ValueError: cannot reindex from a duplicate axis

Alguno sabe como solucionar este error? me sale luego correr el código en jupyter

stop_words = set(stopwords.words('spanish'))

def tokenize_column(df, column_name):
    return (df
               .dropna()
               .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
               .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
               .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
               .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
               .apply(lambda valid_word_list: len(valid_word_list))
            ) 
el_universal['n_tokens_title'] = tokenize_column(el_universal, 'title')
el_universal```

Buen día al escribir el código que se explica en este video, me arroja el siguiente error:

Alguien le ha sucedido lo mismo, podría compartirme como lo solucionó

Traceback (most recent call last):
  File "newspaper_receipe.py", line 123, in <module>
    df = main(arg.filename)
  File "newspaper_receipe.py", line 24, in main
    df = _tokenize_column(df, 'title')
  File "newspaper_receipe.py", line 101, in _tokenize_column
    .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/pandas/core/frame.py", line 6487, in apply
    return op.get_result()
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/pandas/core/apply.py", line 151, in get_result
    return self.apply_standard()
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/pandas/core/apply.py", line 257, in apply_standard
    self.apply_series_generator()
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/pandas/core/apply.py", line 286, in apply_series_generator
    results[i] = self.f(v)
  File "newspaper_receipe.py", line 101, in <lambda>
    .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/nltk/tokenize/__init__.py", line 143, in word_tokenize
    sentences = [text] if preserve_line else sent_tokenize(text, language)
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/nltk/tokenize/__init__.py", line 104, in sent_tokenize
    tokenizer = load('tokenizers/punkt/{0}.pickle'.format(language))
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/nltk/data.py", line 868, in load
    opened_resource = _open(resource_url)
  File "/home/fresant/anaconda3/lib/python3.7/site-packages/nltk/data.py", line 993, in _open
    return find(path_, path + ['']).open()


Saludos

Aquí la tarea que indico el profesor:

Se instala el nlkt en windows es :

pip install nltk

Recuerden que deben realizarlo dentro del entorno virtual en este caso (Anaconda)

Luego en archivo:

Primero importamos lo que necesitamos para que funcione este módulo:

import nltk
from nltk.corpus import stopwords

Luego en el def main lo siguiente:

def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df = _tokenize_column(df, 'title')
    df = _tokenize_column(df, 'body')

    return df

Y la función que pasamos y modifique un poco que hicimos en el Jupyter Notebook:

def _tokenize_column(df, column_name):
    logger.info('Counting words from him: {}'.format(column_name))
    token_count =  (df
                        .dropna()
                        .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                        .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
                        .apply(lambda tokens: list(map(lambda token: token.lower(),tokens)))
                        .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                        .apply(lambda valid_word_list: len(valid_word_list))
                    )
    colum = 'n_tokens_{}'.format(column_name)
    df[colum] = token_count
    return df

Y listo espero que a todos les aya funcionado a la primera. Exitos!

Reto:

import argparse
import hashlib
import logging
import nltk
logging.basicConfig(level=logging.INFO)
from urllib.parse import urlparse
from nltk.corpus import stopwords

import pandas as pd

logger = logging.getLogger(__name__)
stop_words = set(stopwords.words('spanish'))

def main(filename):
    logger.info('Starting cleaning process')
    df= _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df = _tokenize_column_title(df)
    df = _tokenize_column_body(df)
    return df
   

funciones

def _tokenize_column(df, column_name):
    return (df.dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
            )

def _tokenize_column_title(df):
    logger.info('Tokenize the title')
    df['n_tokens_title'] = _tokenize_column(df, 'title')
    
    return df

def _tokenize_column_body(df):
    logger.info('Tokenize the body')
    df['n_tokens_body'] = _tokenize_column(df, 'body')
    
    return df

La función que comparto, tiene como parámetro opcional el stop_words, lo cual permite que si se quiere usar un conjunto diferente al de español, se puede enviar como argumento. Además si la función no se utiliza en el programa, pues no instancia una variable innecesaria de stop_words. Adicionalmente, puede llamarse de dos formas:

1) Una vez por cada campo enviándolo como string:

df = _tokenize_column(df, 'body')
df = _tokenize_column(df, 'title')

2) Una sola vez con una lista de los campos:

df = _tokenize_column(df, ['body', 'title'])

La función:

def _tokenize_column(df, column_name, stop_words=set(stopwords.words('spanish'))):

    column_names = []
    if type(column_name) is not list:
        column_names.append(column_name)
    else:
        column_names = column_name

    logger.info(
        f'Tokenizing column(s) {[column for column in column_names]}')

    for column in column_names:
        df['n_token_'+column] = (df
                                 .dropna()
                                 .apply(lambda row: word_tokenize(row[column]), axis=1)
                                 .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                                 .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                                 .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                                 .apply(lambda valid_word_list: len(valid_word_list)))

    return df

import argparse
import logging
import pandas as pd
import hashlib
import nltk
from nltk.corpus import stopwords
from urllib.parse import urlparse

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def main(filename):
    logger.info('Starting cleaning process')
    
    df =  read_data(filename)
    newspaper_iud = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_iud)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df['n_tokents_title'] = tokenize_column(df, 'title','spanish')
    df['n_tokents_body'] = tokenize_column(df, 'body','spanish')

    print(df)

def read_data(filename):
    logger.info('Reading file {}'.format(filename))
    return pd.read_csv(filename, encoding = "ISO-8859-1")

def _extract_newspaper_uid(filename):
    logger.info('Extarcting newspiper uid')
    newspaper_uid = filename.split('_')[0]

    logger.info('Newspiper uid detected {}'.format(newspaper_uid))
    return newspaper_uid

def _add_newspaper_uid_column(df, newspaper_iud):
    logger.info('Filling newspaper_uid column with {}'.format(newspaper_iud))
    df['newspaper_uid'] = newspaper_iud
    
    return df

def _extract_host(df):
    logger.info('Extractiong host from urls')
    df['host'] = df['url'].apply(lambda url: urlparse(url).netloc)
    
    return df

def _fill_missing_titles(df):
    logger.info("Filling missing title")
    missing_titles_mask = df['title'].isna()

    missing_titles= ( df[missing_titles_mask]['url']
                        .str.extract(r'(?P<missing_titles>[^/]+)$')
                        .applymap(lambda title: title.split('-'))
                        .applymap(lambda title_word_list: ' '.join(title_word_list))
                    )
    df.loc[missing_titles_mask, 'title'] = missing_titles.loc[:,'missing_titles']

    return df

def _generate_uids_for_rows(df):
    logger.info("Generating uids for each row")
    uids = (df
            .apply(lambda row: hashlib.md5(bytes(row['url'].encode())) , axis=1)
            .apply(lambda hash_object: hash_object.hexdigest())
        )
    df['uid'] = uids
    return df.set_index('uid')

def _remove_new_lines_from_body(df):
    logger.info('Removing new lines from body')

    sttripped_body = (df
                      .apply(lambda row: row['body'], axis=1)
                      .apply(lambda body: list(body))
                      .apply(lambda letters: list(map(lambda letter: letter.replace('\n',''),letters)))
                      .apply(lambda letters: list(map(lambda letter: letter.replace('\r',''),letters)))
                      .apply(lambda letters: ''.join(letters))
                 )
    df['body'] = sttripped_body  
    
    return df

def tokenize_column(df, column_name, languaje):
    stop_words = set(stopwords.words(languaje))
    return(df
        .dropna()
        .apply(lambda row: nltk.word_tokenize(row[column_name]),axis=1)
        .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
        .apply(lambda tokens: list(map(lambda token: token.lower(),tokens)))
        .apply(lambda words_list: list(filter(lambda word: word not in stop_words, words_list)))
        .apply(lambda valid_words_list: len(valid_words_list))
    )

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename',
                        help= 'The path to the dirty data.',
                        type= str)
    args = parser.parse_args()
    
    nltk.download('punkt')
    nltk.download('stopwords')

    main(args.filename)```

comparto mi codigo:

llamo a la funcion con una lista de los dos requerimientos que necesito

dataframe = _tokenize_column(dataframe, ['title', 'body'])

aca el codigo de la funcion para que itere por la lista de los dos requerimientos

def _tokenize_column(dataframe, column_name):
    for column in column_name:
        logger.info(f'Tokenizing {column}')
        tokenize_columns = (dataframe
                                .dropna() 
                                .apply(lambda row: nltk.word_tokenize(row[column]), axis=1)
                                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                                .apply(lambda valid_word_list: len(valid_word_list))
                            )
                    
        dataframe[column] = tokenize_columns

    return dataframe```


y aca esta el resultado



INFO:main:Starting cleaning process
INFO:main:Reading file elpais_20_06_02_articles.csv
INFO:main:Extracting newspaper uid
INFO:main:Newspaper uid detected: elpais
INFO:main:Filling newspaper_uid column with elpais
INFO:main:Extracting host from urls
INFO:main:Filling missing titles
INFO:main:Generating uids for each row
INFO:main:Removing newlines from body
INFO:main:Tokenizing title
INFO:main:Tokenizing body



Reto cumplido 😃

def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df = _validate_words(df, 'title')
    df = _validate_words(df, 'body')

    #  -- para verificar que salio bien ---
    #column_name = ['body','title']  
    #for column in column_name:
    #    print(df[['{}'.format(column), 'n_tokens_{}'.format(column)]])


    return df
def _validate_words(df, column_name):
    
    logger.info('Starting to validate words in {}'.format(column_name))
    stop_words = set(stopwords.words('spanish'))

    
    def tokenize_column(df, column_name):
        logger.info('Tokenizing {}'.format(column_name))

        return (df
                   .dropna()
                   .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                   .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                   .apply(lambda tokens: list(map(lambda tokens: tokens.lower(), tokens)))
                   .apply(lambda word_list: list(filter(lambda words: words not in stop_words, word_list)))
                   .apply(lambda valid_word_list: len(valid_word_list))
                )

    df['n_tokens_{}'.format(column_name)] = tokenize_column(df, '{}'.format(column_name))

    return df

El último pedacito de código en la función main() lo que hace es mostar en la terminal las columnas n_tokens añadidas, de esa forma yo verifique que salio bien.

Hola que tal, tuve unos detalles en la función al final, y el código quedó de la siguiente manera:

import argparse # este módulo nos ayuda cuando vamos a crear un script
import logging # con este módulo le vamos imprimiendo en la consola al usuario lo que está pasando
logging.basicConfig(level=logging.INFO)
import hashlib
import nltk
from nltk.corpus import stopwords # los stopwords no nos añaden valor al análisis ulterior (la, los, nos, etc.)
from urllib.parse import urlparse

import pandas as pd


logger = logging.getLogger(__name__) # obtenemos una referencia a nuestro logger con nuestro nombre interno de python


def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename) # vamos a extraer la primera palabra del filename
    df = _add_newspaper_uid_column(df, newspaper_uid) # vamos a añadir la columna newspaper_uid al DataFrame(df)
    df = _extract_host(df) # vamos a añadir a la columan host los host que capturemos de la columna url
    df = _fill_missing_titles(df) # vamos a llenar los titulos vacíos
    df = _generate_uids_for_rows(df) # vamos a convertir los índices en hashes
    df = _remove_new_lines_from_body(df) # vamos a remover los saltos de línea \n
    df = _tokenize_column(df, 'title') # creamos una columna tokenizada de los titles
    df = _tokenize_column(df, 'body') # creamos una columna tokenizada de los titles

    return df


def _read_data(filename):
    logger.info(f'Reading file {filename}')

    return pd.read_csv(filename) # como lo vimos en el jupyter notebooks, leemos el archivo csv


def _extract_newspaper_uid(filename):
    logger.info('Extracting newspaper uid')
    newspaper_uid = filename.split('_')[0] # vamos a tomar la primera parte del filename, si lo dividimos en guiones bajos

    logger.info(f'Newspaper uid detected: {newspaper_uid}')
    return newspaper_uid


def _add_newspaper_uid_column(df, newspaper_uid):
    logger.info(f'Filling newspaper_uid with {newspaper_uid}')
    df['newspaper_uid'] = newspaper_uid # Agregamos los datos de newspaper_uid a la nueva columna que se está agregando al df

    return df


def _extract_host(df):
    logger.info('Extracting host from url')
    df['host'] = df['url'].apply(lambda url: urlparse(url).netloc) # vamos a extraer solo el host de la columna url del df

    return df


def _fill_missing_titles(df):
    logger.info('Filling the empty titles')
    missing_titles_mask = df['title'].isna()

    missing_titles = (df[missing_titles_mask]['url']
        .str.extract(r'(?P<missing_titles>[^/]+)$')
        .applymap(lambda title: title.split('-'))
        .applymap(lambda title_word_list: ' '.join(title_word_list))
    )

    df.loc[missing_titles_mask, 'title'] = missing_titles.loc[:, 'missing_titles']

    return df


def _generate_uids_for_rows(df):
    logger.info('Adding hashes uids to the index')
    uids = (df
        .apply(lambda row: hashlib.md5(bytes(row['url'].encode())), axis=1)
        .apply(lambda hash_object: hash_object.hexdigest())
    )

    df['uid'] = uids

    return df.set_index('uid')


def _remove_new_lines_from_body(df):
    logger.info('Removing new lines from body')
    stripped_body = (df
        .apply(lambda row: row['body'], axis=1) # obtenemos la columna body del DataFrame
        .apply(lambda body: list(body)) # convertimos el body en una lista de letras
        .apply(lambda letters: list(map(lambda letter: letter.replace('\n', ''), letters))) # iteramos entre cada letra y cambiamos /n por ''. Convertimos el objeto map en objeto lista, envolviéndolo en list()
        .apply(lambda letters_list: ''.join(letters_list)) # vamos a unir de nuevo la lista
    )

    df['body'] = stripped_body
    return df


def _tokenize_column(df, column_name):
    logger.info(f'Calculating the number of unique tokens in {column_name}')
    stop_words = set(stopwords.words('spanish')) # seteamos los stopwords a español

    n_tokens = (df
        .dropna() # eliminamos los NaN en caso que aún los haya
        .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1) # tokenizamos nuestra columna
        .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens))) # eliminamos todas las palabras que no sean alfanuméricas
        .apply(lambda tokens_list: list(map(lambda token: token.lower(), tokens_list))) # convertir tokens a lower_case para compararlas correctamente con stopwords
        .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list))) # eliminar palabras dentro de stopwords
        .apply(lambda valid_word_list: len(valid_word_list)) # obtenemos cuántas palabras son
    )

    df['n_tokens_' + column_name] = n_tokens # concatenamos el nombre de la columna con n_tokens para que se aplique a ambas columnas (body y title)

    return df


if __name__ == '__main__':
    parser = argparse.ArgumentParser() # preguntamos al usuario cuál es el archivo (Dataset) que queremos trabajar
    parser.add_argument( # le añadimos un argumento: filename
        'filename',
        help='The path to the dirty data',
        type=str
    )

    args = parser.parse_args() # ahora parseamos los argumentos
    df = main(args.filename) # pasamos el argumento a la función main y lo pasamos a una variable para imprimirla en la consola
    print(df)

Los créditos de la solución son de David Aroesti, no se me había ocurrido concatenar al final el nombre de la columna.

Me sale este error:

C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexes\base.py in _can_reindex(self, indexer)
   3360         # trying to reindex on an axis with duplicates
   3361         if not self.is_unique and len(indexer):
-> 3362             raise ValueError("cannot reindex from a duplicate axis")
   3363 
   3364     def reindex(self, target, method=None, level=None, limit=None, tolerance=None):

ValueError: cannot reindex from a duplicate axis

¿Que hago?

Declaración de las librearías:

import nltk
from nltk.corpus import stopwords
stop_words = set(stopwords.words('spanish')) # Contextualizar el idioma

Se incluye la función principal en el main:

df = _tokenize_words_columns(df)

Declaración de funciones auxiliar y principal:

def _tokenize_words(df, column_name):
    return(df
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
          )


def _tokenize_words_columns(df):   # Contador de palabras significativas en las columnas 'title' y 'body'
    logger.info('Tokenize words in title and body columns')

    df['n_tokens_title'] = _tokenize_words(df, 'title')
    df['n_tokens_body'] = _tokenize_words(df, 'body')

    return df

Reto completado

Tambien se pueden instalar los paquetes en conda o pip directamente desde los jupyter-notebooks:
Para conda:

!conda install nltk -y

Para pip

!pip install nltk -y

PD: El -y, se utiliza para enviar como bandera el si para la instalacion.

# call function
df = _tokenizer_column(df, 'title')

# function
def _tokenizer_column(df, column_name):
    logger.info('Tokenize the {}'.format(column_name))

    stop_words = set(stopwords.words('spanish'))

    tokenizer_column = (df
                        .dropna()
                        .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                        .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                        .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                        .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                        .apply(lambda valid_word_list: len(valid_word_list)) 
                       )

    df['n_tokens_title'] = tokenizer_column

    return df

Esta es la implementación que hice en la receta

import nltk
from nltk.corpus import stopwords

stop_words = set(stopwords.words('spanish'))

def main(filename):
    logger.info('Startig cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspape_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df = _add_tokens_valid(df, 'title')
    df = _add_tokens_valid(df, 'body')

    return df
def _add_tokens_valid(df, column):
    logger.info('Add tokens valid at {}'.format(column))
    df['n_tokens_'+column] = tokenize_column(df, column)
    
    return df

def tokenize_column(df, column_name):
    return (df.dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis= 1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
    )



Comparto mi función _tokenize_column
La ajusté de modo que define automaticamente el nombre de la nueva columna a partir de la columna evaluada:

def _tokenize_column(df, column_name):
    stop_words = set(stopwords.words('spanish'))
    df['n_tokens_'+column_name] = (df
                                    .dropna()
                                    .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                                    .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                                    .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                                    .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                                    .apply(lambda valid_word_list: len(valid_word_list))
                                  )
    return df

Les comparto una pequeña guia de como configurar el entorno y descargar los paquetes de manera efectiva.
El terminal que tengo es cmder y es portable, lo pueden desacargar de https://cmder.net/

Empecemos

1.Luego de instalar anaconda, verificamos la version

conda –version

2.Creamos el entorno en cualquier ruta

conda create --name platzi_data beautifulsoup4 requests numpy pandas matplotlib yaml

#Ruta de intalacion del entorno C:\Users\Username\anaconda3\envs\platzi_data

3.Para chekar que entornos han creado ejecutamos

conda info –envs

5.Nos dirigimos al directorio donde vamos a crear los archivos para trabajar en jupyter y activan su entorno

conda activate platzi_data

6.Verifican si tienen el package nltk en su entorno

conda list

7.Instalan el paquete (recuerden que coge la versión que tienen en anaconda)

conda install nltk

8.Luego para instalar stopword y punkt
(1)Ejecutamos:

py
>>import nltk
>>nltk.dowload()

Imagen 1 y 2
#Y saldra como en el imagen infra e instalamos las librerias que queremos.

(2) O en su defecto ponemos

import nltk
nltk.download('stopwords')
nltk.download('punkt')

8.Finalmente ejecutamos jupyter:

jupiter notebook

#NOTA: esto es para instalar en el entorno, si quieren instalar de forma general para correr newspaper_receipe.py sin entorno.

Entonces salimos del entorno: imagen 3

conda deactive
py
y repetimos los pasos del punto 8.

(1)

(2)

(3)

Comparto mi solución al reto:

En la parte de import agregué esto:

import nltk
from nltk.corpus import stopwords
stop_words = set(stopwords.words('spanish'))

En la función main:

df['n_tokens_title'] = _tokenize_column(df, 'title')
    df['n_tokens_body'] = _tokenize_column(df, 'body')

Y definí la función tokenize añadiendo el logger:

def _tokenize_column(df, column_name):
    logger.info('Tokenizing columns ;)')
    return (df
           .dropna()
           .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
           .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
           .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
           .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
           .apply(lambda valid_word_list: len(valid_word_list))
           )

    return df

nltk para windows, ejecuto esto:

pip install nltk```

Al principio importo las librerías y declaro que busco token en español

import nltk
from nltk.corpus import stopwords

stop_words = set(stopwords.words('spanish'))

En el main declaro las funciones de tokenizar

def main(filename):
    """Función principal de lectura y manipulación de datos"""
    logger.info('Starting cleaning process')
    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)

    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df['n_tokens_title'] = _tokenize_columns(df,'title')
    df['n_tokens_body'] = _tokenize_columns(df,'body')
    return df

Y más abajo la programo

def _tokenize_columns(df, column_name):
    """Tokeniza la columna deseada en el dataframe"""
    logger.info('Tokenizing column_name column in df dataframe')
    return (df
           .dropna()
           .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
           .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
           .apply(lambda tokens: list(map(lambda token: token.lower(),tokens)))
           .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
           .apply(lambda valid_word_list: len(valid_word_list))
           )

Aquí mi código, yo hice solo una función, ‘body’ y ‘title’ los pase como una lista.

import argparse
import hashlib
import nltk
import logging
logging.basicConfig(level=logging.INFO)
from urllib.parse import urlparse
from nltk.corpus import stopwords

import pandas as pd


logger = logging.getLogger(__name__)

stop_words = set(stopwords.words('spanish'))

column_name = ['title', 'body']

def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_liines_from_body(df)
    df = _tokenize_column(df, column_name)

    return df


def _read_data(filename):
    logger.info('Reading file {}'.format(filename))

    return pd.read_csv(filename)


def _extract_newspaper_uid(filename):
    logger.info('Extracting newspaper_uid')
    newspaper_uid = filename.split('_')[0]

    logger.info('Newspaper uid detected: {}'.format(newspaper_uid))
    return newspaper_uid


def _add_newspaper_uid_column(df, newspaper_uid):
    logger.info('Filling newspaper_uid column with {}'.format(newspaper_uid))
    df['newspaper_uid'] = newspaper_uid

    return df

def _extract_host(df):
    logger.info('Extracting host from urls')
    df['host'] = df['url'].apply(lambda url: urlparse(url).netloc)

    return df


def _fill_missing_titles(df):
    logger.info('Filling missing titles')
    missing_titles_mask = df['title'].isna()

    missing_titles = (df[missing_titles_mask]['url']
                        .str.extract(r'(?P<missing_titles>[^/]+)$', expand=True)
                        .applymap(lambda title: title.split('_'))
                        .applymap(lambda title_word_list: ' '.join(title_word_list))
                    )

    df.loc[missing_titles_mask, 'title'] = missing_titles.loc[:, 'missing_titles']

    return df

def _generate_uids_for_rows(df):
    logger.info('Generating uids for each row')
    uids = (df
           .apply(lambda row: hashlib.md5(bytes(row['url'].encode())),axis=1)
           .apply(lambda hash_object: hash_object.hexdigest())
           )

    df['uid'] = uids

    return df.set_index('uid')

def _remove_new_liines_from_body(df):
    logger.info('Removes new lines from body')

    stripped_body = (df
                       .apply(lambda row: row['body'], axis =1)
                       .apply(lambda body: list(body))
                       .apply(lambda letters: list(map(lambda letter: letter.replace('\n', ''), letters)))
                       .apply(lambda letters: list(map(lambda letter: letter.replace('\r', ''), letters)))
                       .apply(lambda letters: ''.join(letters))
                    )
    df['body'] = stripped_body

    return df


def _tokenize_column(df, column_name):

    for column in column_name:
        tokenize_column = (df
                            .dropna()
                            .apply(lambda row: nltk.word_tokenize(row[column]), axis=1)
                            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                            .apply(lambda valid_word_list: len(valid_word_list))
                            )

        df['n_tokens_'+ column] = tokenize_column


    return df


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename',
                        help='The path to the dirty data',
                        type=str)

    args = parser.parse_args()

    df = main(args.filename)
    print(df)

Este es mi código del reto:

def main(filename):
    """Path of the main function"""
    logger.info('Sarting cleaning process')
    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uid_for_rows(df)
    df = _remove_new_lines_from_body(df)
    column_name = input('Name of column is...\t')
    df['n_tokenize_{}'.format(column_name)] = _tokenizing_title_(df, column_name)
    return df


def _tokenizing_title_(df, column_name):
    logger.info('Ready for tokenize {}'.format(column_name))

    return (df
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis = 1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda valid_word_list: len(valid_word_list))
           )

Tengo el siguiente error : AttributeError: ‘DataFrame’ object has no attribute ‘appĺy’

import nltk
from nltk.corpus import stopwords

stop_words = set(stopwords.words('spanish'))

def tokenize_column(df, column_name):
    return (df
            .dropna()
            .appĺy(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
            )

el_universal['n_tokens_title'] = tokenize_column(el_universal, 'title')

el_universal```

Respuesta al desafío:

  1. En el archivo “newspaper_receipe.py”, en la funcioń main agregué lo siguiente:
# desafio
    df = _add_tokenize_title(df)
    df = _add_tokenize_body(df)
  1. Agregué las funciones mencionadas:
def _add_tokenize_title(df):
    df['n_tokens_title'] = _tokenize_column(df, 'title')
    return df

def _add_tokenize_body(df):
    df['n_tokens_body'] = _tokenize_column(df, 'body')
    return df
  1. Agregué la función para tokenizar una columna:
def _tokenize_column(df, column_name):
    return (df
            .dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis = 1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
            )

Y listo !

yo lo hice asi

def _tokenize_column(df, column_name):
	logger.info('Tokenize words')

	stop_words = set(stopwords.words('spanish'))
	tokenize = (df
			.dropna()
			.apply(lambda row: nltk.word_tokenize(row[column_name]), axis = 1)
			.apply(lambda tokens: list(filter(lambda tokens: tokens.isalpha(), tokens)))
			.apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
			.apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
			.apply(lambda valid_word_list: len(valid_word_list))
			)

	df['n_tokens_titles'] = tokenize
	return df

def main(filename, column_name):
	logger.info('Starting cleaning process')

	df = _red_data(filename)
	newspaper_uid = _extract_newspaper_uid(filename)
	df = _add_newspaper_uid_column(df, newspaper_uid)
	df = _extract_host(df)
	df = _fill_missing_titles(df)
	df = _generate_uids_for_rows(df)
	df = _remove_new_lines_from_body(df)
	df = _tokenize_column(df, column_name)

	return df

Tarea
En el main:

df['n_tokens_title'] = _tokenize_column(df, 'title')
df['n_tokens_body'] = _tokenize_column(df, 'body')

La función _tokenize_column sigue siendo la misma:

def _tokenize_column(df, column_name):
    logger.info(' Applying tokens to {} ...'.format(column_name))
    stop_words = set(stopwords.words('spanish'))
    return (df
            .dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis = 1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
           )

De donde saca los parametros de entrada en los lambdas anidados?? en el video serian: row, tokens, tokens y word_list?

Como sabe el Lambda de donde sacar esos parametros??

Mi solucion!!

def tokenize_column(df, column_name, stop_words):
    return (df
            .dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
            )


def _tokenize_columns(df, columns, stop_words):
    for column in columns:
        df['n_token_' + column] = tokenize_column(df, column, stop_words)
    return df

Instalar NLTk no descarga automaticamente todos los archivos necesarios. Cuando vaya a utilizar la biblioteca por primera vez, debe ejecutar los siguientes comandos:

  1. nltk.download(‘stopwords’)

  2. nltk.download(‘punkt’)

Mi resultado del reto:

Para el encabezado

import nltk 	#permite separar palabras y contar la frecuencia de estas

logging.basicConfig(level = logging.INFO)
from urllib.parse  import urlparse
from nltk.corpus import stopwords #stopwords son palabras que no aportan al analisis (de, a, el, la, etc)

import pandas as pd

logger = logging.getLogger(__name__)
stop_words = set(stopwords.words('spanish')) #el conjunto de los stopwords en español

En la función main

	df = _tokenize_columns(df, 'body')
	df = _tokenize_columns(df, 'title')

creando la funcion

	#regresa una columna con el numero de palabras 'claves' de la columna que pide como argumento
def _tokenize_columns(df,column_name):
	global stop_words
	logger.info('Start token the {} column'.format(column_name))

	count_tokens = (df
					.dropna()																				
					.apply (lambda row: nltk.word_tokenize(row[column_name]), axis = 1)						
					.apply (lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))				
					.apply (lambda tokens: list(map(lambda token:token.lower(), tokens)))					
 					.apply (lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))	
					.apply (lambda valid_word_list: len(valid_word_list))									
					)
	
	df['n_tokens_{}'.format(column_name)] = count_tokens		
	
	return df

Sigo teniendo error con mi codigo, no encuentro en donde esta el error.

Dejo mi parte, por si ayuda:

def _tokenize_column(df,column_name):
    tokens=(df
        .dropna()
        .apply(lambda row: nltk.word_tokenize(row[column_name]),axis=1)
        .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
        .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
        .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
        .apply(lambda valid_word_list: len(valid_word_list))
    )
    df['tokens_'+column_name] =tokens
    return df

el reto B):

en el main():

df=_tokenize_column(df,'title')
df=_tokenize_column(df,'body')

la funcion tokenize_column:

def _tokenize_column(df,column_name):
    logger.info('Tokenizing column {}'.format(column_name))

    stop_words=set(stopwords.words('spanish'))

    token_column=(df
                    .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                    .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
                    .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                    .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                    .apply(lambda valid_word_list: len(valid_word_list))
                    )
    df['token_{}'.format(column_name)]=token_column
    logger.info('column {} tokenized'.format(column_name))
    return df
def _tokenize_column(df, column_name):
    stop_words = set(stopwords.words('spanish'))
    for column in column_name:
        logger.info(f'Tokenizing column {column}')
        df['n_token_' + column] = (df
                                .dropna()
                                .apply(lambda row: nltk.word_tokenize(row[column]), axis = 1)
                                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                                .apply(lambda valid_word_list: len(valid_word_list))
                                )
    return df
C:\mintic\python2\web_scrapper>py receta.py semana_2020_04_10_articulos.csv                                                                         INFO:__main__:Inicia limpiado                                             INFO:__main__:Leyendo archivo semana_2020_04_10_articulos.csv             INFO:__main__:Extrayendo id sitio                                         INFO:__main__:Hallada id sitio: semana                                    INFO:__main__:Llenando uid_sitio con semana                               INFO:__main__:Extrayendo el host de urls                                  INFO:__main__:Llenando titulos faltantes                                  INFO:__main__:Generando uid para cada fila                                INFO:__main__:Limpiando el cuerpo del articulo                            INFO:__main__:Tokenizando palabras en titulo                              INFO:__main__:Tokenizando palabras en cuerpo                                                                tk_titulo  tk_cuerpo                    uid                                                                       35e28f859991bd2375ac31c604236ce2          3        621                    2423919c056437f6db54529c7ba63dab          7        383                    d770672fac28323de9a11b0dd72d69a9          9        415                    f366db1296904bab99f168c3457b09f3          8        378                    43ce0880c70d530f7a5e282ca6090d1a          7        340                    ...                                     ...        ...                    27f4d9dbf2e392aa7ba679b741cc03eb          6        370                    093927475a475c7df08c4969415a44b9          6        705                    97efd70814caf43048ecaed9210a8849          6       1186                    d7d19208fd313e8166d1bbb607720de6          4        285                    5ae8615129620ba12d131d1125a8d9b9          4        335                                                                                              [70 rows x 2 columns] 

Les paso el código fragmentado para que sea más fácil de entender 😃

  1. Armamos el token
  2. Quitamos los stopwords
  3. Eliminamos todas las palabras que no sean alfa-numericas.
def filtro_1(sentence):
    """
    Busco los tokens del string
    """
    
    token = nltk.word_tokenize(sentence)
    return token
def filtro_2(token):
    """
    Quito los stopwords
    """
    
    stop_words = set(stopwords.words('english'))
    tokens_2 = [word for word in token if not word in stop_words ]
    return tokens_2
def filtro_3(token):
    """
    Quito las palabras que no sean alfa-numericas (ejemplo -> o :/ )
    """
    
    tokens_2 = [word for word in token if word.isalpha() == True]
    
    return tokens_2

— Ahora implementamos la funciones por separado —

  • Implementamos la columna title
df2 = df1['title'].apply(lambda row: filtro_1(row))
df2 = df2.apply(lambda row: filtro_2(row))
df2 = df2.apply(lambda row: filtro_3(row))

df1['n_tokens_title'] = df2.apply(lambda row: len(row))
df1.head()
  • Implementamos la columna content
df3 = df1['content'].apply(lambda row: filtro_1(row))
df3 = df3.apply(lambda row: filtro_2(row))
df3 = df3.apply(lambda row: filtro_3(row))

df1['n_tokens_content'] = df3.apply(lambda row: len(row))
df1.head()

PD: se recuerda que cuando se aplica la funcion df.apply() este retorna un iterador row, el cual va fila por fila, como si fuera un for.

Hola, lo intenté así en main:

Y así definí la función:

Corro el código y me da esto:

¿Alguno sabe qué es? Se lo agradecería mucho.

Para los que no hayan podido instalar nltk, simplemente den:
pip install nltk
Dado que esto no instala todas las librerías, escriban:
python
Esto los lleva al intérprete de python, allí importan la librería con:
import nltk
Y luego escriben:
nltk.download()
Con esto les abre un instalador, le dan en all y descargan todos los paquetes.

muy interesante!

Excelente clase

Excelente clase

#tokenizar el titulo y el body
import nltk
from nltk.corpus import stopwords

#nltk.donwload('punkt')
#nltk.download('stopwords')
stop_words = set(stopwords.words('spanish'))
def tokenize_column(df, column_name):
    return (df
               .dropna()
               .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
               .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
               .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
               .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
               .apply(lambda valid_word_list: len(valid_word_list))
            )
el_universal['n_tokens_title'] = tokenize_column(el_universal, 'title')

el_universal```

Tengo este error al ejecutar el script

AttributeError: ‘NoneType’ object has no attribute ‘dropna’

Aca esta mi codigo

import argparse
import hashlib
import logging
logging.basicConfig(level=logging.INFO)
from urllib.parse import urlparse
#import urllib.parse
import nltk # permite separar palabras y contar la frecuencia de estas
from nltk.corpus import stopwords # stopwords con palabras que no aportan al analisis ( de, a, el, la, por, para)

import pandas as pd

logger = logging.getLogger(name)

def main(filename):
logger.info(‘Empieza proceso de limpieza’)

df = _read_data(filename)
newspaper_uid = _extract_newspaper_uid(filename)
df = _add_newspaper_uid_column(df, newspaper_uid)
df = _extract_host(df)
df = _fill_missing_titles(df)
df = _generate_uids_for_rows(df)
df = _remove_new_lines_from_body(df)
df = _tokenize_column(df, 'title')
df = _tokenize_column(df, 'body')

return df

def _read_data(filename):
logger.info(‘Leyendo el archivo {}’.format(filename))

return pd.read_csv(filename)

def extract_newspaper_uid(filename):
logger.info(‘Extrayendo el newspaper_uid’)
newspaper_uid = filename.split(’
’)[0]

logger.info('newspaper_uid detectado: {}'.format(newspaper_uid))

return newspaper_uid

def _add_newspaper_uid_column(df, newspaper_uid):
logger.info(‘Completando la columna newspaper_uid con {}’.format(newspaper_uid))
df[‘newspaper_uid’] = newspaper_uid

return df

def _extract_host(df):
logger.info(‘Extrayendo host de las urls’)
df[‘host’] = df[‘url’].apply(lambda url: urlparse(url).netloc)

return df

def _fill_missing_titles(df):
logger.info(‘Filling missing titles’)
missing_titles_mask = df[‘title’].isna()
missing_titles = (df[missing_titles_mask][‘url’]
.str.extract(r’(?P<missing_titles>[^/]+)$’)
.astype(str).applymap(lambda title:title.replace(’-’,’ '))
)

df.loc[missing_titles_mask, 'title'] = missing_titles.loc[:, 'missing_titles']

return df

#def _generate_uids_for_rows(df):

<h1>logger.info(‘Generando ids para cada fila’)</h1> <h1>uids = (df</h1> <h1>.apply(lambda row: hashlib.md5(bytes(row[‘url’].encode())), axis=1)</h1> <h1>.apply(lambda hash_object: hash_object.hexdigest())</h1> <h1>)</h1> <h1>df[‘uid’] = uids</h1> <h1>return df.set_index(‘uid’)</h1>

def _generate_uids_for_rows(df):
logger.info(‘Generating uids for each row’)
uids = (df
.apply(lambda row: hashlib.md5(bytes(row[‘url’].encode())), axis=1)
.apply(lambda hash_object: hash_object.hexdigest())
)

df['uid'] = uids

return df.set_index('uid')

def _remove_new_lines_from_body(df):
logger.info(‘Remover nuevas lineas del body’)

stripped_body = (df
				.apply(lambda row: row['body'], axis=1)
				.astype(str).apply(lambda body: list(body))
				.apply(lambda letters: list(map(lambda letter: letter.replace('\n', ' '), letters)))
				.apply(lambda letters: ''.join(letters))
				)

df['body'] = stripped_body

return df

def _tokenize_column(df, column_name):
logger.info(‘Contando las palabras del: {}’.format(column_name))

stop_words = set(stopwords.words('spanish')) # conjunto de stopwords en español
token_count = (df
					
					.dropna()
					.apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
					.apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
					.apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
					.apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
					.apply(lambda valid_word_list: len(valid_word_list))
				)

column = 'n-tokens_{}'.format(column_name)
df[column] = token_count

if name == ‘main’:
parser = argparse.ArgumentParser()
parser.add_argument(‘filename’,
help=‘El Path de los datos sucios’,
type=str)

args = parser.parse_args()

df = main(args.filename)
print(df)

Esta es la documentación de nltk que es una librería de procesamiento de lenguaje natural, cabe destacarse que este tipo de librerías están mucho más desarrolladas para el procesamiento del inglés, ya que reconocen mejor las stopwords de este idioma. Igual sigue siendo una gran herramienta para el procesamiento de datos en nuestro idioma.

                                  n_tokens_title  n_tokens_body
uids
6fdd430fc84200bac3628d054d2818fd               1            540
32f2d611b43d265f3dfa1bb25b4b9ade               3            283
865518353fb51b8d6d3b913bb5d1aea0               8             95
691bc1e8f75ddda21cfbe7ba495812e4               5            333
4992b5b32ae2a021daec4f9e5608235b               7            301
...                                          ...            ...
4b33fb9fc3a626a456721208cb419cf4               2            271
927224685012110a3a7d25418cb602f0               5            481
f521aaec8ca5eee43bd113d0a40ae718               6             89
7ba1a5c5a7fea5ac226e7b32389dd7b7               7            101
bafa7259c096b210aace06d8f893b857               6            411

[144 rows x 2 columns]```

Son realmente interesantes todas las herramientas que provee Python.

Mi código al reto:

def main(filename):
    logger.info('Starting cleaning process')
    
    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_form_body(df)
    df = tokenize_column(df, 'title')

    return df
def tokenize_column(df, column_name):
	logger.info('Count the keys words')

    tokenize = (df
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis = 1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
            )
    
    
    df['n_tokens_title'] = tokenize
    return df

Si les interesó mucho este tema para Procesamiento de lenguaje Natural, les recomiendo mucho aprender spacy, que es otra librería que complementa muy bien

Hola! estoy siguiendo la ejecución en Jupyter y al correr este pedazo de codigo tengo un error. ¿Alguien mas le sucedio esto? ¿Saben que puede estar pasando?

Comparto mi código funcionando:

def _tokenize_column(df, column_name):
    logger.info('Tokenizing column: '+column_name)
    stop_words = set(stopwords.words('spanish'))

    new_column_name = 'n_tokens_'+column_name

    df[new_column_name] = (df
        .dropna()
        .apply(lambda row: nltk.word_tokenize(row[column_name]),axis=1)
        .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
        .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
        .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
        .apply(lambda valid_words_list: len(valid_words_list))
    )

    return df

El código del reto

def _significant_words(df):
    logger.info("Calculating the number of significant words on title and body.")
    n_tokens_count = (df
                    .dropna()
                    .apply(lambda row: nltk.word_tokenize(row["title"]), axis = 1)
                    .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                    .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                    .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                    .apply(lambda valid_word_list: len(valid_word_list))
                      )
    df["n_tokens_title"] = n_tokens_count
    
    return df

Cumpliendo con el reto.
Este es el código de la función:

def _tokenize_column(df, column_name):
    logger.info('Tokenize the column body and title')
    return (
        df
        .dropna()
        .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
        .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
        .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
        .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
        .apply(lambda valid_word_list: len(valid_word_list))
    )

Este es el llamado a la funcion desde el main:

df['n_tokens_title'] = _tokenize_column(df, 'title')
    df['n_tokens_body'] = _tokenize_column(df, 'body')

Y este es el resultado cuando se ejecuta el codigo:
(

Buenas tardes, estas son las instrucciones que agregue a la receta:
Llamado de las funciones:
#7.Tokenizar el título y el cuerpo del artículo
df = _tokenize_title(df)

df = _tokenize_body(df)

Sección de cada función:

#7.Tokenizar el título y el cuerpo del artículo
def _tokenize_title(df):
logger.info(‘10.Tokenize the title’)
df[‘n_tokens_title’] = _tokenize_column(df, ‘title’)

return df

def _tokenize_body(df):
logger.info(‘11.Tokenize the body’)
df[‘n_tokens_body’] = _tokenize_column(df, ‘body’)

return df

def _tokenize_column(df, column_name):
stop_words = set(stopwords.words(‘spanish’))
return (df
.dropna() #Eliminar si aún quedan algunos NaN, pq nltk falla
.apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
.apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
.apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
.apply(lambda words_list: list(filter(lambda word: word not in stop_words, words_list)))
.apply(lambda valid_word_list: len(valid_word_list))
)

Reto cumplido

import nltk
from nltk.corpus import stopwords

nltk.download('punkt')
nltk.download('stopwords')
stop_words = set(stopwords.words('spanish'))```
dentro de la funcion main :


df[‘n_tokens_title’] = tokenize_column(df,‘title’)
df[‘n_tokens_body’] = tokenize_column(df,‘body’)```

Y mi funcion para tokenizar:

def tokenize_column(df,column_name):
    return(df
                 .dropna()
                 .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                 .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
                 .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                 .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                 .apply(lambda valid_word_list: len(valid_word_list))
          )```

Logré realizar el reto fue excelente y me siento muy feliz porque lo hice…

He aquí mi resultado:

Se adicionó a la receta, la nueva función que se encarga de contar las palabras más relevantes del título y cuerpo del artículo.

def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df,newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df = _tokenize_column(df)

    return df


def _tokenize_column(df):
    stop_words = set(stopwords.words('english'))
    def _tokenize(column_name):
        return (df.dropna()
              .apply(lambda row: nltk.word_tokenize(row[column_name]),axis=1)
              .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
              .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
              .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
              .apply(lambda valid_word_list: len(valid_word_list))
           )
    df['n_tokens_title'] = _tokenize('title')
    df['n_tokens_body'] = _tokenize('body')
    return df

yo lo hice asi en mi intento por hacerlo solo me quedo parecido pero esta bien.

Se pudo realizar la tarea y algunas mejoras gracias a los aportes de los compañeros, tal vez sea un poco anticuado decirlo en 2020, pero los compañ[email protected] en platzi son los verdaderos MVPs

muchachos les dejo un excelente tutorial de NLP con Python NLTK (con ejemplos simples!)
https://likegeeks.com/es/tutorial-de-nlp-con-python-nltk/

En mi caso, estoy tratando con títulos de libros, el problema es que no todos están en inglés así que, en lugar de enviar el df completo, envié los libros cuyo código de idioma sea “en”

gutenberg["n_tokens_title"] = tokenize_column(gutenberg[gutenberg["language"]=="en"], "title")

aqui mi codigo

import nltk

from nltk.corpus import stopwords

stop_words = set(stopwords.words(‘spanish’))

def tokenize_column(df, column_name):
return (df.dropna().apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
.apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
.apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
.apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
.apply(lambda valid_word_list: len(valid_word_list))
)

el_universal[‘n_tokens_title’] = tokenize_column(el_universal, ‘title’)

el_universal

Me pregunto si hay nltk.grineer o nltk.sentient

Este es mi código:

<# tokenizar el titulo y el body

import nltk

from nltk.corpus import stopwords

#nltk.download('punkt')
#nltk.download('stopwords')

stop_words = set(stopwords.words('spanish'))

def tokenize_column(df, column_name):
    return (df
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(),tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(),tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
            )

el_universal['n_tokens_title'] = tokenize_column(el_universal, 'body')

el_universal>

Tengo este error: ValueError: cannot reindex from a duplicate axis

Así acomodé la función del reto:

def _tokenize_column(df, column_name):
    stop_words = set(stopwords.words('spanish'))

    token_column = (df
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
            )

    tokens_column_name = 'n_tokens_'+column_name
    df[tokens_column_name] = token_column

    return df

En la función main() se invocan así:

    df = _tokenize_column(df, 'title')
    df = _tokenize_column(df, 'body')    

Exportar a csv con el reto:

<h3>main</h3>
def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df =_add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_form_body(df)
    df = _data_enrichment(df)
    news_name = filename.split('_')
    df.to_csv(f'{news_name[0]}_limpio.csv', encoding='utf-8', sep= ';')

    return df
<h3>Data enrichment</h3>
def _data_enrichment(df):

    def _tokenize_column(df, column_name):
        logger.info(f'Tokenizing title and {column_name}')
        return (df
                    .dropna()
                    .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                    .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                    .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                    .apply(lambda words_list: list(filter(lambda word: word not in stop_words, words_list)))
                    .apply(lambda valid_word_list: len(valid_word_list)))

    df[f'n_tokens_title'] = _tokenize_column(df, 'title')
    df[f'n_tokens_body'] = _tokenize_column(df, 'body')

    return df

PD: Esta solución me parece mejor, se entinde más y no es mia, yo habia hecho una que no se entendia muy bien.

Muy buen ejercicio. Se pudo completar con ayuda de los comentarios de los compañeros.

nltk es un módulo de Python para la creación de programas Python para trabajar con datos en lenguaje humano. que contiene muchasf unciones diseñadas para su uso en el análisis lingüístico de documentos y en el procesamiento de lenguaje natural.

Reto:

import argparse
import logging
import hashlib
import nltk
from nltk.corpus import stopwords
logging.basicConfig(level=logging.INFO)
from urllib.parse import urlparse
import pandas as pd

stop_words = set(stopwords.words('spanish'))

nltk.download('punkt')
nltk.download('stopwords')

logger = logging.getLogger(__name__)

def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)    
    newspaper_uid = _extract_newspapers_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df['n_tokens_title'] = _tokenize_columns(df, 'title')
    df['n_tokens_body'] = _tokenize_columns(df, 'body')
    return df


def _read_data(filename):
    logger.info('Reading file {}'.format(filename))

    return pd.read_csv(filename)

def _extract_newspapers_uid(filename):
    logger.info('Extracting newspaper uid')
    newspaper_uid = filename.split('_')[0]

    logger.info('Newspaper uid detected: {}'.format(newspaper_uid))

def _add_newspaper_uid_column(df, newspaper_uid):
    logger.info('Filling newspaper_uid column with {}'.format(newspaper_uid))
    df['newspaper_uid'] = newspaper_uid
    return df

def _extract_host(df):
    logger.info('Extracting host from urls')
    df['host'] = df['url'].apply(lambda url: urlparse(url).netloc)

    return df

def _fill_missing_titles(df):
    logger.info('Filling missing titles')
    missing_title_mask = df['title'].isna()
    missing_titles = df[missing_title_mask]['url'].str.extract(r'(?P<missing_titles>[^/]+)$').applymap(lambda title: title.split('-')).applymap(lambda title_word_list: (' ').join(title_word_list))
    df.loc[missing_title_mask, 'title'] = missing_titles.loc[:, 'missing_titles']

    return df

def _generate_uids_for_rows(df):
    logger.info('Generating uids for each row')
    uids = df.apply(lambda row: hashlib.md5(bytes(row['url'].encode())), axis=1).apply(lambda hash_object: hash_object.hexdigest())
    df['uid']=uids

    return df.set_index('uid')

def _remove_new_lines_from_body(df):
    logger.info('Removing new lines from body')

    stripped_body = df.apply(lambda row: row['body'], axis = 1).apply(lambda body: list(body)).apply(lambda letters: list(map(lambda letter: letter.replace('\n', ' '), letters))).apply(lambda letters: ''.join(letters))
    df['body']= stripped_body

    return df

def _tokenize_columns(df, column_name):
    logger.info('Tokenizing {} column'.format(column_name))
    return (df.dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
            )

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename', help = 'The path to the dirty data',
                         type = str)

    args = parser.parse_args()
    filename = args.filename
    df = main(filename)
    print()
    #print(df)

Les comparto mi solución al reto

Archivo main .py

import argparse
import hashlib
import nltk
from nltk.corpus import stopwords
import logging
logging.basicConfig(level=logging.INFO)
from urllib.parse import urlparse


import pandas as pd


logger = logging.getLogger(__name__)

def main(filename):
    logger.info('Starting cleaning process')
    
    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df = _n_tokenize_content(df)

    return df

def _read_data(filename):
    logger.info('Reading file {}'.format(filename))

    return pd.read_csv(filename, encoding= 'ISO-8859-1')


def _extract_newspaper_uid(filename):
    logger.info('Extracting Newspaper uid')
    newspaper_uid = filename.split('_')[0]
    logger.info('Newspaper uid Detected: {}'.format(newspaper_uid))

    return newspaper_uid


def _add_newspaper_uid_column(df, newspaper_uid):
    logger.info('Filling newspaper_uid column with {}'.format(newspaper_uid))
    df['newspaper_uid'] = newspaper_uid

    return df


def _extract_host(df):
    logger.info("Extracting article's host")
    df['host'] = df['url'].apply(lambda url: urlparse(url).netloc)

    return df


def _fill_missing_titles(df):
    logger.info("Filling missing titles")
    missing_titles_mask = df['title'].isna()

    missing_titles = (df[missing_titles_mask]['url']
                        .str.extract(r'(?P<missing_titles>[^/]+)$')
                        .applymap(lambda title: title.split('-'))
                        .applymap(lambda title_word_list: ' '.join(title_word_list))
                    )
    df.loc[missing_titles_mask, 'title'] = missing_titles.loc[:,'missing_titles']

    return df

def _generate_uids_for_rows(df):
    logger.info("Genarating UIDs for rows")
    uids = (df
            .apply(lambda row: hashlib.md5(bytes(row['url'].encode())), axis=1)
            .apply(lambda hash_object: hash_object.hexdigest())
           )
    df['uid'] = uids

    return df.set_index('uid')


def _remove_new_lines_from_body(df):
    logger.info("Remove new lines from body")
    stripped_body = (df
                    .apply(lambda row: row['body'], axis=1)
                    .apply(lambda body: list(body))
                    .apply(lambda letters: list(map(lambda letter: letter.replace('\n',''), letters)))
                    .apply(lambda letters: ''.join(letters))
                )
    df['body']= stripped_body

    return df


def _n_tokenize_content(df):
    logger.info("Tokenize words from title")
    stop_words = set(stopwords.words('spanish'))
    def tokenize_column(df, column_name):
        return (df
               .dropna()
               .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
               .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens))) #filtro de palabras alfanuméricas
               .apply(lambda tokens: list(map(lambda token: token.lower(), tokens))) #comparación de mayusculas y minusculas
               .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list))) #revisa si la palabra está en nuestras stopwrods
               .apply(lambda valid_word_list: len(valid_word_list)) #longitud de la lista de palabras válidas
           )
    df['n_tokens_title'] = tokenize_column(df, 'title')
    df['n_tokens_body'] = tokenize_column(df, 'body')

    return df

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename',
                        help='The path to the dirty data',
                        type=str)

    args = parser.parse_args()

    df = main(args.filename)
    print(df) 

Comparto mi código del reto

Agregué:

import nltk
from nltk.corpus import stopwords

main

df = _add_tokenize(df)

Nuevas funciones

stop_words = set(stopwords.words(('spanish')))
def _tokenize_columns(df, column_name):
    logger.info('Tokenize column: {}'.format(column_name))

    return (df
            .dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
            )


def _add_tokenize(df):
    logger.info('Add tokenize for title and body')

    df['n_tokens_title'] = _tokenize_columns(df, 'title')
    df['n_tokens_body'] = _tokenize_columns(df, 'body')

    return df

Este es mi aporte

def _data_enrichement(df, column_name):
    return (df
                .dropna()
                .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
                .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
                .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
                .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
                .apply(lambda valid_word_list: len(valid_word_list))
    )
    df['n_tokens_title'] = _data_enrichement(df, 'title')
    df['n_tokens_body'] = _data_enrichement(df, 'body')

comparto el código de newspaper_receipe.py

import argparse
import logging
import hashlib
import nltk
logging.basicConfig(level=logging.INFO)
from urllib.parse import urlparse
from nltk.corpus import stopwords

import pandas as pd

logger = logging.getLogger(__name__)
stop_words = set(stopwords.words('spanish'))


def main(filename):
    logger.info('Starting cleaning process')

    df = _read_data(filename)
    newspaper_uid = _extract_newspaper_uid(filename)
    df = _add_newspaper_uid_column(df, newspaper_uid)
    df = _extract_host(df)
    df = _fill_missing_titles(df)
    df = _generate_uids_for_rows(df)
    df = _remove_new_lines_from_body(df)
    df['n_tokens_title'] = _tokenize_column(df, 'title')
    df['n_tokens_body'] = _tokenize_column(df, 'body')

    return df

def _read_data(filename):
    logger.info('Reading file {}'.format(filename))

    return pd.read_csv(filename, encoding = 'unicode_escape')


def _extract_newspaper_uid(filename):
    logger.info('Extracting newspaper uid')
    newspaper_uid = filename.split('_')[0]

    logger.info('Newspaper uid detected: {}'.format(newspaper_uid))
    return newspaper_uid

def _add_newspaper_uid_column(df, newspaper_uid):
    logger.info('Filling newspaper_uid column with {}'.format(newspaper_uid))
    df['newspaper_uid'] = newspaper_uid

    return df

def _extract_host(df):
    logger.info('Extracting host from urls')
    df['host'] = df['url'].apply(lambda url:urlparse(url).netloc)

    return df

def _fill_missing_titles(df):
    logger.info('Filling missing titles')
    missing_titles_mask = df['title'].isna()

    missing_titles = (df[missing_titles_mask]['url']
                        .str.extract(r'(?P<missing_titles>[^/]+)$')
                        .applymap(lambda title: title.split('-'))
                        .applymap(lambda title_word_list: ' '.join(title_word_list))
                      )

    df.loc[missing_titles_mask, 'title'] = missing_titles.loc[:, 'missing_titles']

    return df

def _generate_uids_for_rows(df):
    logger.info('Generating uids for each row')
    uids = (df
            .apply(lambda row:hashlib.md5(bytes(row['url'].encode())), axis=1)
            .apply(lambda hash_object: hash_object.hexdigest())
            )

    df['uid'] = uids

    return df.set_index('uid')


def _remove_new_lines_from_body(df):
    logger.info('Remove new lines from body')

    stripped_body = (df
                     .apply(lambda row: row['body'], axis=1)
                     .apply(lambda body: list(body))
                     .apply(lambda letters: list(map(lambda letter: letter.replace('\n', ' '), letters)))
                     .apply(lambda letters: list(map(lambda letter: letter.replace('\r', ' '), letters)))
                     .apply(lambda letters: ''.join(letters))
                    )

    df['body'] = stripped_body

    return df

def _tokenize_column(df, column_name):
    return(df
            .dropna()
            .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            .apply(lambda valid_word_list: len(valid_word_list))
        )

if __name__=='__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('filename',
                         help='The path to the dirty data',
                         type=str)
    args = parser.parse_args()
    df = main(args.filename)

    print(df)

excelente ejercicio y reto

stop_words = set(stopwords.words('spanish'))

def tokinize_column(df, column_name):
    return (df
               .dropna()
               .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
               .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
               .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
               .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
               .apply(lambda valid_word_list: len(valid_word_list))
           )

el_universal['n_tokens_title'] = tokinize_column(el_universal, 'title')
el_universal

PARA LOS QUE QUIERAN HACER ALGO DIFERENTE AL COPY PASTE
Vamos a crear un csv limpio con las nuevas columnas, ademas vamos a cambiar el metodo extract_newspaper_uid a público para que el nombre del .csv tenga parentesco con el de la pagina web (Recordar que esto tiene que aplicar a cualquier pagina web)

Entonces en el metodo main al final agregamos

# Tokenizar Title
    df = _tokenize_column(df, 'title')
    # Tokenizar Body
    df = _tokenize_column(df, 'body')
    return df

A continuacion los metodos de tokenizacion
_tokenize_column Llama a _tokenizing_words y genera la nueva serie en nuestro DataFrame
_tokenizing_words Tokeniza la nueva Serie y la retorna

def _tokenize_column(df, column_name):
    logger.info('Starting tokenizing process')
    tokenizing_serie = _tokenizing_words(df, column_name)
    
    df['n_tokens_{}'.format(column_name)] = tokenizing_serie
    return df

def _tokenizing_words(df, column_name):
    return (df
            #Eliminar cualquier Na
           .dropna()
            #Tokenizar la columna o serie
           .apply(lambda row: nltk.word_tokenize(row[column_name]), axis=1)
            #Eliminar todas aquellas palabras no Alphanumericas
           .apply(lambda tokens: list(filter(lambda token: token.isalpha(), tokens)))
            #Convertir los tokens en lowercase
           .apply(lambda tokens: list(map(lambda token: token.lower(), tokens)))
            #Eliminar stopwords
           .apply(lambda word_list: list(filter(lambda word: word not in stop_words, word_list)))
            #Obtener el length de la lista de palabras
           .apply(lambda valid_word_list: len(valid_word_list))
          )    

Finalmente en la func if name == ‘main’ despues de imprimir…
Vamos a generar el .csv, pero antes al metodo _extract_newspaper_uid lo volvemos público eliminado el ‘_’ al comienzo de su nombre, quedaria asi extract_newspaper_uid. Esto con el fin de reutilizarlo en la funcion principal

Ahora si, al final de la funcion

if __name__ == '__main__':
    # Preguntar al usuario que tipo de dato quiere
    parser = argparse.ArgumentParser()
    parser.add_argument('filename',help='The path to the dirty data', type=str)
    # Parsear Argumentos
    args = parser.parse_args()
    df = main(args.filename)
    print(df)
    # print(df.iloc[66]['title'])
    #Escribir csv
    newspaper_uid = extract_newspaper_uid(args.filename)
    # Para grabar el DataFrame en csv se usa la siguiente línea de código,
    df.to_csv('{}_limpio.csv'.format(newspaper_uid), encoding="utf-8")