Hito 3: Smartphone Apps Analysis


Damián Árquez, Benjamín Hurtado, Matias Villegas

CC5206 - Introducción a la minería de datos

Dataset

En este proyecto se utilizan dos datasets en conjunto. El primero consta de dos tablas que contienen información sobre el Google Play Store. El segundo contiene una sola tabla con información del App Store de Apple. A continuación se describe la información contenida en cada archivo/tabla:

  • googleplaystore.csv: Se encuentra información detallada de las más de 10.000 aplicaciones disponibles en Google Play Store.
  • googleplaystore_user_reviews.csv: Se encuentra información de más de 64.000 reviews, realizadas por usuarios a las aplicaciones presentes en Google Play Store.
  • AppleStore.cvs. Se encuentra información de 10.000 aplicaciones disponibles en App Store de IOS.

El dataset de Google Play Store se encuentra en el siguiente link (Kaggle): Dataset Google Play Store.

El dataset de App Store se encuentra en el siguiente link (Kaggle): Dataset AppStore

Motivación

Hoy en día los smartphones son uno de los dispositivos más utilizados. Debido a esto, existe un gran mercado de aplicaciones para éstos. Las aplicaciones permiten al dispositivo extender su propósito en distintas categorías (entretenimiento, ocio, educación, productividad, etc...). Dada la alta demanda de smartphones, se genera genera un interés en el desarrollo de software para celulares inteligentes por sobre otras plataformas.

Se desea encontrar una caracterización del mercado actual de aplicaciones a partir de Google Play Store, la tienda de aplicaciones por defecto en el sistema Android, a partir de la información de las aplicaciones en sí, además de sus reseñas.

Esta información se presta de utilidad al momento de diseñar aplicaciones nuevas, comprendiendo qué características pueden hacer una aplicación más exitosa que otra, entendiendo por éxito una alta cantidad de descargas y buenas reseñas, esto para construir una aplicación exitosa se necesita hacer un análisis de datos para obtener así cuál sería la categoría y tipo de aplicación que se debe crear.

Descripción del Problema

El objetivo de este proyecto es caracterizar aplicaciones según los distintos atributos que poseen, buscando relaciones interesantes que puedan existir entre ellos como, por ejemplo, la relación entre si una aplicacion es paga y la evaluación promedio de sus reviews.

En esta misma linea, surgen las siguientes preguntas, que probablemente el análisis de este dataset podrá responder:

Hito 1

  • ¿Influye en la distribución de ratings si una aplicación es paga o no?
  • ¿Qué categoría de aplicaciones tiene un rating más alto?
  • ¿Qué categoría es la más descargada?
  • ¿Qué categoría posee la mayor cantidad de aplicaciones?
  • ¿La categoría con mayor descargas coincide con un buen rating?
  • ¿Cuál es la distribución de peso de aplicación con respecto a la cantidad de descargas?
  • ¿Existen aplicaciones con un rating muy bajo pero muchas descargas?

Hito 2

  • ¿Se puede predecir la categoría que tendrá una aplicación en base solo a su nombre?
  • ¿Se puede predecir el Content Rating (Para todo público, adolescentes, etc) a partir del nombre y categoría de la app? *¿Se puede predecir el comportamiento si una aplicación de una tienda es publicado en otra?
  • ¿Qué tan bien se podrá predecir la cantidad de reviews de una app a partir del resto de sus datos?

Hito 3

  • ¿Es posible predecir la categoría de una aplicación con mayor accuracy a través de la descripción?

Primer análisis

In [0]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns; sns.set(style="ticks", color_codes=True)
import requests

Para mantener los datos disponibles, se subieron a Amazon S3.

Cargamos los datos de las apps.

In [0]:
apps = pd.read_csv('https://s3.us-east-2.amazonaws.com/cc5206-2018-2/googleplaystore.csv')
reviews = pd.read_csv('https://s3.us-east-2.amazonaws.com/cc5206-2018-2/googleplaystore_user_reviews.csv')
In [3]:
apps.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10840 entries, 0 to 10839
Data columns (total 13 columns):
App               10840 non-null object
Category          10840 non-null object
Rating            9366 non-null float64
Reviews           10840 non-null int64
Size              10840 non-null object
Installs          10840 non-null object
Type              10839 non-null object
Price             10840 non-null object
Content Rating    10840 non-null object
Genres            10840 non-null object
Last Updated      10840 non-null object
Current Ver       10832 non-null object
Android Ver       10838 non-null object
dtypes: float64(1), int64(1), object(11)
memory usage: 1.1+ MB
In [4]:
apps.head()
Out[4]:
App Category Rating Reviews Size Installs Type Price Content Rating Genres Last Updated Current Ver Android Ver
0 Photo Editor & Candy Camera & Grid & ScrapBook ART_AND_DESIGN 4.1 159 19M 10,000+ Free 0 Everyone Art & Design January 7, 2018 1.0.0 4.0.3 and up
1 Coloring book moana ART_AND_DESIGN 3.9 967 14M 500,000+ Free 0 Everyone Art & Design;Pretend Play January 15, 2018 2.0.0 4.0.3 and up
2 U Launcher Lite – FREE Live Cool Themes, Hide ... ART_AND_DESIGN 4.7 87510 8.7M 5,000,000+ Free 0 Everyone Art & Design August 1, 2018 1.2.4 4.0.3 and up
3 Sketch - Draw & Paint ART_AND_DESIGN 4.5 215644 25M 50,000,000+ Free 0 Teen Art & Design June 8, 2018 Varies with device 4.2 and up
4 Pixel Draw - Number Art Coloring Book ART_AND_DESIGN 4.3 967 2.8M 100,000+ Free 0 Everyone Art & Design;Creativity June 20, 2018 1.1 4.4 and up
In [5]:
reviews.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 64295 entries, 0 to 64294
Data columns (total 5 columns):
App                       64295 non-null object
Translated_Review         37427 non-null object
Sentiment                 37432 non-null object
Sentiment_Polarity        37432 non-null float64
Sentiment_Subjectivity    37432 non-null float64
dtypes: float64(2), object(3)
memory usage: 2.5+ MB
In [6]:
reviews.head()
Out[6]:
App Translated_Review Sentiment Sentiment_Polarity Sentiment_Subjectivity
0 10 Best Foods for You I like eat delicious food. That's I'm cooking ... Positive 1.00 0.533333
1 10 Best Foods for You This help eating healthy exercise regular basis Positive 0.25 0.288462
2 10 Best Foods for You NaN NaN NaN NaN
3 10 Best Foods for You Works great especially going grocery store Positive 0.40 0.875000
4 10 Best Foods for You Best idea us Positive 1.00 0.300000

En un primer encuentro con los datos no usaremos las columnas Size, Last updated, Current ver y Android ver del dataset apps.

In [0]:
apps = pd.DataFrame(apps, columns=['App', 'Category', 'Rating', 'Reviews', 'Installs', 'Type', 'Price', 'Content Rating', 'Genres'])

Además, notamos que los atributos Installs y Price del dataset Apps son del tipo object y sería mejor para el análisis que fueran valores numéricos. Para esto se realizará un 'Data Cleaning'

Data Cleaning

El Data Cleaning se realizará en base a lo descrito en Kaggle..

Quitar espacios en nombre de atributos

In [0]:
apps.columns = apps.columns.str.replace(' ', '_')

Installs

Se procede a ver los valores únicos en la columna Installs.

In [9]:
apps.Installs.value_counts()
Out[9]:
1,000,000+        1579
10,000,000+       1252
100,000+          1169
10,000+           1054
1,000+             907
5,000,000+         752
100+               719
500,000+           539
50,000+            479
5,000+             477
100,000,000+       409
10+                386
500+               330
50,000,000+        289
50+                205
5+                  82
500,000,000+        72
1+                  67
1,000,000,000+      58
0+                  14
0                    1
Name: Installs, dtype: int64

Hay que eliminar los "+" y las comas.

In [10]:
apps.Installs = apps.Installs.apply(lambda x: x.strip('+'))
apps.Installs = apps.Installs.apply(lambda x: x.replace(',',''))
apps.Installs = pd.to_numeric(apps.Installs)

apps.Installs.describe()
Out[10]:
count    1.084000e+04
mean     1.546434e+07
std      8.502936e+07
min      0.000000e+00
25%      1.000000e+03
50%      1.000000e+05
75%      5.000000e+06
max      1.000000e+09
Name: Installs, dtype: float64

Price

Se verá los valores únicos de Price con el fin de buscar anormalidades:

In [11]:
apps.Price.unique()
Out[11]:
array(['0', '$4.99', '$3.99', '$6.99', '$1.49', '$2.99', '$7.99', '$5.99',
       '$3.49', '$1.99', '$9.99', '$7.49', '$0.99', '$9.00', '$5.49',
       '$10.00', '$24.99', '$11.99', '$79.99', '$16.99', '$14.99',
       '$1.00', '$29.99', '$12.99', '$2.49', '$10.99', '$1.50', '$19.99',
       '$15.99', '$33.99', '$74.99', '$39.99', '$3.95', '$4.49', '$1.70',
       '$8.99', '$2.00', '$3.88', '$25.99', '$399.99', '$17.99',
       '$400.00', '$3.02', '$1.76', '$4.84', '$4.77', '$1.61', '$2.50',
       '$1.59', '$6.49', '$1.29', '$5.00', '$13.99', '$299.99', '$379.99',
       '$37.99', '$18.99', '$389.99', '$19.90', '$8.49', '$1.75',
       '$14.00', '$4.85', '$46.99', '$109.99', '$154.99', '$3.08',
       '$2.59', '$4.80', '$1.96', '$19.40', '$3.90', '$4.59', '$15.46',
       '$3.04', '$4.29', '$2.60', '$3.28', '$4.60', '$28.99', '$2.95',
       '$2.90', '$1.97', '$200.00', '$89.99', '$2.56', '$30.99', '$3.61',
       '$394.99', '$1.26', '$1.20', '$1.04'], dtype=object)

Se debe eliminar el signo "$" y luego convertir la columna a numeric.

In [12]:
apps.Price = apps.Price.apply(lambda p: p.strip('$'))
apps.Price = pd.to_numeric(apps.Price)

apps.Price.describe()
apps.Content_Rating.describe()
Out[12]:
count        10840
unique           6
top       Everyone
freq          8714
Name: Content_Rating, dtype: object

Con esto finaliza la limpieza de datos.

Exploración

Categorías

¿Qué categorías son las más proliferantes en el mercado de aplicaciones de Google? El siguiente gráfico es el único obtenido por un kernel de Kaggle.

In [13]:
apps.Category.value_counts().plot(kind='barh', figsize=(12,8))
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f57386d5978>

Se puede observar que, con una gran ventaja, las aplicaciones clasificadas como familiares son las que más se encuentran en Google Play Store que es en donde se concentran las aplicaciones orientadas para niños pequeños, seguidas por los juegos y aplicaciones con finalidades de herramienta.

Precio de la aplicación

¿Son las aplicaciones pagadas mejor evaluadas? Para responder esto se realiza un gráfico de frecuencia vs el rating.

In [14]:
freeapps = apps[apps.Type == 'Free']
paidapps = apps[apps.Type != 'Free']

g = sns.kdeplot(freeapps.Rating, color="Red", shade = True, label='Gratis')
g = sns.kdeplot(paidapps.Rating, color="Blue", shade = True, label='Pagadas')
g.set_xlabel("Rating")
g.set_ylabel("Frecuencia")
plt.title('Distribución de Rating de Apps pagadas vs gratis',size = 20)
/usr/local/lib/python3.6/dist-packages/statsmodels/nonparametric/kde.py:454: RuntimeWarning: invalid value encountered in greater
  X = X[np.logical_and(X>clip[0], X<clip[1])] # won't work for two columns.
/usr/local/lib/python3.6/dist-packages/statsmodels/nonparametric/kde.py:454: RuntimeWarning: invalid value encountered in less
  X = X[np.logical_and(X>clip[0], X<clip[1])] # won't work for two columns.
Out[14]:
Text(0.5,1,'Distribución de Rating de Apps pagadas vs gratis')
In [15]:
freeapps.Rating.describe()
Out[15]:
count    8719.000000
mean        4.186203
std         0.512338
min         1.000000
25%         4.000000
50%         4.300000
75%         4.500000
max         5.000000
Name: Rating, dtype: float64
In [16]:
paidapps.Rating.describe()
Out[16]:
count    647.000000
mean       4.266615
std        0.547523
min        1.000000
25%        4.100000
50%        4.400000
75%        4.600000
max        5.000000
Name: Rating, dtype: float64

Lo primero que se puede notar es que la distribución de ratings está cargada sobre el 4. Con esto notamos que la mayoría de la gente suele entregar puntuaciones muy altas, lo que puede deberse a que si un usuario disgusta de una aplicación la elimina en vez de puntuarla negativamente.

Se puede apreciar, tanto en el gráfico como en el cálculo estadístico del promedio, que efectivamente existe un ligero desplazamiento de las distribuciones que evidencia que las aplicaciones pagadas tienen, en promedio, mejor valoración que las aplicaciones gratuitas. Esto puede deberse a que como existe un pago de por medio, el desarrollador es una aplicación paga tiene mayor esmero en su desarrollo, además de que probablemente estas aplicaciones no posean publicidad. Se propone comprobar a partir del dataset de reviews que la publicidad aporta a una calificación negativa.

Categoría mejor evaluada

Se realiza un gráfico de barras de error en el cual se consideran los promedios de evaluación por cada categoría considerando su desviación estándar.

In [17]:
ratings_by_cat = apps.groupby('Category')['Rating'] \
  .agg({'mean_rating': 'mean', 'std_rating': 'std'}) 
plt.errorbar(ratings_by_cat.index.tolist(), ratings_by_cat.mean_rating, \
             yerr=ratings_by_cat.std_rating, fmt='o', \
             color='red', ecolor='blue')
plt.xticks(rotation='vertical')
plt.show()
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:1: FutureWarning: using a dict on a Series for aggregation
is deprecated and will be removed in a future version
  """Entry point for launching an IPython kernel.

El resultado de este experimento muestra que la categoría con mejor evaluación es eventos seguida de educación. Para la categoría con peor calificación se tiene que es citas, lo que puede deberse a que las aplicaciones de este estilo suelen producir decepción o malas experiencias en los usuarios.

Instalaciones por categoría

¿Cuál es la categoría con mayor cantidad de descargas? El siguiente gráfico muestra las descargas totales por cada categoría.

In [18]:
installs_by_cat = apps.groupby('Category')['Installs']\
  .agg({'Installs': 'sum'})
plt.bar(installs_by_cat.index.tolist(), installs_by_cat.Installs)
plt.xticks(rotation='vertical')
plt.title('Cantidad de descargas por Categoria')
plt.ylabel('Cantidad de descargas')
plt.show()
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:1: FutureWarning: using a dict on a Series for aggregation
is deprecated and will be removed in a future version
  """Entry point for launching an IPython kernel.

Es importante destacar que este gráfico posee las descargas totales por categoría y NO un promedio. Aquí se muestra que las categorías con más descargas son comunicación y juegos. Ya vimos que la categoría familia posee la mayor cantidad de aplicaciones por sobre el resto de categorías, por lo que se nota que a pesar de la cantidad de aplicaciones presentes en esta categoría, no recibe descargas. Con respecto a nuestro análisis: familia no es una buena categoría para la cual enfocar una aplicación exitosa.

Instalaciones vs cantidad de reviews

¿Existen aplicaciones con un rating muy bajo pero muchas descargas? El gráfico siguiente muestra la cantidad de reviews según la cantidad de instalaciones.

In [19]:
plt.plot(apps.Installs, apps.Reviews, linestyle="", marker="o")
Out[19]:
[<matplotlib.lines.Line2D at 0x7f5735b097f0>]

El gráfico anterior no nos permite obtener información, debido a que en Play Store la cantidad de descargas se definen como más de 100, más de 1000, más de 1.000.000, en vez de entregar cantidades exactas. Debido al crecimiento exponencial de los intervalos no se puede concluir.

Experimentos

Predicción de la categoría según su nombre.

¿Se puede predecir la categoría que tendrá una aplicación en base solo a su nombre?

La idea de esto es poder crear una aplicación cuyo nombre sea consistente con su categoría tomando en cuenta el mercado de aplicaciones ya existente para los usuarios.

A simple vista no se podría predecir, debido a que para el clasificador dos nombres serán distintos y no parecidos. Para esto se utilizó una herramienta de vectorización de sklearn, con cuyos valores fuimos capaces de realizar la clasificación.

Para la clasificación se decidió utilizar un árbol de decisiones. Se optó por este clasificador debido a que entre las otras opciones se tiene KNN, el cual no se ajusta bien a las necesidades del análisis ya que no nos interesa una vecindad de nombres, y cross-validation crece demasiado rápido en costo según cantidad de datos en el dataframe.

In [20]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
from sklearn.feature_extraction.text import TfidfVectorizer

# Extraemos las columnas para entrenar el clasificador
app_names = apps.App
app_categories = apps.Category

# Vectorizamos el texto de los nombres de las apps
tfidf = TfidfVectorizer(sublinear_tf=True, min_df=5, norm='l2', encoding='latin-1', ngram_range=(1, 2), stop_words=['englis', 'spanish'])
features = tfidf.fit_transform(app_names).toarray()

# Codificamos a valores numericos las categorias
le_cat = LabelEncoder()
cat_classes = le_cat.fit_transform(app_categories)

# Dividimos el dataset en test+train
N_train, N_test, cat_train, cat_test = train_test_split(features, cat_classes, test_size=.3, random_state=37, stratify=cat_classes)

clf = DecisionTreeClassifier()
clf.fit(N_train, cat_train)   

cat_pred = clf.predict(N_test)
print("Accuracy en test set:", accuracy_score(cat_test, cat_pred))
print(classification_report(cat_test, cat_pred, target_names=le_cat.classes_))
Accuracy en test set: 0.46863468634686345
                     precision    recall  f1-score   support

     ART_AND_DESIGN       0.30      0.32      0.31        19
  AUTO_AND_VEHICLES       0.38      0.24      0.29        25
             BEAUTY       0.50      0.12      0.20        16
BOOKS_AND_REFERENCE       0.28      0.26      0.27        69
           BUSINESS       0.32      0.30      0.31       138
             COMICS       0.50      0.39      0.44        18
      COMMUNICATION       0.51      0.56      0.53       116
             DATING       0.75      0.70      0.73        70
          EDUCATION       0.64      0.49      0.55        47
      ENTERTAINMENT       0.59      0.49      0.54        45
             EVENTS       0.50      0.32      0.39        19
             FAMILY       0.35      0.58      0.43       592
            FINANCE       0.63      0.53      0.57       110
     FOOD_AND_DRINK       0.61      0.45      0.52        38
               GAME       0.52      0.49      0.50       343
 HEALTH_AND_FITNESS       0.65      0.59      0.62       102
     HOUSE_AND_HOME       0.45      0.35      0.39        26
 LIBRARIES_AND_DEMO       0.50      0.35      0.41        26
          LIFESTYLE       0.21      0.13      0.16       115
MAPS_AND_NAVIGATION       0.42      0.34      0.38        41
            MEDICAL       0.57      0.42      0.49       139
 NEWS_AND_MAGAZINES       0.51      0.47      0.49        85
          PARENTING       0.50      0.17      0.25        18
    PERSONALIZATION       0.83      0.75      0.79       118
        PHOTOGRAPHY       0.73      0.63      0.68       101
       PRODUCTIVITY       0.41      0.35      0.38       127
           SHOPPING       0.65      0.47      0.55        78
             SOCIAL       0.55      0.35      0.43        88
             SPORTS       0.76      0.60      0.67       115
              TOOLS       0.34      0.31      0.32       253
   TRAVEL_AND_LOCAL       0.54      0.49      0.51        77
      VIDEO_PLAYERS       0.37      0.30      0.33        53
            WEATHER       0.90      0.72      0.80        25

          micro avg       0.47      0.47      0.47      3252
          macro avg       0.52      0.43      0.46      3252
       weighted avg       0.49      0.47      0.47      3252

Destacamos que las aplicaciónes de clima (WEATHER) poseen una precisión de 1. Probablemente debido a lo sencillo que resulta nombrarlas (time, tiempo, etc). Por otro lado, las 3 categorías que observamos tenían mayor cantidad de aplicaciones tienen una baja precisión a la hora de ser clasificadas. Esto debido a que la alta cantidad de aplicaciones produce muchos nombres distintos, haciendo dificil la tarea de clasificarlos.

Con estos resultados poseemos un accuracy de aproximadamente 47%. Con esto se propone un modelo que a partir de un nombre entregue 3 posibles categorías a la que ese nombre pertenecería, a modo de tener una aplicación con un nombre consistente con su categoría.

Aplicaciones de Play Store en App Store

¿Se puede predecir el comportamiento si una aplicación de una tienda es publicado en otra? Para esto se utilizó un dataset del App Store

In [21]:
apple = pd.read_csv('https://s3.us-east-2.amazonaws.com/cc5206-2018-2/AppleStore.csv')
apple.head()
Out[21]:
id track_name size_bytes currency price rating_count_tot rating_count_ver user_rating user_rating_ver ver cont_rating prime_genre sup_devices.num ipadSc_urls.num lang.num vpp_lic game_enab
0 281656475 PAC-MAN Premium 100788224 USD 3.99 21292 26 4.0 4.5 6.3.5 4+ Games 38 5 10 1 0
1 281796108 Evernote - stay organized 158578688 USD 0.00 161065 26 4.0 3.5 8.2.2 4+ Productivity 37 5 23 1 0
2 281940292 WeatherBug - Local Weather, Radar, Maps, Alerts 100524032 USD 0.00 188583 2822 3.5 4.5 5.0.0 4+ Weather 37 5 3 1 0
3 282614216 eBay: Best App to Buy, Sell, Save! Online Shop... 128512000 USD 0.00 262241 649 4.0 4.5 5.10.0 12+ Shopping 37 5 9 1 0
4 282935706 Bible 92774400 USD 0.00 985920 5320 4.5 5.0 7.5.1 4+ Reference 37 5 45 1 0
In [22]:
apple.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 11100 entries, 0 to 11099
Data columns (total 17 columns):
id                  11100 non-null int64
track_name          7197 non-null object
size_bytes          11100 non-null int64
currency            7197 non-null object
price               11100 non-null float64
rating_count_tot    11100 non-null int64
rating_count_ver    11100 non-null int64
user_rating         11100 non-null float64
user_rating_ver     11100 non-null float64
ver                 7197 non-null object
cont_rating         7197 non-null object
prime_genre         7197 non-null object
sup_devices.num     11100 non-null int64
ipadSc_urls.num     11100 non-null int64
lang.num            11100 non-null int64
vpp_lic             11100 non-null int64
game_enab           11100 non-null int64
dtypes: float64(3), int64(9), object(5)
memory usage: 1.4+ MB

Con este dataframe tenemos columnas en común con el de Play Store, por lo que podemos identificar las aplicaciones en común para realizar clasificación. A continuación se calcula cuántas aplicaciones están presentes en ambos dataframes juzgando sólo por su nombre.

In [23]:
#Cantidad de aplicaciones con el mismo nombre en ambos dataframes
s1 = apps.App.str.lower().str.strip()
s2 = apple.track_name.str.lower().str.strip()
print("Hay",s1[s1.isin(s2)].size,"aplicaciones en común.")
Hay 569 aplicaciones en común.

Observamos que hay 569 aplicaciones que coinciden en ambos dataframes. Este número es demasiado pequeño para realizar una clasificación ya que corresponde a aproximadamente el 5% del tamaño de lo que deseamos clasificar, además de que en estos valores se presentan outliers, como serían las aplicaciones de whatsapp, facebook, instagram, entre otras, resultando en una cantidad útil de datos aún menor.

Debido a esto el estudio no se puede realizar, ya que los resultados no serían representativos.

Futuros desafíos

Resolviendo varias dudas que generó el hito 1, se propone cambiar el estudio de Google Play Store a un análisis de tiendas de aplicaciones en general, obteniendo relaciones a partir de resultados de los distintos dataframes por separado.

Además de esto, se buscará algún método para intentar predecir el rating de una aplicación con otra herramienta, ya que en esta entrega no se pudo lograr con clasificadores.

Se propone utilizar el dataset de Reviews para buscar palabras clave que hagan que una review corresponda a un sentimiento en particular, de forma de saber qué comportamientos de aplicación producen un sentimiento negativo/positivo según los usuarios.

Hito 3

Feedback del hito 2

Luego de las presentaciones del hito 2, se recibió un feedback por escrito de algunos compañeros además de comentarios tanto por parte de otros estudiantes como del cuerpo docente.

El feedback por escrito no fue de gran ayuda, ya que la mayoría apelaba ante la presentación en sí antes que el proyecto presentado, y los pocos comentarios que iban dirigidos a la información sobre el proyecto, eran de información que sí entregamos, pero durante las presentaciones no se dieron a entender tan claramente.

Los comentarios no escritos que recibimos de nuestros compañeros y del cuerpo docente dieron a lugar a lo que se realizó en el hito 3. Durante el hito 2 se nos llamó la atención sobre el hecho de utilizar un clasificador sobre el nombre de aplicaciones, ya que "Tu app" y "TuApp" podrían ser clasificadas de manera distinta, cuando cláramente aluden a lo mismo. Esto es debido a la naturaleza de los nombres de las aplicaciones y cómo se vectoriza el texto para poder trabajar.

Además, notamos que nuestro dataset poseía poca información, por lo que, a pesar de escapar un poco de lo visto en el curso, se realizó Scraping para poder agregar la descripción de las aplicaciones a éste.

Con esto, se resolvería un poco la problemática de la poca información acerca de una misma App, además de que en la descripción, al ser texto más extenso y escrito, usualmente, de manera formal, es posible realizar una clasificación esperando un mejor resultado que con los nombres.

Indice de descargas por cantidad de aplicaciones, en cada Categoria

En vista de los gráficos del hito anterior, se nota que puede ser interesante medir la cantidad de descargas promedio de cada categoría, dado que, por ejemplo, a pesar de que la categoría Familia lidera en cuanto a cantidad de aplicaciones, pero no se destaca en el gráfico de cantidad de descargas totales.

El siguiente gráfico muestra la cantidad de descargas promedio de cada categoría.

In [24]:
apps_by_cat = apps.groupby('Category').count()['App']
inst_by_apps = installs_by_cat.div(apps_by_cat, axis=0)
plt.plot(inst_by_apps, ls=':', marker='o')
plt.xticks(rotation='vertical')
plt.show()

Se puede notar que esta métrica se acerca más a lo que se podría esperar, pues la categoría que lidera (por bastante) es Communication la que contiene aplicaciones como Whatsapp, Telegram, Line, etc. De hecho, ahora se puede apreciar que Family no se destaca tanto como se esperaría.

Clasificación por descripciones

En vista de la limitada información de la que se dispone. Se utiliza un scraper en Python que permite extender el Dataset para ahora disponer de las descripciones de cada aplicación. El scraper puede encontrarse en este link

Utilizando esta nueva información, se procede a clasificar las descripciones con el fin de predecir la categoría de una App en base a ésta.

Se debe recordar que en el hito anterior se había clasificado con los nombres de las aplicaciones. Se espera que esta clasificación sea más efectiva pues las descripciones suelen presentar más información sobre el contenido de su aplicación que el nombre.

In [25]:
import pandas as pd
df = pd.read_csv('https://users.dcc.uchile.cl/~bhurtado/resources/googleplaystore.csv')
df.head()
Out[25]:
App Category Rating Reviews Size Installs Type Price Content_Rating Genres Last_Updated Current_Ver Android_Ver Package_id Description
0 Photo Editor & Candy Camera & Grid & ScrapBook ART_AND_DESIGN 4.1 159 19M 10,000+ Free 0 Everyone Art & Design January 7, 2018 1.0.0 4.0.3 and up com.lyrebirdstudio.montagenscolagem With more than 150 million installs, Photo Col...
1 Coloring book moana ART_AND_DESIGN 3.9 967 14M 500,000+ Free 0 Everyone Art & Design;Pretend Play January 15, 2018 2.0.0 4.0.3 and up com.kidscoloringbook.android Welcome to coloring books and pages can be int...
2 U Launcher Lite – FREE Live Cool Themes, Hide ... ART_AND_DESIGN 4.7 87510 8.7M 5,000,000+ Free 0 Everyone Art & Design August 1, 2018 1.2.4 4.0.3 and up com.phone.launcher.lite 🔥U Launcher Lite is a lite version of U Lau...
3 Sketch - Draw & Paint ART_AND_DESIGN 4.5 215644 25M 50,000,000+ Free 0 Teen Art & Design June 8, 2018 Varies with device 4.2 and up com.sonymobile.sketch This is the official Sketch app from Sony. Ske...
4 Paper flowers instructions ART_AND_DESIGN 4.4 167 5.6M 50,000+ Free 0 Everyone Art & Design March 26, 2017 1.0 2.3 and up com.PaperFlowerInstructions.CCStudio Papercraft flowers are crafted using simple ma...

Para este análisis se considerará solamente la categoría (Category) y la descripción (Description).

In [26]:
col = ['Category', 'Description']
df = df[col]
# Hay una app con descripción nula
df = df[pd.notnull(df['Description'])]

df.columns = col

# Representación numérica a las categorias
df['category_id'] = df['Category'].factorize()[0]
category_id_df = df[['Category', 'category_id']].drop_duplicates().sort_values('category_id')
category_to_id = dict(category_id_df.values)
id_to_category = dict(category_id_df[['category_id', 'Category']].values)

df.head()
Out[26]:
Category Description category_id
0 ART_AND_DESIGN With more than 150 million installs, Photo Col... 0
1 ART_AND_DESIGN Welcome to coloring books and pages can be int... 0
2 ART_AND_DESIGN 🔥U Launcher Lite is a lite version of U Lau... 0
3 ART_AND_DESIGN This is the official Sketch app from Sony. Ske... 0
4 ART_AND_DESIGN Papercraft flowers are crafted using simple ma... 0

En la siguiente figura se puede apreciar que las clases están bastante debalanceadas. Esto al momento de poner a prueba el clasificador puede significar un problema, ya que la categoría FAMILY tiene muchos más datos con los que se puede producir un sesgo. Sin embargo, se decide seguir adelante sin hacer oversampling o subsampling, pues se considera que el dataset representa el esquema real de aplicaciones, no se debe perturbar a quitar o agregar aplicaciones.

In [27]:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
df.groupby('Category').Description.count().plot.bar(ylim=0)
plt.show()

Para procesar el texto de las descripciones se utiliza la herramienta TfidfVectorizer de python, con el fin de vectorizar el texto con la técnica Term-frecuency inverse document frecuency.

In [28]:
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf = TfidfVectorizer(sublinear_tf=True, min_df=5, norm='l2', encoding='latin-1', ngram_range=(1, 2), stop_words='english')

features = tfidf.fit_transform(df.Description).toarray()
labels = df.category_id
features.shape
Out[28]:
(10304, 55316)

A continuación, se utilizarán 5 modelos distintos de clasificación, entre ellos, el Decision Tree dada la alta familiarización con su funcionamiento, con el fin de considerarlo como una solución base en cuanto a comparación de métricas.

Esta comparación se realizará en función del accuracy de los modelos, los cuales se podrán apreciar en el siguiente gráfico de boxplot que muestra los resultados de los 5 modelos.

In [0]:
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import LinearSVC
from sklearn.naive_bayes import MultinomialNB

from sklearn.model_selection import cross_val_score

models = [
    RandomForestClassifier(n_estimators=200, max_depth=3, random_state=0),
    LinearSVC(),
    MultinomialNB(),
    LogisticRegression(random_state=0),
    DecisionTreeClassifier(),
]
CV = 5
cv_df = pd.DataFrame(index=range(CV * len(models)))
entries = []
for model in models:
  model_name = model.__class__.__name__
  accuracies = cross_val_score(model, features, labels, scoring='accuracy', cv=CV)
  for fold_idx, accuracy in enumerate(accuracies):
    entries.append((model_name, fold_idx, accuracy))
cv_df = pd.DataFrame(entries, columns=['model_name', 'fold_idx', 'accuracy'])

import seaborn as sns

sns.boxplot(x='model_name', y='accuracy', data=cv_df)
sns.stripplot(x='model_name', y='accuracy', data=cv_df, 
              size=8, jitter=True, edgecolor="gray", linewidth=2)
plt.show()

Definitivamente Random Forest no se ajusta bien a los datos. Además, se aprecia que LinearSVC y Logistic Regression presentan mejor accuracy's mejor que el considerado como base, Decision Tree.

Dado que LinearSVC es el mejor en cuanto a esta métrica se procede a calcular la matriz de confusión:

In [29]:
from sklearn.svm import LinearSVC
import seaborn as sns
model = LinearSVC()

X_train, X_test, y_train, y_test, indices_train, indices_test = train_test_split(features, labels, df.index, test_size=0.33, random_state=0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

from sklearn.metrics import confusion_matrix

conf_mat = confusion_matrix(y_test, y_pred)
fig, ax = plt.subplots(figsize=(10,10))
sns.heatmap(conf_mat, annot=True, fmt='d',
            xticklabels=category_id_df.Category.values, yticklabels=category_id_df.Category.values)
plt.ylabel('Real')
plt.xlabel('Predicción')
plt.show()

Se puede apreciar que, en efecto, la matriz de confusión tiene una diagonal bastante definida, lo que habla bien de las predicciones que realiza el modelo LinearSVC.

Algo interesante de notar es el cuadrado que se genera en el centro de la matriz en que se observa que el modelo confunde, con mayor tasa, las categorías de Juegos y Familia, lo que puede explicarse con la gran correlación que existe entre estas dos categorías, pues en FAMILY se encuentran muchos de los juegos enfoncados para niños pequeños.

A continuación se presenta, de cierta manera, otra métrica que muestra los unigramas y bigramas, utilizados por el modelo, más comunes en el análisis.

In [30]:
model.fit(features, labels)

N = 2
for Category, category_id in sorted(category_to_id.items()):
  indices = np.argsort(model.coef_[category_id])
  feature_names = np.array(tfidf.get_feature_names())[indices]
  unigrams = [v for v in reversed(feature_names) if len(v.split(' ')) == 1][:N]
  bigrams = [v for v in reversed(feature_names) if len(v.split(' ')) == 2][:N]
  print("# '{}':".format(Category))
  print("  . Top unigramas:\n    . {}".format('\n    . '.join(unigrams)))
  print("  . Top bigramas:\n    . {}".format('\n    . '.join(bigrams)))
# 'ART_AND_DESIGN':
  . Top unigramas:
    . art
    . brushes
  . Top bigramas:
    . coloring book
    . craft ideas
# 'AUTO_AND_VEHICLES':
  . Top unigramas:
    . car
    . controle
  . Top bigramas:
    . used car
    . vehicle history
# 'BEAUTY':
  . Top unigramas:
    . whitening
    . hairstyles
  . Top bigramas:
    . makeup tutorial
    . teeth whitening
# 'BOOKS_AND_REFERENCE':
  . Top unigramas:
    . bible
    . books
  . Top bigramas:
    . ag phd
    . 10 000
# 'BUSINESS':
  . Top unigramas:
    . business
    . customers
  . Top bigramas:
    . images notes
    . control access
# 'COMICS':
  . Top unigramas:
    . comics
    . manga
  . Top bigramas:
    . new updates
    . comics app
# 'COMMUNICATION':
  . Top unigramas:
    . calls
    . mallorca
  . Top bigramas:
    . world talking
    . bluetooth device
# 'DATING':
  . Top unigramas:
    . dating
    . chat
  . Top bigramas:
    . dating app
    . video chat
# 'EDUCATION':
  . Top unigramas:
    . lessons
    . cã
  . Top bigramas:
    . day best
    . best companion
# 'ENTERTAINMENT':
  . Top unigramas:
    . tv
    . theatres
  . Top bigramas:
    . network live
    . movie trailers
# 'EVENTS':
  . Top unigramas:
    . event
    . tickets
  . Top bigramas:
    . happy birthday
    . app 2018
# 'FAMILY':
  . Top unigramas:
    . students
    . simulator
  . Top bigramas:
    . mini games
    . live tv
# 'FINANCE':
  . Top unigramas:
    . banking
    . financial
  . Top bigramas:
    . mobile banking
    . credit score
# 'FOOD_AND_DRINK':
  . Top unigramas:
    . recipes
    . food
  . Top bigramas:
    . browse menu
    . order pay
# 'GAME':
  . Top unigramas:
    . game
    . racing
  . Top bigramas:
    . free ğÿ
    . unlock new
# 'HEALTH_AND_FITNESS':
  . Top unigramas:
    . fitness
    . workout
  . Top bigramas:
    . personal training
    . access sign
# 'HOUSE_AND_HOME':
  . Top unigramas:
    . rent
    . apartment
  . Top bigramas:
    . home improvement
    . app dedicated
# 'LIBRARIES_AND_DEMO':
  . Top unigramas:
    . biz
    . ti
  . Top bigramas:
    . easy understand
    . version new
# 'LIFESTYLE':
  . Top unigramas:
    . church
    . sermons
  . Top bigramas:
    . connect engage
    . contact email
# 'MAPS_AND_NAVIGATION':
  . Top unigramas:
    . taxi
    . transit
  . Top bigramas:
    . ev charging
    . app used
# 'MEDICAL':
  . Top unigramas:
    . anatomy
    . patient
  . Top bigramas:
    . diagnosis treatment
    . computed tomography
# 'NEWS_AND_MAGAZINES':
  . Top unigramas:
    . news
    . newspaper
  . Top bigramas:
    . breaking news
    . news app
# 'PARENTING':
  . Top unigramas:
    . baby
    . kidsâ
  . Top bigramas:
    . offline video
    . audio pronunciation
# 'PERSONALIZATION':
  . Top unigramas:
    . theme
    . wallpaper
  . Top bigramas:
    . live wallpaper
    . good news
# 'PHOTOGRAPHY':
  . Top unigramas:
    . camera
    . photo
  . Top bigramas:
    . camera application
    . camera app
# 'PRODUCTIVITY':
  . Top unigramas:
    . notes
    . edit
  . Top bigramas:
    . note taking
    . able manage
# 'SHOPPING':
  . Top unigramas:
    . shopping
    . shop
  . Top bigramas:
    . shopping experience
    . easy ğÿ
# 'SOCIAL':
  . Top unigramas:
    . posts
    . people
  . Top bigramas:
    . way download
    . app helps
# 'SPORTS':
  . Top unigramas:
    . football
    . league
  . Top bigramas:
    . app control
    . quad core
# 'TOOLS':
  . Top unigramas:
    . vpn
    . keyboard
  . Top bigramas:
    . app used
    . app compatible
# 'TRAVEL_AND_LOCAL':
  . Top unigramas:
    . travel
    . flight
  . Top bigramas:
    . sur le
    . able access
# 'VIDEO_PLAYERS':
  . Top unigramas:
    . video
    . player
  . Top bigramas:
    . video editor
    . video player
# 'WEATHER':
  . Top unigramas:
    . weather
    . radar
  . Top bigramas:
    . weather app
    . accurate weather

Se puede apreciar que los resultados se presentan con bastante sentido en relación a sus categorías, lo cual da aún más confibilidad sobre el modelo.

In [31]:
from sklearn import metrics
print(metrics.classification_report(y_test, y_pred, target_names=df['Category'].unique()))
                     precision    recall  f1-score   support

     ART_AND_DESIGN       0.47      0.38      0.42        21
  AUTO_AND_VEHICLES       0.40      0.17      0.24        23
             BEAUTY       0.40      0.29      0.33         7
BOOKS_AND_REFERENCE       0.73      0.59      0.65        78
           BUSINESS       0.53      0.59      0.56       152
             COMICS       1.00      0.37      0.54        19
      COMMUNICATION       0.63      0.54      0.58       120
             DATING       0.81      0.77      0.79        74
          EDUCATION       0.68      0.57      0.62        40
      ENTERTAINMENT       0.56      0.63      0.59        43
             EVENTS       0.30      0.17      0.21        18
            FINANCE       0.75      0.82      0.78       122
     FOOD_AND_DRINK       0.73      0.71      0.72        45
 HEALTH_AND_FITNESS       0.77      0.72      0.74       100
     HOUSE_AND_HOME       0.64      0.64      0.64        25
 LIBRARIES_AND_DEMO       0.62      0.24      0.34        21
          LIFESTYLE       0.44      0.27      0.33       119
               GAME       0.69      0.75      0.72       365
             FAMILY       0.57      0.65      0.60       636
            MEDICAL       0.80      0.83      0.81       146
             SOCIAL       0.64      0.49      0.56        87
           SHOPPING       0.73      0.77      0.75        87
        PHOTOGRAPHY       0.73      0.78      0.75       112
             SPORTS       0.77      0.69      0.73       120
   TRAVEL_AND_LOCAL       0.61      0.62      0.61        78
              TOOLS       0.49      0.64      0.56       263
    PERSONALIZATION       0.72      0.76      0.74       116
       PRODUCTIVITY       0.54      0.41      0.47       121
          PARENTING       0.56      0.29      0.38        17
            WEATHER       0.81      0.71      0.76        31
      VIDEO_PLAYERS       0.69      0.48      0.57        64
 NEWS_AND_MAGAZINES       0.69      0.65      0.67        71
MAPS_AND_NAVIGATION       0.60      0.43      0.50        60

          micro avg       0.64      0.64      0.64      3401
          macro avg       0.64      0.56      0.58      3401
       weighted avg       0.64      0.64      0.63      3401

Finalmente, con respecto a los distintos modelos de clasificación utilizados en el análisis de descripciones, claramente LinearSVC y Logistic Regression son los que mejor se adaptan a los datos siendo el primero, el que tiene mejores métricas de accuracy.

Conclusiones

Haciendo un recuento global del proyecto, es claro para el grupo que éste no representa un éxito en lo absoluto, pues si bien se logró aplicar conocimientos adquiridos en el curso, no fue posible aplicar muchas otras de las herramientas y análisis aprendidos.

Esto se debe, mayoritariamente, a que al no conocer, en un principio, las herramientas que se aprenderían, la elección del dataset no fue óptima en función de la posiblidad de usar éstas. En muchos sentidos el dataset no aportó la información necesaria para realizar hartas ideas que surgieron, y la cantidad de columnas limitó la posibilidad de usar clasificadores más interesantes.

Definitivamente, una de los cosas que se puede mejorar en un futuro, en otros proyectos, es que se tendrá una visión mucho más realista de qué es posible y qué no, incluso al inicio de éste.