Predictor de calidad de respuestas en Stackover Flow

Integrantes:

  • Gabriela Mendoza
  • Vicente Illanes
  • Pablo Arancibia

Introducción

En el contexto del curso CC5206 Introducción a la Minería de Datos, se realiza un proyecto sobre una base de datos escogida por los integrantes del grupo, con el objetivo de aplicar herramientas del curso sobre los datos y predecir su comportamiento. La base de datos escogida por el grupo es "stack overflow dataset", la cuál posee información de preguntas y respuestas realizadas por usuarios del sitio web Stack Overflow, el cuál es utilizado por la comunidad de desarrolladores informáticos, básicamente un usuario hace pública una pregunta, esperando recibir respuestas de otros usuarios. Además, los usuarios pueden calificar preguntas y respuestas según su calidad, asignandole un puntaje o score.

Problemática central

La mayoría de las veces que uno publica una preguntas en Stack Overflow es porque se necesita un respuesta confiable y a corto plazo. Probablemente se esté estancado en un bugg y se necesite seguir avanzando con la tarea de programación. Stack overflow es uno de los foros de programación con más flujo de preguntas/respuestas, en general es un foro bastante popular. Sin embargo, no está ausente de preguntas abandonadas, o threads que nunca fueron cerrados, ya que todas las respuestas que recibió el cuestionador fueron inútiles al momento de resolver su problema. Basado en esta problemática se decide proveer herramientas que intenten predecir el Score de una respuesta asociada a una pregunta en particular, a partir de los atributos que estas posean.

Hipótesis

  • El score de una respuesta es directamente proporcional a la cantidad de “comentarios positivos” e inversamente proporcional a la cantidad de “comentarios negativos”

  • El historial de un usuario es un buen atributo para la creación de un clasificador de calidad. Una respuesta tiene mejor calidad (mayor score) si proviene de un usuario con mejor reputación.

  • El DataSet es poco privado respecto a la información entregada y los comentarios expuestos por los usuarios

Preprocesamiento y Limpieza de los Datos

En primer lugar, se realiza un análisis de los datos para seleccionar las tablas que son importantes para poder predecir el Score de una respuesta. Estas tablas son Post_Answers, Post_Questions, Users y Comments. Sobre estas tablas se seleccionan los atributos más relevantes para lograr un buen clasificador, como se muestra:

Post_Answers

  • score
  • body
  • creation_date

Post_Questions

  • body
  • title
  • creation_date
  • last_activity_date

Users

  • reputation
  • up_votes
  • down_votes
  • age
  • location

Comments

  • text

Para reducir el número de datos, se filtran las tablas Comments, Anwers y Questions, seleccionando sólo los años 2018 y 2019. Las tablas de bigquery se filtran, y se seleccionan sólo los atributos de interés y sus llaves foráneas, para luego guardarlas en un archivo .csv con la librería pandas.

Finalmente se combinan estas cuatro tablas en una sola, realizando los siguientes cruces entre llaves foráneas:

  • Post_Answers(parent_id)= Post_Question(id)
  • Post_Answers(id)= Comments(post_id)
  • Post_Answers(owner_user_id)= Users(id)

Atributos Creados

Antes de trabajar con los atributos es necesario pasarlos a un formato numérico, es decir pasar de datos crudos como fechas y texto, a datos que servirán para clasificar. Es importante recalcar que los métodos actuales de clasificación sólo trabajan sobre escalares, por lo cual se deben crear nuevos atributos. Se crean los siguientes atributos para las siguientes Tablas:

Comments

  • positive
  • negative

Corresponde al puntaje atribuido al texto en cuanto a qué tan positivo o negativo es, usando un conjunto de palabras base llamado Sentiment Lexicon "sentiwordnet", del autor Andrea Esuli. Para obtener este puntaje se recorre el comentario como una bolsa de palabras, y por cada palabra se verifica si ocurre o no en el diccionarios de palabras. Este diccionario es del tipo llave: par, donde el primer valor del par corresponde al puntaje positivo de la palabra, mientras que el segundo valor corresponde al puntaje negativo. Query_Answers

  • length_answer

Query_Question

  • length_question

También, se crearon atributos que almacenan el largo en caracteres del texto de la respuesta y pregunta (length_answer / length_question), eliminando cuidadosamente el código html que contienen para no considerarlo en el valor.

Otra información importante es el transcurso de tiempo, en milisegundos entre la publicación de la pregunta y la respuesta, la cuál se almacena en el atributo delta_time.

Clases creadas

Query_Answers

  • class

Se crean tres clases para clasificar el puntaje, usando un método similar a Equal Frequency. Es decir, se intenta dividir el dominio de puntajes en clases de la misma frecuencia. Debido a que el dominio es discreto esta división es parcialmente posible. Se realiza la siguiente división:

  • Score<0 : Outlier, Score = 0 : Class 0, Score=1: Class 1, Score>1: Class 2

Principales resultados hito anterior, y cambios para este hito

En el hito anterior se utilizaron los datos del año 2018, solamente. Obteniendo así un total de 1000 datos por calse, mediante el uso de subsampling y oversamling:

  • 167 outliers
  • 1960 datos clase 0
  • 962 datos clase 1
  • 523 datos clase 2

Los experimentos corridos sobre este set de datos arrojaron un f1-score del orden de 0.6 puntos. Lo cual no era muy bueno.También se corrieron experimentos de clustering, sobre los cuales no se vio ninguna agrupación concluyente. (ver anexo de clustering)

A difetrencia del hito anterior en éste se realizan experimentos sobre una cantidad de datos 10 veces mayor. esperando así ver mejoras en la calidad del calsificador.

Cargando los datos ya unidos a los comentarios

In [1]:
import pandas as pd
print('cargando Joined-Table')
joinT = pd.read_csv('Joined_table_3.tsv', sep='\t')
joinT.head()
cargando Joined-Table
Out[1]:
Unnamed: 0 Unnamed: 0.1 nada user_id reputation_user likes_user dislikes_user age_user location_user id_answer ... body_answer score_answer id_question title_question body_question creation_date_question last_activity_date_question score_discrete positive negative
0 6237 666275 666275 9291038 58 5 0 NaN Belgium 54713299 ... <p>according to the <a href="https://experienc... 2 54710302 Max Columns(20+) in Table - SAP UI5 <p>SAP Fiori suggests to go for Responsive Tab... 2019-02-15 13:23:27.300000+00:00 2019-02-15 16:23:41.780000+00:00 1 0.750 1.25
1 17520 1195960 1195960 8283848 14069 536 105 NaN Kochi, Kerala, India 49028261 ... <p><s>add this in <code>settings.py</code><br>... 2 49028160 I cannot get django debug toolbar to appear in... <p>Its a very simple small project located her... 2018-02-28 11:09:26.733000+00:00 2018-02-28 13:33:18.113000+00:00 1 0.250 0.25
2 7399 2355474 2355474 8485403 993 49 10 NaN NaN 48444827 ... <p>I believe one <code>lapply</code> should be... 2 48444448 Efficient way to select variables from a list ... <p>I have a list of three data frames as follo... 2018-01-25 13:47:16.473000+00:00 2018-01-25 14:07:16.177000+00:00 1 1.625 1.25
3 44830 547112 547112 7509065 5827 364 515 NaN NaN 49594507 ... <p><code>C:\Users\faisal\library</code> is not... 2 49594492 selenium.common.exceptions.WebDriverException:... <p>I want to use the chrome webdriver to conne... 2018-04-01 03:22:49.650000+00:00 2018-04-04 15:41:03.167000+00:00 1 0.375 0.00
4 49587 280828 280828 8599868 1455 45 5 NaN NaN 53390549 ... <p>That's because you haven't installed Androi... 2 53390096 react native stuck in configuring react-native... <p>I did this :</p>\n\n<ol>\n<li><code>react n... 2018-11-20 09:40:31.537000+00:00 2018-12-17 08:05:06.977000+00:00 1 2.750 0.25

5 rows × 22 columns

Creación de los artibutos escalares

El siguiente código crea los atributos mencionados anteriormente, y además obtiene 2 atributos adicionales, positive_realtive y negative_relative, que corresponden al cuociente entre positivos y el total, y negativos y el total.

In [2]:
import time
from datetime import datetime
import re

joinT['delta_time'] = joinT['creation_date_answer'].map(lambda x: time.mktime(datetime.strptime(x[:19], '%Y-%m-%d %H:%M:%S').timetuple())) - joinT['creation_date_question'].map(lambda x: time.mktime(datetime.strptime(x[:19], '%Y-%m-%d %H:%M:%S').timetuple()))
joinT =joinT[["reputation_user", "likes_user", "dislikes_user", "location_user", "body_answer", "body_question", "delta_time"
             ,"positive", "negative", "score_discrete" ]]
TAG_RE = re.compile(r'<[^>]+>') #sirve para remover tags HTML de las respuestas
joinT['length_answer'] = joinT['body_answer'].map(lambda x: len(TAG_RE.sub('', x)))
joinT['length_question'] = joinT['body_question'].map(lambda x: len(TAG_RE.sub('', x)))
joinT.head()
Out[2]:
reputation_user likes_user dislikes_user location_user body_answer body_question delta_time positive negative score_discrete length_answer length_question
0 58 5 0 Belgium <p>according to the <a href="https://experienc... <p>SAP Fiori suggests to go for Responsive Tab... 10814.0 0.750 1.25 1 446 238
1 14069 536 105 Kochi, Kerala, India <p><s>add this in <code>settings.py</code><br>... <p>Its a very simple small project located her... 348.0 0.250 0.25 1 931 280
2 993 49 10 NaN <p>I believe one <code>lapply</code> should be... <p>I have a list of three data frames as follo... 1200.0 1.625 1.25 1 269 2138
3 5827 364 515 NaN <p><code>C:\Users\faisal\library</code> is not... <p>I want to use the chrome webdriver to conne... 211.0 0.375 0.00 1 112 1911
4 1455 45 5 NaN <p>That's because you haven't installed Androi... <p>I did this :</p>\n\n<ol>\n<li><code>react n... 1451.0 2.750 0.25 1 308 205

Creación de atributo location

In [3]:
import pandas as pd
import math
countries = pd.read_csv('country-codes.csv', sep=',')
import numpy as np

countries['English short name lower case'] =  countries['English short name lower case'].str.lower()
joinT['location_user']=joinT['location_user'].str.lower()
countries = countries[['English short name lower case', 'Numeric code']]
dic=countries.set_index("English short name lower case").T.to_dict('list')
joinT['location']= np.nan
for index, row in joinT.iterrows():
    key = row['location_user']
    if type(key)== str:
        if key in dic:
            joinT.at[index,'location']= dic.get(key)[0]
        elif 'usa' in key or 'united states' in key:
            joinT.at[index,'location']= 840.0
        else:
            arr=dic.keys()
            for k in arr:
                if k in key or key in k:
                    joinT.at[index,'location']= dic.get(k)[0]
                    break
    else:
        joinT.at[index,'location']= 0

joinT['location'].fillna(0, inplace = True)
joinT.head()
Out[3]:
reputation_user likes_user dislikes_user location_user body_answer body_question delta_time positive negative score_discrete length_answer length_question location
0 58 5 0 belgium <p>according to the <a href="https://experienc... <p>SAP Fiori suggests to go for Responsive Tab... 10814.0 0.750 1.25 1 446 238 56.0
1 14069 536 105 kochi, kerala, india <p><s>add this in <code>settings.py</code><br>... <p>Its a very simple small project located her... 348.0 0.250 0.25 1 931 280 356.0
2 993 49 10 NaN <p>I believe one <code>lapply</code> should be... <p>I have a list of three data frames as follo... 1200.0 1.625 1.25 1 269 2138 0.0
3 5827 364 515 NaN <p><code>C:\Users\faisal\library</code> is not... <p>I want to use the chrome webdriver to conne... 211.0 0.375 0.00 1 112 1911 0.0
4 1455 45 5 NaN <p>That's because you haven't installed Androi... <p>I did this :</p>\n\n<ol>\n<li><code>react n... 1451.0 2.750 0.25 1 308 205 0.0

Atributos Descartados

  • age_user: la edad del usuario no se usa ya que sólo un 20% de los datos tiene el registro de este atributo.
  • last_activity_question: se descarta debido a que no se considera relevante para clasificar la calidad de una resupesta.

Oversampling y Subsampling

Debido a la poca equitatividad entre cantidad de datos por clase. Se realizo subsamplig de la clase 0 y oversampling de la clase 2, para obtener 3 clases con alrededor de 1000 datos.

In [4]:
filteredT= joinT[joinT["score_discrete"]!=-1]
print(filteredT['score_discrete'].value_counts())
# subsampling sobre la clase 2 y de clase 1
data_subsampled = pd.concat([filteredT[filteredT["score_discrete"]==2].sample(10000),filteredT[filteredT["score_discrete"]==1].sample(10000),filteredT[filteredT["score_discrete"]==0]])
print("Data subsampled on class '1' & '2'")
print(data_subsampled['score_discrete'].value_counts())
1    14408
2    14025
0     9994
Name: score_discrete, dtype: int64
Data subsampled on class '1' & '2'
2    10000
1    10000
0     9994
Name: score_discrete, dtype: int64

Experimentos

En primer lugar se quiere entender cómo se realaciona cada uno de los atributos entre ellos. Por lo que se grafica un diagrama de densidades, en el cual es posible ver las ocurrencias de dos atributos en un gráfico. (El siguiente gráfico sólo muestra una porción de los atributos)

In [5]:
import matplotlib.pyplot as plt
import seaborn as sns; sns.set(style="ticks", color_codes=True)

data = data_subsampled
g = sns.pairplot(data[["score_discrete", "reputation_user", "likes_user", "dislikes_user","delta_time", "positive", "negative","length_question","location"]]) # Parametro kind="reg" agrega una recta
plt.show()
Out[5]:

En el gráfico anterior es posible ver que los atributos más densos entre sí, corresponden a aquellos relacionados con cualidades del usuario, por ejemplo, los pares: {reputation, likes}, {reputation, dislikes}. Por otro lado, aquellos atributos que mejor se agrupan con la clase a predecir, a partir del gráfico anterior, son : reputation, dislikes y delta_time.

Experimentos de clasificación

Para probar el poder de clasificación de los atributos seleccionados se realizan experimentos de clasificación con los métodos Decision Tree y K-means evaluados bajo cross-validation. Se realizan los siguientes experimentos:

  • Predicción del score para cada atributo individual (En anexos)
  • Predicción del score para pares de atributos (No incluido en el informe )
  • Predicción del score con los mejores 3 atributos
  • Predicción con todos los atributos (n)
  • Ensayo y error para n-1 antributos
In [6]:
# IMPORTACIONES
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import cross_validate
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.dummy import DummyClassifier
from sklearn.svm import SVC  # support vector machine classifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB  # naive bayes
from sklearn.neighbors import KNeighborsClassifier
import warnings
warnings.simplefilter("ignore")
In [7]:
# DEFINICION DE FUNCIONES

# CROSS-VALIDATION
def cross_val(func, _X, _Y, name):
    scoring = ['precision_macro', 'recall_macro', 'accuracy', 'f1_macro']
    cv_results = cross_validate(func, _X, _Y, cv = 7, scoring = scoring, return_train_score= True)
    print('f1_score '+ name +':', np.mean(cv_results['test_f1_macro']))
    print("------------------\n")
    return np.mean(cv_results['test_f1_macro'])

#DECISION TREE
def DT(_X, _Y):
    X_train, X_test, y_train, y_test = train_test_split(_X, _Y, test_size=.33, random_state=37, stratify=_Y)
    clf = DecisionTreeClassifier()
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    cross_val(clf, X, Y, 'DT')

#Naive Bayes
def NB(_X, _Y):
    X_train, X_test, y_train, y_test = train_test_split(_X, _Y, test_size=.33, random_state=37, stratify=_Y)
    clf = GaussianNB()
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    cross_val(clf, X, Y, 'NB')

#Dummy
def Dummy(_X, _Y):
    X_train, X_test, y_train, y_test = train_test_split(_X, _Y, test_size=.33, random_state=37, stratify=_Y)
    clf =  DummyClassifier(strategy='stratified')
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    cross_val(clf, X, Y, 'Dummy')


#K NEAREST NEIGHBOURS
def KNN(_X, _Y):
    X_train, X_test, y_train, y_test = train_test_split(_X, _Y, test_size=.33, random_state=37, stratify=_Y)
    K = 5# numero de vecinos
    knn = KNeighborsClassifier(n_neighbors=K)
    cross_val(knn, X,Y.values.ravel(),'KNN')

Comparando clasificadores

Predicción con todos los atributos

In [8]:
X = data_subsampled[["reputation_user", "likes_user", "dislikes_user","delta_time", "positive", "negative","length_answer", "length_question", "location"]]
Y = data_subsampled[["score_discrete"]]
#DECISION TREE
DT(X,Y)
# K NEAREST NEIGHBOURS
KNN(X,Y)
# NAIVE BAYES
NB(X,Y)
# Dummy
Dummy(X,Y)
f1_score DT: 0.3649638654456276
------------------

f1_score KNN: 0.36416758323682086
------------------

f1_score NB: 0.2645337751503522
------------------

f1_score Dummy: 0.33364491177598754
------------------

Predicción con 3 atributos

In [10]:
X = data_subsampled[["reputation_user", "likes_user", "dislikes_user"]]
#DECISION TREE
DT(X,Y)
# K NEAREST NEIGHBOURS
KNN(X,Y)
# NAIVE BAYES
NB(X,Y)
# Dummy
Dummy(X,Y)
f1_score DT: 0.3798129029201544
------------------

f1_score KNN: 0.37722471835714527
------------------

f1_score NB: 0.24196024807124078
------------------

f1_score Dummy: 0.3313718944575821
------------------

Experimentos de Privacidad (VER DOCUMENTO ANEXADO DE PRIVACIDAD)

Conclusiones

Con bajo número de datos (hito 2) el modelo predictivo generado alcanza un accuracy de 60%. Como son 3 clases, sin un clasificador el porcentaje de acierto es de 33%. Por lo tanto el clasificador es bastante mejor que "random", equivocandose 1 de cada 3 clasificaciones. Sin embargo, con un mayor número de datos no se logran clasificadores de calidad. Mientras que al aumentar el número de datos, el f1_scores disminuía considerablemente, alcanzando resultados cercanos a random con decision tree, y incluso peores con otros métodos. Por lo que se puede indicar que una base de datos más grande no asegura un mejor modelo de clasificación, dada la diversidad de los datos.

Tras realizar experimentos es posible ver que cierta combinación de atributos mejora la precisión del modelo de clasificación, por ejemplo {reputation, likes , dislikes}. Mientras que al incluirlos otros en el modelo, como similarity, pueden empeorar mucho la precisión. Descubrir cuales son los atributos que mejoran el desempeño de un modelo predictivo no es una tarea fácil y por lo general conlleva mucho trabajo explorativo y pruebas de ensayo-error.

De los resultados anteriores, es posible ver que el modelo que mejor se ajusta a los datos es Decision Tree, a pesar de que KNN es un modelo de mejor desempeño en general. Esto se puede debe a que los datos utilizados son un conjunto muy disperso, es decir existen muchos clusters de distintas densidades, por lo que no es posible clasificarlos bajo un mismo parámetro.

Con respecto a las hipótesis planteadas se observó que:

  • El historial de un usuario (reputación, likes) afecta positivamente en la predicción de puntaje del clasificador. Sin embargo, no se lograron clasificadores de calidad.
  • El dataset es poco privado respecto a las opiniones vertidas por lo cual es necesario la utilización de algún método de anonimización

ANEXOS

In [11]:
# DEFINICION DE FUNCIONES

# CROSS-VALIDATION
def cross_val(func, _X, _Y, name):
    scoring = ['precision_macro', 'recall_macro', 'accuracy', 'f1_macro']
    cv_results = cross_validate(func, _X, _Y, cv = 7, scoring = scoring, return_train_score= True)
    print('Promedio Precision ' + name +':', np.mean(cv_results['test_precision_macro']))
    print('Promedio Accucary '+ name +':', np.mean(cv_results['test_accuracy']))
    print('Recall ' + name +':', np.mean(cv_results['test_recall_macro']))
    print('f1_score '+ name +':', np.mean(cv_results['test_f1_macro']))

#DECISION TREE
def DT(_X, _Y):
    X_train, X_test, y_train, y_test = train_test_split(_X, _Y, test_size=.33, random_state=37, stratify=_Y)
    clf = DecisionTreeClassifier()
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    cross_val(clf, X, Y, 'DT')

#K NEAREST NEIGHBOURS
def KNN(_X, _Y):
    X_train, X_test, y_train, y_test = train_test_split(_X, _Y, test_size=.33, random_state=37, stratify=_Y)
    K = 5# numero de vecinos
    knn = KNeighborsClassifier(n_neighbors=K)
    cross_val(knn, X,Y.values.ravel(),'KNN')
In [12]:
#Predicción sobre cada atributo:
import warnings
warnings.filterwarnings("ignore")
attributes=["reputation_user", "likes_user", "dislikes_user","delta_time", "positive", "negative", "length_answer", "length_question", "location"]
for attr in attributes:
    X = data_subsampled[[attr]]
    print(attr)
    DT(X,Y)
    KNN(X,Y)
    NB(X,Y)
    Dummy(X,Y)
reputation_user
Promedio Precision DT: 0.38781187716529514
Promedio Accucary DT: 0.3950136083258299
Recall DT: 0.3950447263909705
f1_score DT: 0.3849676837425579
Promedio Precision KNN: 0.3805367148295489
Promedio Accucary KNN: 0.38687778219064456
Recall KNN: 0.3869081606134125
f1_score KNN: 0.3756062185813733
Promedio Precision NB: 0.3403154498317212
Promedio Accucary NB: 0.3576386548115184
Recall NB: 0.3577589621582888
f1_score NB: 0.23493298937872956
Promedio Precision Dummy: 0.3272970797885217
Promedio Accucary Dummy: 0.33436687799926884
Recall Dummy: 0.3333336222410254
f1_score Dummy: 0.3365227509269892
likes_user
Promedio Precision DT: 0.3849387367285635
Promedio Accucary DT: 0.3940799014116753
Recall DT: 0.3941297452806091
f1_score DT: 0.3738417651577781
Promedio Precision KNN: 0.3665172812012284
Promedio Accucary KNN: 0.372507668937304
Recall KNN: 0.3725395972629363
f1_score KNN: 0.3614737696286416
Promedio Precision NB: 0.3633531987020598
Promedio Accucary NB: 0.3603061631643555
Recall NB: 0.36042384805099037
f1_score NB: 0.24394772503027698
Promedio Precision Dummy: 0.33207693537634897
Promedio Accucary Dummy: 0.3358332755957109
Recall Dummy: 0.333868730782222
f1_score Dummy: 0.33579941332412255
dislikes_user
Promedio Precision DT: 0.3851935645998362
Promedio Accucary DT: 0.3918789353065076
Recall DT: 0.3919333002755952
f1_score DT: 0.36883179007211175
Promedio Precision KNN: 0.3428698215605638
Promedio Accucary KNN: 0.33826755943609577
Recall KNN: 0.3382647477065777
f1_score KNN: 0.33781954342800696
Promedio Precision NB: 0.38559590488575485
Promedio Accucary NB: 0.34786992344329715
Recall NB: 0.3479949925317193
f1_score NB: 0.212396005522866
Promedio Precision Dummy: 0.3333910924410743
Promedio Accucary Dummy: 0.33490082157749507
Recall Dummy: 0.3294988005528242
f1_score Dummy: 0.33510999349274945
delta_time
Promedio Precision DT: 0.33623571068051067
Promedio Accucary DT: 0.33596693465666677
Recall DT: 0.3359769273489057
f1_score DT: 0.33463222439315327
Promedio Precision KNN: 0.34155882080087835
Promedio Accucary KNN: 0.3403344074002446
Recall KNN: 0.3403571557058247
f1_score KNN: 0.3337169059899668
Promedio Precision NB: 0.22611052339523977
Promedio Accucary NB: 0.3333670924613002
Recall NB: 0.33333326332632424
f1_score NB: 0.17914484901754424
Promedio Precision Dummy: 0.33774411393098325
Promedio Accucary Dummy: 0.3288982488385715
Recall Dummy: 0.33686561445787266
f1_score Dummy: 0.3320895617829774
positive
Promedio Precision DT: 0.3307545652488092
Promedio Accucary DT: 0.32986627537988683
Recall DT: 0.32987583895829814
f1_score DT: 0.3268399078998408
Promedio Precision KNN: 0.33012398211333566
Promedio Accucary KNN: 0.33009967103871196
Recall KNN: 0.3301266620524862
f1_score KNN: 0.3135438180340325
Promedio Precision NB: 0.3288022039513162
Promedio Accucary NB: 0.3305662752350684
Recall NB: 0.3305269044209446
f1_score NB: 0.2720539268460093
Promedio Precision Dummy: 0.333663320487145
Promedio Accucary Dummy: 0.33633445642762216
Recall Dummy: 0.33593473272961283
f1_score Dummy: 0.33435285360261646
negative
Promedio Precision DT: 0.3384105874995507
Promedio Accucary DT: 0.34183542033245956
Recall DT: 0.34187227174635787
f1_score DT: 0.3239760198362495
Promedio Precision KNN: 0.3430595727439228
Promedio Accucary KNN: 0.3433354836124821
Recall KNN: 0.34335023305084117
f1_score KNN: 0.3375692805219658
Promedio Precision NB: 0.2956726408136919
Promedio Accucary NB: 0.34330208258142436
Recall NB: 0.34331144364049926
f1_score NB: 0.2757600909169145
Promedio Precision Dummy: 0.33141890051566675
Promedio Accucary Dummy: 0.33086557035662567
Recall Dummy: 0.33206565418017336
f1_score Dummy: 0.33309073297300545
length_answer
Promedio Precision DT: 0.34604628491761164
Promedio Accucary DT: 0.34626929832286013
Recall DT: 0.34629106719516783
f1_score DT: 0.34183631848414636
Promedio Precision KNN: 0.3415559683683392
Promedio Accucary KNN: 0.342068582209561
Recall KNN: 0.3420920937222315
f1_score KNN: 0.33472840862513414
Promedio Precision NB: 0.3717484750605341
Promedio Accucary NB: 0.3537702693750158
Recall NB: 0.35387321356796914
f1_score NB: 0.26726817436567646
Promedio Precision Dummy: 0.33794025893002305
Promedio Accucary Dummy: 0.3343337570506836
Recall Dummy: 0.3309327311116448
f1_score Dummy: 0.3321726853681678
length_question
Promedio Precision DT: 0.3315832205255952
Promedio Accucary DT: 0.3314324794612985
Recall DT: 0.3314421286180126
f1_score DT: 0.32981260468070206
Promedio Precision KNN: 0.33937118956030227
Promedio Accucary KNN: 0.34000069158844354
Recall KNN: 0.3400231046782953
f1_score KNN: 0.3331304506040062
Promedio Precision NB: 0.32273208850737095
Promedio Accucary NB: 0.3433686358569439
Recall NB: 0.3433227604754796
f1_score NB: 0.24023599924839914
Promedio Precision Dummy: 0.33688452513540235
Promedio Accucary Dummy: 0.3309994084587698
Recall Dummy: 0.3351675250665129
f1_score Dummy: 0.3339517759783366
location
Promedio Precision DT: 0.35704332905518604
Promedio Accucary DT: 0.36010412203186287
Recall DT: 0.3601610357699931
f1_score DT: 0.3201211508571255
Promedio Precision KNN: 0.3298364380728949
Promedio Accucary KNN: 0.32963332316408595
Recall KNN: 0.3296075364009149
f1_score KNN: 0.30748418158516166
Promedio Precision NB: 0.34152877757375133
Promedio Accucary NB: 0.3463362564501801
Recall NB: 0.34641600939604184
f1_score NB: 0.26546300393611844
Promedio Precision Dummy: 0.3304288385817431
Promedio Accucary Dummy: 0.33186787725811395
Recall Dummy: 0.32919919408577386
f1_score Dummy: 0.3302552579682619
In [13]:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# Make fake dataset
values_dummy = [0.329,0.336, 0.331,0.335, 0.333,0.333, 0.332, 0.325, 0.331]
values_dt = [0.388,0.375,0.370,0.341, 0.338, 0.329, 0.334, 0.321, 0.315]
values_nb = [0.235, 0.246, 0.212, 0.242, 0.179, 0.275, 0.227, 0.274, 0.269]
values_knn = [0.381, 0.364, 0.337, 0.333, 0.330, 0.324, 0.324, 0.335, 0.311]
bars = ('reputation','likes', 'dislikes', 'positive', 'delta time', 'length question', 'length answer', 'negative', 'location' )
y_pos = np.arange(len(bars))

ind = np.arange(len(values_dummy))
width = 0.80

fig, ax = plt.subplots(figsize=(12, 12))
rects1 = ax.barh(ind - width/2, values_dummy, width/4,
                label='Dummy')
rects2 = ax.barh(ind- width/4, values_dt, width/4,
                label='Decision Tree')
rects3 = ax.barh(ind, values_nb, width/4,
                label='Naive Bayes')
rects4 = ax.barh(ind + width/4, values_knn, width/4,
                label='KNN')

plt.yticks(y_pos, bars, fontsize=16)
 
plt.legend(loc= 0)

ax.set_xlabel('F1-score',fontsize=16)
ax.set_ylabel('Atributos', fontsize=16)
ax.set_title('F1-score para clasificadores por atributo', fontsize=20)
# Show graphic
plt.show()
Out[13]:
In [14]:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# Make fake dataset
values_dummy = [0.328, 0.333]
values_dt = [0.348, 0.371]
values_nb = [0.253, 0.268]
values_knn = [0.357, 0.368]
bars = ('reputation/likes/dislikes','todos los atributos')
y_pos = np.arange(len(bars))

ind = np.arange(len(values_dummy))
width = 0.80

fig, ax = plt.subplots(figsize=(12, 12))
rects1 = ax.barh(ind - width/2, values_dummy, width/4,
                label='Dummy')
rects2 = ax.barh(ind- width/4, values_dt, width/4,
                label='Decision Tree')
rects3 = ax.barh(ind, values_nb, width/4,
                label='Naive Bayes')
rects4 = ax.barh(ind + width/4, values_knn, width/4,
                label='KNN')

plt.yticks(y_pos, bars, fontsize=16)
 
plt.legend(loc= 0)

ax.set_xlabel('F1-score',fontsize=16)
ax.set_ylabel('Atributos', fontsize=16)
ax.set_title('F1-score para clasificadores por atributo', fontsize=20)
# Show graphic
plt.show()
Out[14]:
In [0]:
 
In [0]: