Integrantes: Cristóbal Miranda - Pablo Miranda - Ignacio Vallejos
PlayerUnknown’s Battlegrounds (PUBG) es un videojuego de batalla en lÃnea masivo, el juego es del género Battle Royale que combina los elementos de un videojuego de supervivencia con la jugabilidad de un último jugador en pie. En él hasta 100 jugadores saltan en paracaÃdas desde un avión a una isla, donde tienen que buscar armas y equipo para matar a otros jugadores y asà ser el último hombre en pie, dentro del mapa hay una área segura que disminuye a medida que avanza el tiempo, dirigiendo a los jugadores sobrevivientes a áreas más reducidas y forzando asà enfrentamientos.
En este trabajo se utilizara un dataset obtenido de kaggle.com, que es de una competencia abierta. Link a la competencia
En una partida de PUBG participan hasta 100 jugadores. Los jugadores pueden estar en equipos, los cuales son clasificados al final de la partida basados en cuantros equipos están vivos cuando éstos son eliminados. En cada partida los jugadores pueden recoger distintas armas, municiones y equipo, además pueden revivir compañeros derribados siempre y cuando éstos no mueran, pueden conducir vehÃculos, nadar, correr, disparar y experimentar todas las consecuencias de ésto, como por ejemplo ser atropellados y ser eliminados.
Cuál será la mejor estrategia para ganar en PUBG?. Evitar peleas y esperar hasta que queden pocos jugadores para pelear, o ir buscando peleas a diestra y siniestra durante toda la partida?
Predecir la posición en el marcador en la que quedará un jugador al final de la partida, según sus acciones en un juego de PUBG para partidas en solitario, en duos y en grupos, tanto para las variantes de 3era persona como de 1era persona.
Inicialmente se van a probar varios modelos predictivos utilizando el dataset de entrenamiento y se comparan según su precisión medida con el dataset de prueba. Utilizaremos gran mayorÃa de los métodos de clasificación vistos en clases.
Se utilizarán datos de más de 65.000 partidas distribuÃdos en 2 datasets, uno para entrenar y otro para testear.
Se importan librerÃas a utilizar
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
Se leen los datos
train_data = pd.read_csv("train_V2.csv")
train_data.head()
Una descripción de las columnas
train_data.info()
Para nuestro análisis no nos interesan las partidas no ranqueadas, por lo que seran eliminadas.
Se eliminan partidas no ranqueadas
initial_elements = train_data.shape[0]
relevant_matches = ['solo', 'solo-fpp', 'duo', 'duo-fpp', 'squad', 'squad-fpp']
non_custom = train_data[train_data.matchType.isin(relevant_matches)]
train_data = non_custom
non_custom = None
removed_elements = initial_elements - train_data.shape[0]
print("Se han quitado %d elementos cuyos modos de juego no nos sirven del dataset" % removed_elements)
Lo siguiente es un histograma sobre la cantidad de jugadores de cada equipo en todo el dataset de entrenamiento. Esto es para saber si la gente prefiere jugar sola o en equipo y notar que los grupos de 5 o más personas son de partidas personalizadas, no corresponden al modo normal de juego.
ggid = train_data.groupby(['groupId'])[['Id']].count()
ggid.loc[ggid['Id'] >= 8] = '8+'
plt.figure(figsize=(20,10))
sns.countplot(ggid['Id'].astype('str').sort_values())
plt.xlabel('Jugadores por grupo', fontsize=20)
plt.ylabel('Cantidad de grupos', fontsize=20)
plt.suptitle('Distribución de jugadores por equipos', fontsize=35)
plt.tick_params(labelsize=20)
plt.show()
Interpretación: Las partidas con más de 4 jugadores ocurren porque al crearse el grupo pueden entrar o salir jugadores y todos quedan registrados en el grupo, pero estos jugadores quedan sin datos en la partida (no matan a nadie, no recorren nada, etc), por lo que generan ruido. Dicho ésto eliminaremos todos los grupos que figuren con más de 4 jugadores, ésto no nos quita el ruido para las partidas de un jugador o 2 jugadores, pero para esos tipos de partidas se eliminaran los grupos correspondientes al momento de ocuparlos.
Se eliminan grupos que tengan más de 4 jugadores
ggid = train_data.groupby(['groupId'])[['Id']].count()
lesser_5_ggid = ggid.drop(ggid[ggid['Id'] >= 5].index)
ggid = None
selected_l5 = train_data.loc[train_data['groupId'].isin(lesser_5_ggid.index)]
removed_elements = train_data.shape[0] - selected_l5.shape[0]
lesser_5_ggid = None
train_data = selected_l5
print("Se han quitado %d elementos con 5 o más integrates por grupo del dataset" % removed_elements)
Lo siguiente es revisar la cantidad de grupos por juego. Esto para saber si la gente prefiere jugar sola o en grupo.
mmggid = train_data.groupby(['matchId'])[['groupId']].count()
mmggid = mmggid['groupId']
mmggid.loc[mmggid <= 10] = 10
mmggid.loc[(mmggid <= 20) & (mmggid > 10)] = 20
mmggid.loc[(mmggid <= 30) & (mmggid > 20)] = 30
mmggid.loc[(mmggid <= 40) & (mmggid > 30)] = 40
mmggid.loc[(mmggid <= 50) & (mmggid > 40)] = 50
mmggid.loc[(mmggid <= 60) & (mmggid > 50)] = 60
mmggid.loc[(mmggid <= 70) & (mmggid > 60)] = 70
mmggid.loc[(mmggid <= 80) & (mmggid > 70)] = 80
mmggid.loc[(mmggid <= 90) & (mmggid > 80)] = 90
mmggid.loc[(mmggid <= 100) & (mmggid > 90)] = 100
mmggid.loc[mmggid == 10] = '1-10'
mmggid.loc[mmggid == 20] = '11-20'
mmggid.loc[mmggid == 30] = '21-30'
mmggid.loc[mmggid == 40] = '31-40'
mmggid.loc[mmggid == 50] = '41-50'
mmggid.loc[mmggid == 60] = '51-60'
mmggid.loc[mmggid == 70] = '61-70'
mmggid.loc[mmggid == 80] = '71-80'
mmggid.loc[mmggid == 90] = '81-90'
mmggid.loc[mmggid == 100] = '91-100'
plt.figure(figsize=(20,10))
sns.countplot(mmggid.astype('str').sort_values())
plt.xlabel('equipos por juego ', fontsize=20)
plt.ylabel('Cantidad de partidas', fontsize=20)
plt.suptitle('Distribución de equipos por partida', fontsize=35)
plt.tick_params(labelsize=20)
plt.show()
mmggid = None
Interpretación: Se ve que la mayorÃa de los grupos se encuentran en el rango 91-100 que corresponde mayoritariamente a los equipos en partidas de un solo jugador
Lo siguiente que se verá es la distribución de eliminaciones de todo el dataset.
Se obtiene el promedio de eliminaciones
prom_kills = round(train_data['kills'].mean(), 4)
print(f'El jugador promedio mata {prom_kills} personas.')
Otra medición es el promedio de kills por partida.
prom_kills_game = round(train_data.groupby(['matchId'])[['kills']].mean()['kills'].mean(), 4)
print(f'El jugador promedio mata a {prom_kills_game} personas por partida.')
Por último en cuanto a la distribución de eliminaciones, se tiene la distribución de eliminaciones de todo el dataset dada por el siguiente histograma.
kills_data = train_data.copy()
kills_data.loc[kills_data['kills'] >= 6] = '6+'
plt.figure(figsize=(20,10))
sns.countplot(kills_data['kills'].astype('str').sort_values())
plt.xlabel('Eliminaciones', fontsize=20)
plt.ylabel('Cantidad de jugadores', fontsize=20)
plt.suptitle('Distribución de eliminaciones de los jugadores', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
kills_data = None
Interpretación: De esto se ve que la mayorÃa de los jugadores no logran tener eliminaciones y que una parte muy reducida de los jugadores logra tener 6 o más eliminaciones.
Ahora se verá cómo afectan las eliminaciones en ganar el juego.
Cuánta gente gana sin tener eliminaciones?
win_nokill = len(train_data[(train_data['kills'] == 0) & (train_data['winPlacePerc'] == 1.0)])
print("Hay %d jugadores en todo el dataset que ganaron una partida y no hicieron ninguna kill." % win_nokill)
res = (win_nokill/len(train_data))*100
print("Hay %.4f%% de jugadores en todo el dataset que ganaron una partida y no hicieron ninguna kill." % res)
Cuánta gente gana sin hacer daño?
win_nodamage = len(train_data[(train_data['damageDealt'] == 0) & (train_data['winPlacePerc'] == 1.0)])
print("Hay %d jugadores en todo el dataset que ganaron una partida y no hicieron daño." % win_nodamage)
res = (win_nodamage/len(train_data))*100
print("Hay %.4f%% de jugadores en todo el dataset que ganaron una partida y no hicieron daño." % res)
headshot_data = train_data.copy()
headshot_data.loc[headshot_data['headshotKills'] >= 5] = '5+'
plt.figure(figsize=(20,10))
sns.countplot(headshot_data['headshotKills'].astype('str').sort_values())
plt.xlabel('Tiros a la cabeza', fontsize=20)
plt.ylabel('Cantidad de jugadores', fontsize=20)
plt.suptitle('Distribución de tiros a la cabeza de los jugadores', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
headshot_data = None
Interpretación: De esto se ve que hay una gran cantidad de jugadores que no logran hacer eliminaciones por headshot y que significa que lograr hacerlos dan cuenta de una gran habilidad.
Otra caracterÃstica importante que demuestra habilidad es la capacidad de ejecutar eliminaciones a distancia.
plt.figure(figsize=(20,10))
sns.distplot(train_data['longestKill'])
plt.xlabel('Distancia de eliminación', fontsize=20)
plt.ylabel('Distribución de jugadores', fontsize=20)
plt.suptitle('Distribución de distancias de eliminación', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
Para interpretar este gráfico se calculan los siguientes resultados
Se obtienen distancia de eliminación máxima, mÃnima y promedio
min_lk = train_data['longestKill'].min()
max_lk = train_data['longestKill'].max()
mean_lk = train_data['longestKill'].mean()
print("Minima distancia de eliminación = %.3f" % min_lk)
print("Máxima distancia de eliminación = %.3f" % max_lk)
print("Promedio de distancia de eliminación = %.3f" % mean_lk)
Interpretación: El promedio de los jugadores logra una distancia de aproximadamente 25 metros y la mÃnima distancia es 0 porque quienes no sacan kills se les cuenta como 0 distancia de eliminación. El máximo de 1052 metros se debe a cómo se toma este dato, que es la distancia entre el jugador que muere y el que lo incapacita, pero un jugador puede incapacitar a otro y escapar en un auto, lo cual hace que esta distancia aumente mucho, también puede darse el caso de un jugador de gran habilidad que atropella a otro y escapa.
Esto hace que este dato no sea relevante para partidas con mas de un jugador por equipo. Para partidas de un jugador por equipo, este dato si es relevante, porque el jugador al ser noqueado queda eliminado automaticamente.
¿Cuántos jugadores quedan con distancia de eliminación igual a 0?
dist_elim0 = len(train_data[train_data['longestKill'] == 0])
print("La cantidad de jugadores que quedaron con distancia de eliminación igual a 0 son: %d" % dist_elim0)
Con respecto a la totalidad del dataset esto corresponde a
print("%.1f%%" % (float(dist_elim0)/(train_data.shape[0] + 1)*100))
plt.figure(figsize=(20,10))
sns.countplot(train_data['vehicleDestroys'].astype('str').sort_values())
plt.xlabel('VehÃculos destruidos', fontsize=20)
plt.ylabel('Cantidad de jugadores', fontsize=20)
plt.suptitle('Distribución de vehÃculos destruidos de los jugadores', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
Se obtiene el porcentaje de jugadores que destruyen vehÃculos
veh_destr = len(train_data[train_data['vehicleDestroys'] > 0])
no_ve_destr = len(train_data[train_data['vehicleDestroys'] == 0])
print ("El %.2f%% de los jugadores destruyen vehÃculos." % (veh_destr*100.0 /len(train_data)))
print ("El %.2f%% de los jugadores no destruyen vehÃculos." % (no_ve_destr*100.0 /len(train_data)))
Interpretación: Los jugadores que destruyen vehÃculos son muy pocos, por lo que es un potencial indicador de habilidad.
plt.figure(figsize=(20,10))
sns.distplot(train_data['walkDistance'])
plt.xlabel('Distancia recorrida [m]', fontsize=20)
plt.ylabel('Cantidad de jugadores', fontsize=20)
plt.suptitle('Distribución de distancia recorrida a pie por los jugadores', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
Se obtienen máximo, mÃnimo y promedio
no_wd = len(train_data[train_data['walkDistance'] == 0])
min_wd = train_data['walkDistance'].min()
max_wd = train_data['walkDistance'].max()
mean_wd = train_data['walkDistance'].mean()
print("Minima distancia recorrida a pie = %.3f" % min_wd)
print("Máxima distancia recorrida a pie = %.3f" % max_wd)
print("Promedio de distancia recorrida a pie = %.3f" % mean_wd)
print("\nPorcentaje de jugadores que recorre 0 metros a pie: %.3f%%" % (no_wd* 100.0 / len(train_data)))
Interpretación: El promedio de los jugadores recorre a pie una distancia aproximada de 1275 metros y un 1.2% de los jugadores recorre 0 metros, estos son jugadores que mueren antes de moverse, ya sea por desconexiones o jugadores que no hacen nada (afk: away from keyboard), y gente que cae en el agua y muere antes de salir, también la máxima distancia recorrida es de 25.780 metros lo cual probablemente sea por uso de software tramposo, ya que el tiempo que dura una partida no alcanza para recorrer 17 kms a pie
plt.figure(figsize=(20,10))
sns.distplot(train_data['swimDistance'])
plt.xlabel('Distancia nadada [m]', fontsize=20)
plt.ylabel('Cantidad de jugadores', fontsize=20)
plt.suptitle('Distribución de distancia nadada por los jugadores', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
Se estudian valores extremos y el promedio
no_sw = len(train_data[train_data['swimDistance'] == 0])
min_sw = train_data['swimDistance'].min()
max_sw = train_data['swimDistance'].max()
mean_sw = train_data['swimDistance'].mean()
print("Minima distancia nadada = %.3f" % min_sw)
print("Máxima distancia nadada = %.3f" % max_sw)
print("Promedio de distancia nadada = %.3f" % mean_sw)
print("\nPorcentaje de jugadores que nada 0 metros: %.3f%%" % (no_sw* 100.0 / len(train_data)))
Interpretación: El promedio de los jugadores nada una distancia aproximada de 5 metros, el record es de 3.5km. El 92.7% de los jugadores no nada, esto se debe mayoritariamente a que en el agua no se puede disparar y que se va considerablemente más lento que en tierra. Usando conocimiento del juego, se podrÃa considerar no tan relevante este atributo en cuanto a la habilidad del jugador, porque pueden haber jugadores muy buenos que nunca entran al agua o jugadores muy malos que tampoco lo hacen.
heal_items = train_data.copy()
heal_items = heal_items['heals'].sort_values()
heal_items.loc[heal_items >= 20] = '20+'
plt.figure(figsize=(20,10))
sns.countplot(heal_items)
plt.xlabel('Items de curación utilizados', fontsize=20)
plt.ylabel('Cantidad de jugadores', fontsize=20)
plt.suptitle('Distribución de cantidad de items de curación utilizados por jugadores', fontsize=35)
plt.tick_params(labelsize=15)
plt.show()
heal_items = None
Valores extremos y promedio
no_hl = len(train_data[train_data['heals'] == 0])
min_hl = train_data['heals'].min()
max_hl = train_data['heals'].max()
mean_hl = train_data['heals'].mean()
print("Minima cantidad de items de curación utilizada = %.3f" % min_hl)
print("Máxima cantidad de items de curación utilizada = %.3f" % max_hl)
print("Promedio de items de curación utilizados = %.3f" % mean_hl)
print("\nPorcentaje de jugadores que no utilizan items de curación: %.3f%%" % (no_hl* 100.0 / len(train_data)))
Interpretación: El promedio de los jugadores utiliza 1 item de curación por partida y el máximo es 80. También se puede apreciar que poco mas de la mitad de los jugadores no utiliza items de curación, ésto se debe a que muchos mueren después de su primer enfrentamiento, son jugadores que no logran ni una kill en las partidas.
Se hace para conocer que tan correlacionados son los atributos entre, par a par.
Primero se define una función que servira para obtener las correlaciones entre atributos
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
def corr_graph(input_data, extra_title='', to_drop_cols=['matchType', 'Id', 'groupId', 'matchId', 'DBNOs', 'revives']):
plt.figure(figsize=(15,15))
solos_cp = input_data.copy()
solos_cp = solos_cp.drop(to_drop_cols, axis=1)
scorr=solos_cp.corr()
tofind = np.where(scorr > 0.7)
indexes = []
cols_ = []
cols_2 = solos_cp.columns.values
for i in range(len(tofind[0])):
if tofind[0][i] < tofind[1][i]:
pair = (tofind[0][i], tofind[1][i])
indexes.append(pair)
cols_.append((cols_2[pair[0]], cols_2[pair[1]]))
ax = plt.gca()
cax = ax.matshow(scorr,cmap=plt.cm.YlOrRd)
ax.set_xticklabels(cols_2, rotation='vertical', fontsize=20)
ax.set_yticklabels(cols_2, fontsize=20)
ax.set_xticks(np.arange(0, len(cols_2), 1))
ax.set_yticks(np.arange(0, len(cols_2), 1))
ax.tick_params(axis='x', labelbottom=True, labeltop=False)
cbar = ax.figure.colorbar(cax, ax=ax, shrink = 0.8)
cbar.ax.set_ylabel("Correlation", rotation=-90, va="bottom", fontsize= 25)
cbar.ax.set_yticklabels(cbar.ax.get_yticklabels(which='both'), fontsize=15)
plt.grid()
plt.title('Matriz de correlación de atributos %s' % extra_title, fontsize=35)
plt.show()
print("Los pares con mayor correlación son:\n")
for col in cols_:
print('\t -', col[0], "con", col[1])
print('\n')
best_correlations = scorr['winPlacePerc'].sort_values(ascending=False)[1:6]
print("Las 5 variables que tiene mayor correlación con winPlacePerc son:\n")
for index in range(len(best_correlations)):
print('\t -', best_correlations[best_correlations == best_correlations[index]].index[0], "-", best_correlations[index])
print("\n")
corr_graph(train_data, extra_title='para el dataset completo')
En lo siguiente se filtraran los datos del dataset completo segun el matchType.
Filtros segun matchType
solos = train_data[train_data.matchType == 'solo']
duos = train_data[train_data.matchType == 'duo']
squad = train_data[train_data.matchType == 'squad']
solosfpp = train_data[train_data.matchType == 'solo-fpp']
duosfpp = train_data[train_data.matchType == 'duo-fpp']
squadfpp = train_data[train_data.matchType == 'squad-fpp']
Se estudia la matriz de correlación para cada filtro
corr_graph(solos, 'para matchType=solos')
corr_graph(solosfpp, 'para matchType=solosfpp')
Comparación solos con solosfpp: Se ve que a grandes rasgos no hay diferencia
corr_graph(duos, 'para matchType=duos')
corr_graph(squad, 'para matchType=squad')
Interpretación: A grandes rasgos no hay diferencias muy notorias de las matrices de correlación entre los distintos matchTypes. De igual forma no hay diferencia notoria entre los modos de primera y tercera persona.
En primer lugar se importan cosas necesarias que ya se hicieron en las secciones anteriores
from common import *
Para esta parte se necesitan definir ciertas funciones que ayudaran a generar resultados rápidamente. Para ahorrar espacio en este informe se definen en el archivo classification_utils.py.
from classification_utils import *
Se quiere predecir si un jugador tiene buen rendimiento en la partida según el lugar en el que quedó. Como el dato que se quiere predecir esta como un número real entre 0 y 1, lo que se hace es particionar en dos categorÃas según el criterio winPlacePerc >= 0.9, donde la clase positiva significa estar en el mejor 10% de una partida.
Para que haya un balance de clases se utilizara la función select_train_data_cls que hace subsampling equilibrado en las dos clases. Para estos experimentos se seleccionarán 15 mil elementos por clase.
En lo siguiente se hace clasificación para partidas solo, utilizando todos los atributos. Ciertamente se ignoran algunos que son atributos de ID no relevantes para la clasificación.
Se hace clasificación para partidas solo
all_columns_relevant = get_columns_relevant(solos)
X, y = select_train_data_cls(solos, all_columns_relevant, percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Solo, con todos los atributos ')
Se quitan atributos redundantes segun el análisis de correlación
all_columns_relevant = get_columns_relevant(solos)
all_columns_relevant.remove('killStreaks')
all_columns_relevant.remove('damageDealt')
all_columns_relevant.remove('winPoints')
all_columns_relevant.remove('numGroups')
X, y = select_train_data_cls(solos, all_columns_relevant, percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Solo, quitando atributos redundantes ')
Se utilizan los atributos que están mas correlacionados con winPlacePerc
X, y = select_train_data_cls(solos, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Solo, usando 5 atributos importantes')
Se prueba solo utilizando el atributo kills
X, y = select_train_data_cls(solos, ['kills'], percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Solo, usando kills')
Interpretación: Al utilizar un solo atributo, se ve el evidente impacto negativo que hay en el recall de los resultados.
Se prueba utilizando kills y walkDistance
X, y = select_train_data_cls(solos, ['kills', 'walkDistance'], percentage=0.9, to_select=15000)
solo_results_kills_wd = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills_wd, ' en partidas Solo, usando kills y walkDistance')
Interpretación: De lo visto hasta ahora se ve que utilizando los 5 atributos más correlacionados con winPlacePerc se obtienen resultados muy similares a usando todos los atributos y a usar todos los atributos excepto redundantes. Por lo tanto desde ahora en adelante se hara la clasificación utilizando los 5 atributos más importantes.
Se prueban los 5 atributos más importantes con el modo Solo en primera persona
X, y = select_train_data_cls(solosfpp, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Solo, usando 5 atributos importantes')
Comparación con modo solo: Se ve que comparado con el modo solo no tiene diferencias significativas en los resuiltados. Por lo tanto ya no tiene demasiado sentido seguir comparando entre los modos primera y tercera persona. De ahora en adelante solo se utilizará para el análisis los modos en tercera persona (sin el sufijo fpp).
Clasificación utilizando los 5 atributos más correlacionados a winPlacePerc en el modo Duos
X, y = select_train_data_cls(duos, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Duo, usando 5 atributos importantes')
Clasificación utilizando los 5 atributos más correlacionados a winPlacePerc en el modo Squad
X, y = select_train_data_cls(squad, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.9, to_select=15000)
solo_results_kills = run_the_classifiers(X, y)
plot_bar_chart(solo_results_kills, ' en partidas Squad, usando 5 atributos importantes')
Interpretación: Nuevamente se ve que no hay tanta diferencia en los resultados al variar segun el matchType.
También resulta interesante ver el caso en que se particiona el espacio de winPlacePerc en más de dos clases y ver como afecta al poder predictivo de los clasificadores. El espacio se particiona segun percentiles, en vez de una partición desbalanceada como la que se tenÃa antes de >= 0.9.
Para este análisis solo se utilizarán partidas con matchType = solos, porque no hay tanta diferencia de resultados al cambiar el matchType.
Se usan todas las columnas no redundantes con 10 categorÃas
all_columns_relevant = get_columns_relevant(solos)
all_columns_relevant.remove('killStreaks')
all_columns_relevant.remove('damageDealt')
all_columns_relevant.remove('winPoints')
all_columns_relevant.remove('numGroups')
X, y = select_train_data_cls_several_categories(solos, all_columns_relevant, categories=10, to_select=15000)
solo_results = run_the_classifiers(X, y, average='micro')
plot_bar_chart(solo_results, ' en partidas Solo, con 10 categorias')
Se usan todas las columnas no redundantes con 6 categorÃas
X, y = select_train_data_cls_several_categories(solos, all_columns_relevant, categories=6, to_select=15000)
solo_results = run_the_classifiers(X, y, average='micro')
plot_bar_chart(solo_results, ' en partidas Solo, con 6 categorias')
Se usan todas las columnas no redundantes con 3 categorÃas
X, y = select_train_data_cls_several_categories(solos, all_columns_relevant, categories=3, to_select=15000)
solo_results_kills = run_the_classifiers(X, y, average='micro')
plot_bar_chart(solo_results_kills, ' en partidas Solo, con 3 categorias')
Interpretación: Se ve que mientras menos categorÃas, mayor el poder predictivo de los clasificadores.
Como el atributo winPlacePerc es un valor real entre 0 y 1 que se quiere predecir, es necesario tener un modelo regresivo para este fin.
En este trabajo se prueban los regresores de Random Forest y Support Vector Machine.
Se importa código python que contiene funciones útiles para probar los modelos.
from common import *
from regression_utils import *
Para saber que hiper-parámetros utilizar en los regresores se utiliza GridSearchCV que busca de forma exhaustiva los mejores parámetros según una métrica que se especifica.
Grid search para Random Forest Regression
X, y = select_train_data_reg(solos, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.5, to_select=1000)
gs_random_fr(X, y)
De esto se ve que para distintos criterios se obtienen configuraciones parecidas, en pos de la eficiencia se usara la siguiente configuración, que es la que da mejor puntaje R^2:
'criterion': 'mse', 'max_depth': 4, 'min_samples_split': 2, 'n_estimators': 100
Grid search para SVR
X, y = select_train_data_reg(solos, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.5, to_select=1000)
gs_svr(X, y, n_jobs=3)
En este caso se ve que los parámetros que más se repiten son C=0.5 y epsilon=0.001, por lo que se usaran aquellos.
Se define la siguiente funcion utilitaria
def output_reg(rf_s, svm_s):
print("Random Forest Regression Score = %.3f" % rf_s)
print("Support Vector Machine Regression Score = %.3f" % svm_s)
Regresión para los 5 atributos más correlacionados a winPlacePerc, con matchType = solos
X, y = select_train_data_reg(solos, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.5, to_select=15000)
rfs = random_forest_regressor_score(X, y)
svmrs = svm_regressor_score(X, y)
output_reg(rfs, svmrs)
Regresión para los 5 atributos más correlacionados a winPlacePerc, con matchType = duos
X, y = select_train_data_reg(duos, ['walkDistance', 'weaponsAcquired', 'boosts', 'damageDealt', 'kills'], percentage=0.5, to_select=15000)
rfs = random_forest_regressor_score(X, y)
svmrs = svm_regressor_score(X, y)
output_reg(rfs, svmrs)
Regresión para los 5 atributos más correlacionados a winPlacePerc, con matchType = squad
X, y = select_train_data_reg(squad, ['walkDistance', 'boosts', 'weaponsAcquired', 'damageDealt', 'heals'], percentage=0.5, to_select=15000)
rfs = random_forest_regressor_score(X, y)
svmrs = svm_regressor_score(X, y)
output_reg(rfs, svmrs)
Para los otros matchType se omiten en este informe porque son similares y no aportan más información que lo que ya está.
A continuación se aplicaran tecnicas de clustering para ver posibles cumulos de datos e intentar establecer mas relaciones entre ellos.
Para esto haremos clustering de 2 clases con cada uno de los 6 dataset, usaremos las 5 clases con mayor correlación con respecto a winPlacePerc, nuestra clase a predecir, y la misma ya mencionada.
A partir de este analisis se espera poder obtener más información con respecto a los datos y posiblemente poder refinar la predicción hasta lograr un valor cercano al 100%.
from sklearn.cluster import KMeans, DBSCAN
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import itertools
from cycler import cycler
def get_normalized2(X):
return (X - np.mean(X)) / np.std(X)
def get_normalized(X):
return (X - np.min(X)) / (np.max(X) - np.min(X))
def get_iter_style():
return itertools.cycle(cycler(marker=['s', '8', 'X', 'P', 'D', 'p', '*', 'H',
9, 1, 2, 3, 4, '_', 'x', '|', 10, 11, 4]) * cycler(color=[
'#e6194b', '#3cb44b', '#ffe119', '#0082c8', '#f58231', '#911eb4', '#46f0f0',
'#f032e6', '#d2f53c', '#fabebe', '#008080', '#e6beff', '#aa6e28', '#fffac8',
'#800000', '#aaffc3', '#808000', '#ffd8b1', '#000080', '#808080'
]))
def kmeans_2d(X, k, columns, extra_title=""):
kmeans_result = KMeans(n_clusters=k, random_state=0).fit(X)
labels = kmeans_result.labels_
centroids = kmeans_result.cluster_centers_
iterstyle = get_iter_style()
plt.figure(figsize=(20,10))
#Xs = []
for i in range(k):
Xi = X[labels == i]
Xi_x, Xi_y = Xi[:, 0], Xi[:, 1]
#Xs.append((Xi_x, Xi_y))
plt.scatter(Xi_x, Xi_y, **next(iterstyle))
plt.scatter(centroids[i][0], centroids[i][1], c='black', s=300)
#plt.scatter(X1_x, X1_y, c='red')
#plt.scatter(X2_x, X2_y, c='blue')
plt.xlabel(columns[0], fontsize=20)
plt.ylabel(columns[1], fontsize=20)
plt.suptitle('Kmeans clustering en 2D con k=%d%s' % (k, extra_title), fontsize=35)
plt.tick_params(labelsize=20)
plt.show()
def kmeans(X, k, extra_title=""):
kmeans_result = KMeans(n_clusters=k, random_state=0).fit(X)
labels = kmeans_result.labels_
centroids = kmeans_result.cluster_centers_
vis_X = PCA(n_components=2).fit_transform(X)
iterstyle = get_iter_style()
plt.figure(figsize=(20,10))
#Xs = []
for i in range(k):
Xi = vis_X[labels == i]
Xi_x, Xi_y = Xi[:, 0], Xi[:, 1]
#Xs.append((Xi_x, Xi_y))
plt.scatter(Xi_x, Xi_y, **next(iterstyle))
plt.scatter(centroids[i][0], centroids[i][1], c='black', s=300)
#plt.scatter(X1_x, X1_y, c='red')
#plt.scatter(X2_x, X2_y, c='blue')
plt.xlabel('X', fontsize=20)
plt.ylabel('Y', fontsize=20)
plt.suptitle('Kmeans clustering con k=%d%s' % (k, extra_title), fontsize=35)
plt.tick_params(labelsize=20)
plt.show()
Los parametros con mayor correlación para este dataset fueron walkDistance, WeaponsAcquired, boosts, damageDealt, kills.
Se haran experiemntos con KMeans entre cada uno de estos parametro y winPlacePerc, y veremos que es lo que sucede.
columns_to_select = ['walkDistance', 'winPlacePerc']
X, _ = select_train_data_cls(solos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos con walkDistance y winPlacePerc")
columns_to_select = ['weaponsAcquired', 'winPlacePerc']
X, _ = select_train_data_cls(solos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos con weaponsAcquired y winPlacePerc")
columns_to_select = ['boosts', 'winPlacePerc']
X, _ = select_train_data_cls(solos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos con boosts y winPlacePerc")
columns_to_select = ['damageDealt', 'winPlacePerc']
X, _ = select_train_data_cls(solos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos con damageDealt y winPlacePerc")
columns_to_select = ['kills', 'winPlacePerc']
X, _ = select_train_data_cls(solos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos con kills y winPlacePerc")
Los parametros con mayor correlación para este dataset fueron walkDistance, WeaponsAcquired, boosts, damageDealt, kills. Al igual que en el anterior.
Se haran experiemntos con KMeans entre cada uno de estos parametro y winPlacePerc, y veremos que es lo que sucede.
columns_to_select = ['walkDistance', 'winPlacePerc']
X, _ = select_train_data_cls(solosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos fpp con walkDistance y winPlacePerc")
columns_to_select = ['weaponsAcquired', 'winPlacePerc']
X, _ = select_train_data_cls(solosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos fpp con weaponsAcquired y winPlacePerc")
columns_to_select = ['boosts', 'winPlacePerc']
X, _ = select_train_data_cls(solosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos fpp con boosts y winPlacePerc")
columns_to_select = ['damageDealt', 'winPlacePerc']
X, _ = select_train_data_cls(solosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos fpp con damageDealt y winPlacePerc")
columns_to_select = ['kills', 'winPlacePerc']
X, _ = select_train_data_cls(solosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Solos fpp con kills y winPlacePerc")
Los parametros con mayor correlación para este dataset fueron walkDistance, boosts, WeaponsAcquired, damageDealt, kills.
Se haran experiemntos con KMeans entre cada uno de estos parametro y winPlacePerc, y veremos que es lo que sucede.
columns_to_select = ['walkDistance', 'winPlacePerc']
X, _ = select_train_data_cls(duos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos con walkDistance y winPlacePerc")
columns_to_select = ['boosts', 'winPlacePerc']
X, _ = select_train_data_cls(duos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos con boosts y winPlacePerc")
columns_to_select = ['weaponsAcquired', 'winPlacePerc']
X, _ = select_train_data_cls(duos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos con weaponsAcquired y winPlacePerc")
columns_to_select = ['damageDealt', 'winPlacePerc']
X, _ = select_train_data_cls(duos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos con damageDealt y winPlacePerc")
columns_to_select = ['kills', 'winPlacePerc']
X, _ = select_train_data_cls(duos, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos con kills y winPlacePerc")
Los parametros con mayor correlación para este dataset fueron walkDistance, WeaponsAcquired, boosts, damageDealt, kills. Al igual que en el anterior.
Se haran experiemntos con KMeans entre cada uno de estos parametro y winPlacePerc, y veremos que es lo que sucede.
columns_to_select = ['walkDistance', 'winPlacePerc']
X, _ = select_train_data_cls(duosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos fpp con walkDistance y winPlacePerc")
columns_to_select = ['weaponsAcquired', 'winPlacePerc']
X, _ = select_train_data_cls(duosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos fpp con weaponsAcquired y winPlacePerc")
columns_to_select = ['boosts', 'winPlacePerc']
X, _ = select_train_data_cls(duosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos fpp con boosts y winPlacePerc")
columns_to_select = ['damageDealt', 'winPlacePerc']
X, _ = select_train_data_cls(duosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos fpp con damageDealt y winPlacePerc")
columns_to_select = ['kills', 'winPlacePerc']
X, _ = select_train_data_cls(duosfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Duos fpp con kills y winPlacePerc")
Los parametros con mayor correlación para este dataset fueron walkDistance, boosts, weaponsAcquired, damageDealt, heals.
Se haran experiemntos con KMeans entre cada uno de estos parametro y winPlacePerc, y veremos que es lo que sucede.
columns_to_select = ['walkDistance', 'winPlacePerc']
X, _ = select_train_data_cls(squad, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads con walkDistance y winPlacePerc")
columns_to_select = ['boosts', 'winPlacePerc']
X, _ = select_train_data_cls(squad, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads con boosts y winPlacePerc")
columns_to_select = ['weaponsAcquired', 'winPlacePerc']
X, _ = select_train_data_cls(squad, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads con weaponsAcquired y winPlacePerc")
columns_to_select = ['damageDealt', 'winPlacePerc']
X, _ = select_train_data_cls(squad, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads con damageDealt y winPlacePerc")
columns_to_select = ['heals', 'winPlacePerc']
X, _ = select_train_data_cls(squad, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads con heals y winPlacePerc")
Los parametros con mayor correlación para este dataset fueron walkDistance, boosts, weaponsAcquired, heals, damageDealt.
Se haran experiemntos con KMeans entre cada uno de estos parametro y winPlacePerc, y veremos que es lo que sucede.
columns_to_select = ['walkDistance', 'winPlacePerc']
X, _ = select_train_data_cls(squadfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads fpp con walkDistance y winPlacePerc")
columns_to_select = ['boosts', 'winPlacePerc']
X, _ = select_train_data_cls(squadfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads fpp con boosts y winPlacePerc")
columns_to_select = ['weaponsAcquired', 'winPlacePerc']
X, _ = select_train_data_cls(squadfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads fpp con weaponsAcquired y winPlacePerc")
columns_to_select = ['heals', 'winPlacePerc']
X, _ = select_train_data_cls(squadfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads fpp con heals y winPlacePerc")
columns_to_select = ['damageDealt', 'winPlacePerc']
X, _ = select_train_data_cls(squadfpp, columns_to_select, percentage=0.9, to_select=15000)
X[:, 0] = get_normalized(X[:, 0])
kmeans_2d(X, 5, columns_to_select, ", Squads fpp con damageDealt y winPlacePerc")