<jupyter_start><jupyter_text>Import de las librerias<jupyter_code>import numpy as np
import pandas as pd

#visualizacion de datos

import matplotlib.pyplot as plt 
import seaborn as sns

#import graphviz

plt.style.use("ggplot")
pd.set_option("display.max_rows",100)
import xgboost as xgb

from sklearn import preprocessing

from sklearn import model_selection
from sklearn.tree import DecisionTreeClassifier

import xgboost as xgb
from datetime import datetime

from sklearn.tree import export_graphviz

# para pintar arboles de decision
import graphviz<jupyter_output><empty_output><jupyter_text>Import de los datos<jupyter_code>df=pd.read_csv("sample_mmp.csv")

df.head()
df.drop("Unnamed: 0", axis = 1, inplace = True)
#Elimino la columna "Unnamed: 0" ya que es un duplicado del indice
df.head()
#compruebo que se ha eliminado correctamente<jupyter_output><empty_output><jupyter_text>Machine learning Checklist - Analisis Univariante 2.1  Tamaño del dataset<jupyter_code>df.info(verbose = False)
#Vemos los tipos de variables. Tengo 36 variables tipo float(son numericas), 17 variables int (son numéricas) y 30 object (variables no numéricas). Así que tengo que ver cuáles son para pasarlas a numéricas.

#Veo que ocupa más de 316 MB en Ram
#Veo que hay 500000 entradas y que el identificados está correcto, por lo que no es necesaerio haver un reset index<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 500000 entries, 0 to 499999
Columns: 83 entries, MachineIdentifier to HasDetections
dtypes: float64(36), int64(17), object(30)
memory usage: 316.6+ MB<jupyter_text>Visualizacion directa<jupyter_code>df.columns
df.head(1)
TARGET = 'HasDetections'
#Nuestro target es cuando la columna 'HasDetections'=1, lo que significa que se ha detectado Malware en la máquina<jupyter_output><empty_output><jupyter_text>Tipos de atributos<jupyter_code>df.info(verbose=True)
##Vemos que tipo de dato es cada columna, los nulos que tiene, etc<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 500000 entries, 0 to 499999
Data columns (total 83 columns):
 #   Column                                             Non-Null Count   Dtype  
---  ------                                             --------------   -----  
 0   MachineIdentifier                                  500000 non-null  object 
 1   ProductName                                        500000 non-null  object 
 2   EngineVersion                                      500000 non-null  object 
 3   AppVersion                                         500000 non-null  object 
 4   AvSigVersion                                       500000 non-null  object 
 5   IsBeta                                             500000 non-null  int64  
 6   RtpStateBitfield                                   498168 non-null  float64
 7   IsSxsPassiveMode                                   500000 non-null  int64  
 8   DefaultBrowsersIdentifier                          24061 non-null   float6[...]<jupyter_text>Paso 1: Ver que columnas de tipo categórico son en realidad numéricas y viceversa<jupyter_code>constant_columns=[]

for columns in df.columns:
    if df[columns].nunique() == 1:
        constant_columns.append(columns)

#Antes de empezar miro las columnas que tienen un valor único

df.drop(constant_columns, axis =1 , inplace = True)

#Elimino estas columnas ['PuaMode', 'Census_IsWIMBootEnabled']
lista_var_num=[]
lista_var_cat=[]

for i in df.columns:
    if df[i].dtype == "int64" or df[i].dtype == "float64":
       lista_var_num.append(i)
    else:
       lista_var_cat.append(i)

#Creo una sublista con las variables numéricas y otra con las variables categóricas
lista_var_cat

lista_var_num<jupyter_output><empty_output><jupyter_text>Estadisticos descriptivos<jupyter_code>#Para ver los estadisticos descriptivos usamos el metodo describe. Transponemos las filas a columas y viceversas para verlo mejor

df.describe().T

#Nos muestra solo las filas numericas
df.head().T
df.describe(exclude=np.number).T

#Para que nos muestre las categoricas, lo que hacemos es decirle que excluya las numericas
#En la columna de count puedo ver si alguna columna tiene nulos (PuaMode,SmartScreen...)<jupyter_output><empty_output><jupyter_text>Valores Nulos<jupyter_code>df.select_dtypes(include = np.number).isnull().sum()

#Miro las variables numericas que tienen nulos
#Al hacer un sum me hace el conteo
df.select_dtypes(exclude = np.number).isnull().sum()

#Miro las variables no numericas que tienen nulos
#Al hacer un sum me hace el conteo<jupyter_output><empty_output><jupyter_text>Variable target - Distribucion<jupyter_code>df[TARGET].value_counts()

#Veo que hay 249953 máquinas en las que se ha detectado Malware y 250047 en las que no
df[TARGET].mean()

#Veo cual es la proporción. Casi en la mitad de las máquinas se ha detectado Malware
#Veo que no es una tasa dominante(super grande o super pequeña)<jupyter_output><empty_output><jupyter_text>ML checklist Puntos  2.1.7 - 11: EDA  El objetivo del EDA es asegurarse que todas las variables sean numéricas a la vez de sacar conclusiones antes de lanzar el modelo.  Análisis de fechas<jupyter_code>#: No tenemos ninguna variable fecha<jupyter_output><empty_output><jupyter_text>Análisis de variables<jupyter_code>infectados_por_so = df[['Platform','HasDetections']].groupby(['Platform']).mean()
infectados_por_so

#Veo el porcentaje de infectados por Sistema Operativo. Me fijo que windows 2016 está un poco por debajo de la media y que windows 8 la supera.
infectados_por_so.hist()
infectados_por_producto = df[['ProductName','HasDetections']].groupby(['ProductName']).mean()
infectados_por_producto 

#Me fijo que en los equipos con Microsoft Security Essentials Prerelease (mseprerelease) no hay ningina máquina infectada. Dato curioso cuanto menos.	
infectados_por_pais = df[['CountryIdentifier','HasDetections']].groupby(['CountryIdentifier']).mean()
infectados_por_pais
#Veo que en algunos paises como el 219 el ratio de infeccion es super bajo<jupyter_output><empty_output><jupyter_text>Análisis de variables numéricas<jupyter_code>df["IsBeta"].value_counts()

#La variable "IsBeta" - Defender state information e.g. false nos muestra información sobre el estado del defensor. Vemos que prácticamente todas son falsas
#Podriamos eliminar esta columna
df['IsBeta'].plot(kind='hist', bins = 100)
df["DefaultBrowsersIdentifier"].value_counts()

#Investigo un poco la variable DefaultBrowsersIdentifier
df['DefaultBrowsersIdentifier'].plot(kind='hist', bins = 100)

#Analizo gráficamente la variable DefaultBrowsersIdentifier
sns.boxplot(data=df, x=TARGET, y='DefaultBrowsersIdentifier')

#Analizo gráficamente la variable DefaultBrowsersIdentifier
sns.boxplot(data=df, x=TARGET, y='ProductName')

#Analizo gráficamente la variable ProductName
sns.boxplot(data=df, x=TARGET, y='Platform')

#Analizo gráficamente la variable Platform<jupyter_output><empty_output><jupyter_text>Análisis categoría<jupyter_code>df.pivot_table(index='Census_InternalBatteryType', values=TARGET, aggfunc=[len, sum, np.mean]).sort_values(by=[('mean',TARGET)], ascending = False)

#Vemos que en los equipos con baterias 3ion,batt y virt	no hay malware. En cambio los equipos con baterias li-l,icp3 y lgl0	están infectados. En el resto la probabilidad de que estén infectados o no varía.<jupyter_output><empty_output><jupyter_text>Creación de funciones<jupyter_code>def OHE(dataframe, column):
    dummy_df = pd.get_dummies(dataframe[column],prefix=column)
    dataframe = pd.concat((dataframe, dummy_df), axis=1)
    del(dataframe[column])
    return dataframe

    #Creo una función para realizar el OHE a la variable dada

def setOthers(dataframe, column, num_value):
    top_categories = dataframe[column].value_counts().head(num_value)
    top_categories_list = top_categories.index.to_list()
    top_categories_list.append('Others')
    dataframe[column] = pd.Categorical(dataframe[column], categories= top_categories_list)
    return dataframe[column].fillna('Others')

     #Creo una función para realizar el Set Others a la variable dada<jupyter_output><empty_output><jupyter_text>Tratamiento de variables. Ver excell adjunto para entender que se hace a cada variable<jupyter_code>#Empiezo a hacer el setOthers a las distintas variables
df['EngineVersion'] = setOthers(df,'EngineVersion', 4)

df['AppVersion'] = setOthers(df,'AppVersion', 10)

df['AvSigVersion'] = setOthers(df,'AvSigVersion', 5)
df['RtpStateBitfield'] = setOthers(df,'RtpStateBitfield', 2)
df['DefaultBrowsersIdentifier'] = setOthers(df,'DefaultBrowsersIdentifier', 3)
df['AVProductStatesIdentifier'] = setOthers(df,'AVProductStatesIdentifier', 4)

df['AVProductsInstalled'] = setOthers(df,'AVProductsInstalled', 2)
df['AVProductsEnabled'] = setOthers(df,'AVProductsEnabled', 5)
df['CountryIdentifier'] = setOthers(df,'CountryIdentifier', 5)
df['CityIdentifier'] = setOthers(df,'CityIdentifier', 5)
df['OrganizationIdentifier'] = setOthers(df,'OrganizationIdentifier', 4)
df['GeoNameIdentifier'] = setOthers(df,'GeoNameIdentifier',5)
df['LocaleEnglishNameIdentifier'] = setOthers(df,'LocaleEnglishNameIdentifier',5)
df['OsVer'] = setOthers(df,'OsVer',3)
df['OsBuild'] = setOthers(df,'OsBuild',3)
df['OsSuite'] = setOthers(df,'OsSuite',2)
df['OsPlatformSubRelease'] = setOthers(df,'OsPlatformSubRelease',4)
df['OsBuildLab'] = setOthers(df,'OsBuildLab',4)
df['SkuEdition'] = setOthers(df,'SkuEdition',3)
df['IeVerIdentifier'] = setOthers(df,'IeVerIdentifier',4)
df['SmartScreen'] = setOthers(df,'SmartScreen',4)
df['Census_MDC2FormFactor'] = setOthers(df,'Census_MDC2FormFactor',4)
df['Census_OEMNameIdentifier'] = setOthers(df,'Census_OEMNameIdentifier',5)

df['Census_ProcessorCoreCount'] = setOthers(df,'Census_ProcessorCoreCount',5)

df['Census_ProcessorModelIdentifier'] = setOthers(df,'Census_ProcessorModelIdentifier',3)

df['Census_PrimaryDiskTotalCapacity'] = setOthers(df,'Census_PrimaryDiskTotalCapacity',5)

df['Census_SystemVolumeTotalCapacity'] = setOthers(df,'Census_SystemVolumeTotalCapacity',5)
df['Census_TotalPhysicalRAM'] = setOthers(df,'Census_TotalPhysicalRAM',4)
df['Census_ChassisTypeName'] = setOthers(df,'Census_ChassisTypeName',5)

df['Census_InternalPrimaryDiagonalDisplaySizeInInches'] = setOthers(df,'Census_InternalPrimaryDiagonalDisplaySizeInInches',5)
df['Census_InternalPrimaryDisplayResolutionHorizontal'] = setOthers(df,'Census_InternalPrimaryDisplayResolutionHorizontal',5)
df['Census_InternalPrimaryDisplayResolutionVertical'] = setOthers(df,'Census_InternalPrimaryDisplayResolutionVertical',3)
df['Census_PowerPlatformRoleName'] = setOthers(df,'Census_PowerPlatformRoleName',3)
df['Census_InternalBatteryType'] = setOthers(df,'Census_InternalBatteryType',3)
df['Census_InternalBatteryNumberOfCharges'] = setOthers(df,'Census_InternalBatteryNumberOfCharges',2)
df['Census_OSVersion'] = setOthers(df,'Census_OSVersion',3)
df['Census_OSBranch'] = setOthers(df,'Census_OSBranch',3)
df['Census_OSBuildNumber'] = setOthers(df,'Census_OSBuildNumber',4)
df['Census_OSBuildRevision'] = setOthers(df,'Census_OSBuildRevision',4)
df['Census_OSEdition'] = setOthers(df,'Census_OSEdition',3)
df['Census_OSSkuName'] = setOthers(df,'Census_OSSkuName',3)
df['Census_OSInstallTypeName'] = setOthers(df,'Census_OSInstallTypeName',4)
df['Census_OSInstallLanguageIdentifier'] = setOthers(df,'Census_OSInstallLanguageIdentifier',12)
df['Census_OSUILocaleIdentifier'] = setOthers(df,'Census_OSUILocaleIdentifier',12)
df['Census_OSWUAutoUpdateOptionsName'] = setOthers(df,'Census_OSWUAutoUpdateOptionsName',2)
df['Census_GenuineStateName'] = setOthers(df,'Census_GenuineStateName',2)
df['Census_ActivationChannel'] = setOthers(df,'Census_ActivationChannel',2)

df['Census_FlightRing'] = setOthers(df,'Census_FlightRing',2)

df['Census_FirmwareManufacturerIdentifier'] = setOthers(df,'Census_FirmwareManufacturerIdentifier',3)
df['Census_FirmwareVersionIdentifier'] = setOthers(df,'Census_FirmwareVersionIdentifier',4)
df['OsBuild'] = setOthers(df,'OsBuild',2)
#Borro las variables que he decidido borrar
borrar_todas=['Census_ThresholdOptIn','Census_ThresholdOptIn','Census_IsFlightsDisabled','Census_IsFlightingInternal','Census_IsPortableOperatingSystem','MachineIdentifier','IsBeta','Census_DeviceFamily']


df.drop(borrar_todas, axis =1 , inplace = True)

#elimino variables
#Hago EL OHE
#Intento hacer funcion para hacer el OHE todas a la evz, pero me peta. Voy una a una

#lista_OHE=['ProductName','EngineVersion','AppVersion','AvSigVersion','RtpStateBitfield','DefaultBrowsersIdentifier','AVProductStatesIdentifier','AVProductsInstalled','AVProductsEnabled','CountryIdentifier','CityIdentifier','OrganizationIdentifier','GeoNameIdentifier','LocaleEnglishNameIdentifier','Processor','OsVer','OsBuild','OsSuite','OsPlatformSubRelease','OsBuildLab','SkuEdition','IeVerIdentifier','SmartScreen','Census_MDC2FormFactor','Census_OEMNameIdentifier','Census_OEMModelIdentifier','Census_ProcessorCoreCount','Census_ProcessorManufacturerIdentifier','Census_ProcessorModelIdentifier','Census_ProcessorClass','Census_PrimaryDiskTotalCapacity','Census_PrimaryDiskTypeName','Census_SystemVolumeTotalCapacity','Census_TotalPhysicalRAM','Census_ChassisTypeName','Census_InternalPrimaryDiagonalDisplaySizeInInches','Census_InternalPrimaryDisplayResolutionHorizontal','Census_InternalPrimaryDisplayResolutionVertical','Census_PowerPlatformRoleName','Census_InternalBatteryType','Census_InternalBatteryNumberOfCharges','Census_OSVersion','Census_OSArchitecture','Census_OSBranch','Census_OSBuildNumber','Census_OSBuildRevision','Census_OSEdition','Census_OSSkuName','Census_OSInstallTypeName','Census_OSInstallLanguageIdentifier','Census_OSUILocaleIdentifier','Census_OSWUAutoUpdateOptionsName','Census_GenuineStateName','Census_ActivationChannel','Census_FlightRing','Census_FirmwareManufacturerIdentifier','Census_FirmwareVersionIdentifier','Wdft_RegionIdentifier']


#for i in lista_OHE:
#   df[i]=OHE(df, i)
df =  OHE(df,'Platform')
df =  OHE(df,'ProductName')
df =  OHE(df,'EngineVersion')
df =  OHE(df,'AppVersion')
df =  OHE(df,'AvSigVersion')
df =  OHE(df,'RtpStateBitfield')
df =  OHE(df,'DefaultBrowsersIdentifier')
df =  OHE(df,'AVProductStatesIdentifier')
df =  OHE(df,'AVProductsInstalled')
df =  OHE(df,'AVProductsEnabled')
df =  OHE(df,'CountryIdentifier')
df =  OHE(df,'CityIdentifier')
df =  OHE(df,'OrganizationIdentifier')
df =  OHE(df,'GeoNameIdentifier')
df =  OHE(df,'LocaleEnglishNameIdentifier')
df =  OHE(df,'Processor')
df =  OHE(df,'OsVer')
df =  OHE(df,'OsBuild')
df =  OHE(df,'OsSuite')
df =  OHE(df,'OsPlatformSubRelease')
df =  OHE(df,'OsBuildLab')
df =  OHE(df,'SkuEdition')
df =  OHE(df,'IeVerIdentifier')
df =  OHE(df,'SmartScreen')
df =  OHE(df,'Census_MDC2FormFactor')
df =  OHE(df,'Census_OEMNameIdentifier')
df =  OHE(df,'Census_ProcessorCoreCount')
df =  OHE(df,'Census_ProcessorManufacturerIdentifier')
df =  OHE(df,'Census_ProcessorModelIdentifier')
df =  OHE(df,'Census_ProcessorClass')
df =  OHE(df,'Census_PrimaryDiskTotalCapacity')
df =  OHE(df,'Census_PrimaryDiskTypeName')
df =  OHE(df,'Census_SystemVolumeTotalCapacity')
df =  OHE(df,'Census_TotalPhysicalRAM')
df =  OHE(df,'Census_ChassisTypeName')
df =  OHE(df,'Census_InternalPrimaryDiagonalDisplaySizeInInches')
df =  OHE(df,'Census_InternalPrimaryDisplayResolutionHorizontal')
df  =  OHE(df,'Census_InternalPrimaryDisplayResolutionVertical')
df =  OHE(df,'Census_PowerPlatformRoleName')
df =  OHE(df,'Census_InternalBatteryType')
df =  OHE(df,'Census_InternalBatteryNumberOfCharges')
df =  OHE(df,'Census_OSVersion')
df =  OHE(df,'Census_OSArchitecture')
df =  OHE(df,'Census_OSBranch')
df =  OHE(df,'Census_OSBuildNumber')
df =  OHE(df,'Census_OSBuildRevision')
df =  OHE(df,'Census_OSEdition')
df =  OHE(df,'Census_OSSkuName')
df =  OHE(df,'Census_OSInstallTypeName')
df =  OHE(df,'Census_OSInstallLanguageIdentifier')
df =  OHE(df,'Census_OSUILocaleIdentifier')
df =  OHE(df,'Census_OSWUAutoUpdateOptionsName')
df =  OHE(df,'Census_GenuineStateName')
df =  OHE(df,'Census_ActivationChannel')
df =  OHE(df,'Census_FlightRing')
df =  OHE(df,'Census_FirmwareManufacturerIdentifier')
df =  OHE(df,'Census_FirmwareVersionIdentifier')
df =  OHE(df,'Wdft_RegionIdentifier')
#En las variables que son booleanas pero con nulos, le doy a los nulos el valor de -1
booleanas_quitar_nulos=['IsProtected','AutoSampleOptIn','SMode','Firewall','UacLuaenable','Census_IsVirtualDevice','Census_IsAlwaysOnAlwaysConnectedCapable','Wdft_IsGamer','Census_OEMModelIdentifier']
def rellena_con_valor(df, ListaConNulos, valor):
    for i in ListaConNulos:
        df[i].fillna(value=valor, inplace=True)

#creo una funcion para rellenar los NULOS con un valor 'valor' 
rellena_con_valor(df, booleanas_quitar_nulos, -1)
df.columns[df.isnull().any()].tolist() 

#Antes de lanzar el modelo reviso que no haya ninguna columna con nulos
df.isnull().any(False).sum()

#doble revision. Cuento los nulos que hay<jupyter_output><empty_output><jupyter_text>Empezamos con el modelling  Separacion del dataset: 1.development/validacion 2. del development: train/test  1.development/validacion<jupyter_code>#estrategia de SO con mas registros 
df_val = df[df['Platform_windows10'] == 1].sample(frac=0.3)
df_dev = pd.merge(df,df_val, indicator=True, how='outer').query('_merge=="left_only"').drop('_merge', axis=1)

#Tomo el 75% de los valores para desarrollar el modelo y el 25% para validarlo
#Curiosidad: Lo he hecho con windows 10 y sin Windows 10, sin filtrar por variable. Sin filtrar por variable la tasa de acierto es un 1% más alta, pero he dejado el modelo de Windows 10 porque entiendo que refleja más la situación real
df_dev.shape
#miro el tamaño del df_dev 
df_val.shape
#y del df_val
df_val_X = df_val.drop(TARGET,axis=1) #NO INPLACE, NO QUIERO MODIFICAR, atributos
df_val_y = df_val[[TARGET]] #DOBLE CORCHETE PORQUE QUIERO DF, NO SERIES, target

df_dev_X = df_dev.drop(TARGET,axis=1) #NO INPLACE, NO QUIERO MODIFICAR
df_dev_y = df_dev[[TARGET]]

df_dev_X.head()<jupyter_output><empty_output><jupyter_text>Particion train test<jupyter_code>X_train, X_test, y_train, y_test = model_selection.train_test_split(
    df_dev_X,
    df_dev_y,
    random_state = 42,
    test_size = 0.2
)

#función para entrenar el modelo
X_test.shape #suficientes observaciones para testear<jupyter_output><empty_output><jupyter_text>Primer modelo<jupyter_code># INSTANCIAR el modelo, hago un árbol de decisiones
dt = DecisionTreeClassifier(
                        max_depth=4, # Importante, numero de preguntas que realiza el algoritmo
                        min_samples_leaf=50, # solo para el nodo terminal, numero de observaciones minima
                        random_state=42,
                )

#2.fitear

dt.fit(X_train, y_train)
dot_data = export_graphviz(decision_tree= dt,
                out_file=None,
                feature_names= X_test.columns,
                class_names= ['not infected','infected']
)

graphviz.Source(dot_data)

#creo el árbol

# PREDICCION DEL MODELO
dt.predict(X_test)

dt.predict_proba(X_test)


y_test_pred = pd.DataFrame(dt.predict(X_test), index= y_test.index, columns= ['DetectionPrediction'])

y_test_pred.head()

results_df = y_test.join(y_test_pred)

# evaluacion booleana
results_df['Success'] = (results_df['DetectionPrediction'] == results_df['HasDetections']).astype(int)

results_df.head()

# EVALUAR EL MODELO
# vamos a medir el porcentaje de acierto. Sale que es de aprox 60%
results_df['Success'].mean()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10994549007507971, 'fraction_numerical': 0.01414172580479276, 'mean_word_length': 6.555167055167055, 'pattern_counts': {'":': 1, '<': 73, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '405523', 'license': 'MIT', 'n_tokens_mistral': 6875, 'n_tokens_neox': 6519, 'n_words': 1353, 'path': '0321_SupML_Alfredo Bescos.ipynb', 'repo_name': 'alfredo-bb/Microsoft-Malware-ML-Classification', 'size': '19446'}