Idea

La idea seria hacer una busqueda de un termino en twitter , bajarse los mensajes asociados , pasarlos por un clasificador bayesiano y representar los resultados en Minecraft.

A su vez modificando la geografia que lo anterior genera en Minecraft cambiar los parametros del clasificador Bayesiano para que los siguientes mensajes que lleguen sean clasificados según los nuevos parametros del clasificador.


In [ ]:

Procesamiento del texto


In [ ]:
import json
from pprint import pprint
 
with open('periodicos.json', 'r') as f:
    tweets = json.load(f)
    
#pprint(tweets)

tweets["el_pais"]

In [ ]:
import re
 
emoticons_str = r"""
    (?:
        [:=;] # Eyes
        [oO\-]? # Nose (optional)
        [D\)\]\(\]/\\OpP] # Mouth
    )"""
 
regex_str = [
    emoticons_str,
    r'<[^>]+>', # HTML tags
    r'(?:@[\w_]+)', # @-mentions
    r"(?:\#+[\w_]+[\w\'_\-]*[\w_]+)", # hash-tags
    r'http[s]?://(?:[a-z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-f][0-9a-f]))+', # URLs
 
    r'(?:(?:\d+,?)+(?:\.?\d+)?)', # numbers
    r"(?:[a-z][a-z'\-_]+[a-z])", # words with - and '
    r'(?:[\w_]+)', # other words
    r'(?:\S)' # anything else
]
    
tokens_re = re.compile(r'('+'|'.join(regex_str)+')', re.VERBOSE | re.IGNORECASE | re.UNICODE)
emoticon_re = re.compile(r'^'+emoticons_str+'$', re.VERBOSE | re.IGNORECASE | re.UNICODE)
 
def strip_non_ascii(string):
    ''' Returns the string without non ASCII characters'''
    stripped = (c for c in string if 0 < ord(c) < 127)
    return ''.join(stripped)

def strip_less_than_1_chars(string):
    ''' Returns the string without non ASCII characters'''
    stripped = (c for c in string if len(c)>3)
    return ''.join(stripped)

def tokenize(s):
    return tokens_re.findall(s)
 
def preprocess(s):
    try:
        ascii_string = strip_non_ascii(s)
        tokens = tokenize(ascii_string)
        tokens = [token.lower() for token in tokens if len(token) > 3 and not token.startswith('http')]
        return tokens
    except TypeError:
        return None
 
tweet = "RT @marcobonzanini: just an example! :D http://example.com #NLP"
print(preprocess(tweets["el_pais"][0][1]))
# ['RT', '@marcobonzanini', ':', 'just', 'an', 'example', '!', ':D', 'http://example.com', '#NLP']

In [ ]:
'http://example.com'.startswith('http')

In [ ]:
tweets_elpais_procesados = [(preprocess(tweet[1]),'positive') for tweet in tweets["el_pais"]]
print(tweets_elpais_procesados)

In [ ]:
#Cambiando un tweet de sentimiento

tweetConNuevoSentimiento = ([u'@elpais_america', u'tras', u'ciudadana', u'corredor', u'chapultepec', u'gobierno', \
                             u'presentar', u'otros', u'proyectos', u'para', u'transformarlo'], 'negative')

for index,tweet in enumerate(tweets_elpais_procesados):
    if tweets_elpais_procesados[index][0] == tweetConNuevoSentimiento[0]:
        del tweets_elpais_procesados[index]
        tweets_elpais_procesados.append(tweetConNuevoSentimiento)
        
print(tweets_elpais_procesados)

In [ ]:
import json
from collections import Counter
import nltk
from nltk.corpus import stopwords
import string
 
tweets_elpais_procesados = [preprocess(tweet) for (fecha,tweet) in tweets["el_pais"]]

punctuation = list(string.punctuation)
stop = stopwords.words('spanish') + punctuation + ['rt', 'via']

count_all = Counter()
terms_all = []

for tweet in tweets_elpais_procesados:
    terms_all = [term for term in tweet if term not in stop]
    # Update the counter
    count_all.update(terms_all)
    
# Print the first 5 most frequent words
print(count_all.most_common(5)) 
print(count_all)

Si el codigo anterior da error quizás se tengan que descargar los recursos del nltk con las sentencias:

import nltk
nltk.download()

In [ ]:
# Count terms only once, equivalent to Document Frequency
terms_single = set(terms_all)
# Count hashtags only
terms_hash = [term for term in preprocess(tweet['text']) 
              if term.startswith('#')]
# Count terms only (no hashtags, no mentions)
terms_only = [term for term in preprocess(tweet['text']) 
              if term not in stop and
              not term.startswith(('#', '@'))] 
              # mind the ((double brackets))
              # startswith() takes a tuple (not a list) if 
              # we pass a list of inputs

print terms_hash
print ""
print terms_only

Clasificador Bayesiano


In [ ]:
pos_tweets = [('I love this car', 'positive'),
              ('This view is amazing', 'positive'),
              ('I feel great this morning', 'positive'),
              ('I am so excited about the concert', 'positive'),
              ('He is my best friend', 'positive')]

neg_tweets = [('I do not like this car', 'negative'),
              ('This view is horrible', 'negative'),
              ('I feel tired this morning', 'negative'),
              ('I am not looking forward to the concert', 'negative'),
              ('He is my enemy', 'negative')]

test_tweets = [
    (['feel', 'happy', 'this', 'morning'], 'positive'),
    (['larry', 'friend'], 'positive'),
    (['not', 'like', 'that', 'man'], 'negative'),
    (['house', 'not', 'great'], 'negative'),
    (['your', 'song', 'annoying'], 'negative')]

In [ ]:
tweets = []
for (words, sentiment) in pos_tweets + neg_tweets:
    words_filtered = [e.lower() for e in words.split() if len(e) >= 3] 
    tweets.append((words_filtered, sentiment))

print (tweets)

In [ ]:
# The list of word features need to be extracted from the tweets. 
# It is a list with every distinct words ordered by frequency of appearance.
# We use the following function to get the list plus the two helper functions.

def get_words_in_tweets(tweets):
    all_words = []
    for (words, sentiment) in tweets:
        all_words.extend(words)
    return all_words

def get_word_features(wordlist):
    wordlist = nltk.FreqDist(wordlist)
    word_features = wordlist.keys()
    return word_features

word_features = get_word_features(get_words_in_tweets(tweets))

print word_features

In [ ]:
# To create a classifier, we need to decide what features are relevant. 
# To do that, we first need a feature extractor. 
# The one we are going to use returns a dictionary indicating what words are contained in the input passed. 
# Here, the input is the tweet. We use the word features list defined above along with the input 
# to create the dictionary

def extract_features(document):
    document_words = set(document)
    features = {}
    for word in word_features:
        features['contains(%s)' % word] = (word in document_words)
    return features

print(extract_features(['love', 'this', 'car']))

In [ ]:
# With our feature extractor, we can apply the features to our classifier using the method apply_features. 
# We pass the feature extractor along with the tweets list defined above.

training_set = nltk.classify.apply_features(extract_features, tweets)

print training_set

In [ ]:
# Now that we have our training set, we can train our classifier.

classifier = nltk.NaiveBayesClassifier.train(training_set)

print classifier.show_most_informative_features(32)

In [ ]:
tweet = 'Larry is my friend'
print classifier.classify(extract_features(tweet.split()))

In [ ]:


In [3]:
%%writefile preprocesadorTweets.py
import unicodedata
import json
import re
import random

def leeArchivoTweets(archivo):
    with open(archivo, 'r') as f:
        tweets = json.load(f)
    return tweets
 
emoticons_str = r"""
    (?:
        [:=;] # Eyes
        [oO\-]? # Nose (optional)
        [D\)\]\(\]/\\OpP] # Mouth
    )"""
 
regex_str = [
    emoticons_str,
    r'<[^>]+>', # HTML tags
    r'(?:@[\w_]+)', # @-mentions
    r"(?:\#+[\w_]+[\w\'_\-]*[\w_]+)", # hash-tags
    r'http[s]?://(?:[a-z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-f][0-9a-f]))+', # URLs
 
    r'(?:(?:\d+,?)+(?:\.?\d+)?)', # numbers
    r"(?:[a-z][a-z'\-_]+[a-z])", # words with - and '
    r'(?:[\w_]+)', # other words
    r'(?:\S)' # anything else
]
    
tokens_re = re.compile(r'('+'|'.join(regex_str)+')', re.VERBOSE | re.IGNORECASE | re.UNICODE)
emoticon_re = re.compile(r'^'+emoticons_str+'$', re.VERBOSE | re.IGNORECASE | re.UNICODE)

def eliminaTildes(s):
     return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))
    
def strip_non_ascii(string):
    ''' Returns the string without non ASCII characters'''
    stripped = (c.encode('ascii') for c in string if 0 < ord(c) < 127)
    return ''.join(stripped)

def strip_less_than_3_chars(string):
    ''' Returns the string without non ASCII characters'''
    stripped = (c for c in string if len(c)>3)
    return ''.join(stripped)

def tokenize(s):
    return tokens_re.findall(s)
 
def preprocesa(s):
    try: 
        sinTildes = eliminaTildes(s)
        ascii_string = strip_non_ascii(sinTildes)
        tokens = tokenize(ascii_string)
        tokens = [token.lower() for token in tokens if len(token) > 3 and not token.startswith('http')]
        return tokens
    except TypeError:
        return None

def preprocesaTweets(tweets, periodico):
    """ El sentimiento inicial de los tweets es aleatorio"""
    return [(preprocesa(tweet[1]),random.choice(['positive', 'negative'])) for tweet in tweets[periodico]]   

def preprocesaTweet(tweet, sentimiento):
    """ Esta funcion espera un tweet en formato ascii"""
    tokens = tokenize(tweet)
    tokens = [token.lower() for token in tokens if len(token) > 3 and not token.startswith('http')]
    return (tokens,sentimiento)


Overwriting preprocesadorTweets.py

In [2]:
%%writefile clasificadorBayesiano.py
import preprocesadorTweets as preprocesador
import nltk
from nltk.corpus import stopwords


class Clasificador():
    
    def __init__(self, train_tweets):
        self.word_features = self.get_word_features(self.get_words_in_tweets(train_tweets))
        
    # The list of word features need to be extracted from the tweets. 
    # It is a list with every distinct words ordered by frequency of appearance.
    # We use the following function to get the list plus the two helper functions.

    def get_words_in_tweets(self,tweets):
        all_words = []
        for (words, sentiment) in tweets:
            all_words.extend(words)
        return all_words

    def get_word_features(self,wordlist):
        wordlist = nltk.FreqDist(wordlist)
        word_features = wordlist.keys()
        return word_features


    # To create a classifier, we need to decide what features are relevant. 
    # To do that, we first need a feature extractor. 
    # The one we are going to use returns a dictionary indicating what words are contained in the input passed. 
    # Here, the input is the tweet. We use the word features list defined above along with the input 
    # to create the dictionary

    def extract_features(self,document):
        document_words = set(document)
        features = {}
        for word in self.word_features:
            features['contains(%s)' % word] = (word in document_words)
        return features


    def entrenaClasificador(self,train_tweets):
        # With our feature extractor, we can apply the features to our classifier using the method apply_features. 
        # We pass the feature extractor along with the tweets list defined above.
        training_set = nltk.classify.apply_features(self.extract_features, train_tweets)
        # Now that we have our training set, we can train our classifier.
        return nltk.NaiveBayesClassifier.train(training_set)

    def clasifica(self,clasificador, tweet):
        return clasificador.classify(self.extract_features(tweet.split()))

    def cambiaSentimiento(self,tweets, tweetConNuevoSentimiento, sentimiento):
        """Cambiando un tweet de sentimiento
        tweetConNuevoSentimiento = 'RT @elpais_opinion: No se equivoquen, la campa\xf1a electoral es una 
                                    extirpaci\xf3n de los recuerdos. David Trueba explica por qu\xe9 https://t.co/u\u2026' """

        tweetConNuevoSentimiento = preprocesador.preprocesaTweet(tweetConNuevoSentimiento, sentimiento)
        for index,tweet in enumerate(tweets):
            if tweets[index][0] == tweetConNuevoSentimiento[0]:
                del tweets[index]
                tweets.append(tweetConNuevoSentimiento)
        return tweets


Overwriting clasificadorBayesiano.py

In [ ]:
tweetConNuevoSentimiento = """RT @elpais_opinion: No se equivoquen, la campa\xf1a electoral es una 
                                extirpaci\xf3n de los recuerdos. David Trueba explica por qu\xe9 https://t.co/u\u2026"""
nuevosTweets = cambiaSentimiento(tweets_elpais_procesados, tweetConNuevoSentimiento, 'negative')
print(nuevosTweets)

In [7]:
import clasificadorBayesiano

train_tweets=[(['love', 'this', 'car'], 'positive'), (['this', 'view', 'amazing'], 'positive'), 
              (['feel', 'great', 'this', 'morning'], 'positive'), (['excited', 'about', 'the', 'concert'], 'positive'), 
              (['best', 'friend'], 'positive'), (['not', 'like', 'this', 'car'], 'negative'), 
              (['this', 'view', 'horrible'], 'negative'), (['feel', 'tired', 'this', 'morning'], 'negative'), 
              (['not', 'looking', 'forward', 'the', 'concert'], 'negative'), (['enemy'], 'negative')]

test_tweets = [(['feel', 'happy', 'this', 'morning'], 'positive'),
               (['larry', 'friend'], 'positive'),
               (['not', 'like', 'that', 'man'], 'negative'),
               (['house', 'not', 'great'], 'negative'),
               (['your', 'song', 'annoying'], 'negative')]
    
# Los train_tweets iniciales son para coger las palabras sobre las que va calcular las probabilidades
cl = clasificadorBayesiano.Clasificador(train_tweets)  

# Los train_tweets siguientes son para entrenar al clasificador
clasificador = cl.entrenaClasificador(train_tweets)

#print clasificador.show_most_informative_features(32)

tweet = 'Larry is my friend'
print cl.clasifica(clasificador, tweet)

#Llega mas informacion
train_tweets.extend(test_tweets)

clasificador = cl.entrenaClasificador(train_tweets)

#print clasificador.show_most_informative_features(32)

tweet = 'Larry is my friend'
print cl.clasifica(clasificador, tweet)


positive
positive

In [8]:
import preprocesadorTweets as preprocesador
import clasificadorBayesiano

#-----------------------------------------------------------------------------------------------------
# -------------------------- Preparando y entrenando inicialmente al clasificador --------------------
#-----------------------------------------------------------------------------------------------------
periodicos = ['elmundoes', 'el_pais', 'abc_es','larazon_es',
                  'eldiarioes','publico_es','20m']

periodicosEntrenamiento = ['elmundoes', 'el_pais', 'abc_es','eldiarioes','20m']

tweetsBase = preprocesador.leeArchivoTweets("periodicos_4horas.json")
tweetsEntrenamiento = preprocesador.leeArchivoTweets("periodicos.json")

tweetsBaseProcesados = []
for periodico in periodicos:
    tweetsBaseProcesados.extend(preprocesador.preprocesaTweets(tweetsBase,periodico))
    
tweetsEntrenamientoProcesados = []
for periodico in periodicosEntrenamiento:
    tweetsEntrenamientoProcesados.extend(preprocesador.preprocesaTweets(tweetsEntrenamiento,periodico))
    
#print(len(tweetsBaseProcesados))
#print(tweetsBaseProcesados)
#print(len(tweetsEntrenamientoProcesados))
#print(tweetsEntrenamientoProcesados)

# Los tweets iniciales son para coger las palabras sobre las que va calcular las probabilidades
cl = clasificadorBayesiano.Clasificador(tweetsBaseProcesados)  

# Los tweetsEntrenamientoProcesados siguientes son para entrenar al clasificador
clasificador = cl.entrenaClasificador(tweetsEntrenamientoProcesados)

#print clasificador.show_most_informative_features(10)

#-----------------------------------------------------------------------------------------------------
# --------Al llegar nuevos tweets los clasifico y vuelvo a entrenar al clasificador con ellos --------
#-----------------------------------------------------------------------------------------------------

tweetsLlegados = []

nuevoTweet_texto = "Al menos siete muertos en un ataque de los talibanes a un aeropuerto de Afganistan"
nuevoTweet_sentimiento = cl.clasifica(clasificador, nuevoTweet_texto)

nuevoTweetProcesado = preprocesador.preprocesaTweet(nuevoTweet_texto, nuevoTweet_sentimiento)

print("clasificacion inicial: " + str(nuevoTweetProcesado))

#Vuelvo a entrenar al clasificador con el nuevo tweet que ha entrado junto con todos los anteriores
tweetsEntrenamientoProcesados.append(nuevoTweetProcesado)
clasificador = cl.entrenaClasificador(tweetsEntrenamientoProcesados)

#print clasificador.show_most_informative_features(10)

#-----------------------------------------------------------------------------------------------------
# -------------------------- Cambio un tweet de sentimiento  --------------------
#-----------------------------------------------------------------------------------------------------

    
tweetsEntrenamientoProcesados = cl.cambiaSentimiento(tweetsEntrenamientoProcesados, 
                                "Al menos siete muertos en un ataque de los talibanes a un aeropuerto de Afganistan",
                                'positive')

clasificador = cl.entrenaClasificador(tweetsEntrenamientoProcesados)

#print clasificador.show_most_informative_features(10)

print("")
print("Cambio de sentimiento respecto al tweet " + str(tweetsEntrenamientoProcesados[-1]))
print("")
print ("clasificacion tras cambio sentimiento: %s " % cl.clasifica(clasificador, nuevoTweet_texto))


clasificacion inicial: (['menos', 'siete', 'muertos', 'ataque', 'talibanes', 'aeropuerto', 'afganistan'], 'negative')

Cambio de sentimiento respecto al tweet (['menos', 'siete', 'muertos', 'ataque', 'talibanes', 'aeropuerto', 'afganistan'], 'positive')

clasificacion tras cambio sentimiento: positive 

In [43]:
%%writefile clasificadorPrensa.py

import preprocesadorTweets as preprocesador
import clasificadorBayesiano

class ClasificadorPrensa():
    def __init__(self):
        self.periodicos = ['elmundoes', 'el_pais', 'abc_es','larazon_es',
                              'eldiarioes','publico_es','20m']

        self.periodicosEntrenamiento = ['elmundoes', 'el_pais', 'abc_es','eldiarioes','20m']
        self.tweetsBaseProcesados = self.extraeTweets("periodicos_4horas.json", self.periodicos)
        self.tweetsEntrenamientoProcesados  = self.extraeTweets("periodicos.json", self.periodicosEntrenamiento)
        self.clasificador = clasificadorBayesiano.Clasificador(self.tweetsBaseProcesados)  
        self.clasificadorEntrenado = self.clasificador.entrenaClasificador(self.tweetsEntrenamientoProcesados)
        
    def extraeTweets(self, archivo, periodicos):     
        tweets = preprocesador.leeArchivoTweets(archivo)
        tweetsProcesados = []
        for periodico in periodicos:
            tweetsProcesados.extend(preprocesador.preprocesaTweets(tweets,periodico))
        return tweetsProcesados
    
    def clasificaNuevoTweet(self, nuevoTweet_texto):
        """ Da la clasificacion del Tweet segun el clasificador bayesiano ('positive' o 'negative') 
        y vuelve a entrenar al clasificador con esta informacion"""
        nuevoTweet_sentimiento = self.clasificador.clasifica(self.clasificadorEntrenado, nuevoTweet_texto)
        nuevoTweetProcesado = preprocesador.preprocesaTweet(nuevoTweet_texto, nuevoTweet_sentimiento)
        #Vuelvo a entrenar al clasificador con el nuevo tweet que ha entrado junto con todos los anteriores
        self.tweetsEntrenamientoProcesados.append(nuevoTweetProcesado)
        self.clasificadorEntrenado = self.clasificador.entrenaClasificador(self.tweetsEntrenamientoProcesados)
        return nuevoTweet_sentimiento
        
    def cambiaSentimiento(self, tweet_texto,nuevoSentimiento):
        self.tweetsEntrenamientoProcesados = self.clasificador.cambiaSentimiento(self.tweetsEntrenamientoProcesados, 
                                                                                    tweet_texto, nuevoSentimiento)

        self.clasificadorEntrenado = self.clasificador.entrenaClasificador(self.tweetsEntrenamientoProcesados)


Overwriting clasificadorPrensa.py

In [38]:
c = ClasificadorPrensa()
#print(c.tweetsEntrenamientoProcesados)
#c.clasificadorEntrenado.show_most_informative_features(10)
print c.clasificaNuevoTweet("Al menos siete muertos en un ataque de los talibanes a un aeropuerto de Afganistan")
#c.clasificadorEntrenado.show_most_informative_features(10)
c.cambiaSentimiento("Al menos siete muertos en un ataque de los talibanes a un aeropuerto de Afganistan",'positive')
print("Tras el cambio de sentimiento " + str(c.tweetsEntrenamientoProcesados[-1]))


negative
Tras el cambio de sentimiento (['menos', 'siete', 'muertos', 'ataque', 'talibanes', 'aeropuerto', 'afganistan'], 'positive')

In [ ]: