Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)! Outras formas de apoiar o HackTricks: * Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com) * Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family) * **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.** * **Compartilhe suas técnicas de hacking enviando PRs para os repositórios do GitHub** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
# Tipos básicos de dados possíveis Os dados podem ser **contínuos** (**infinitos** valores) ou **categóricos** (nominais) onde a quantidade de valores possíveis é **limitada**. ## Tipos Categóricos ### Binário Apenas **2 valores possíveis**: 1 ou 0. No caso de um conjunto de dados onde os valores estão em formato de string (por exemplo, "True" e "False"), você atribui números a esses valores com: ```python dataset["column2"] = dataset.column2.map({"T": 1, "F": 0}) ``` ### **Ordinal** Os **valores seguem uma ordem**, como em: 1º lugar, 2º lugar... Se as categorias forem strings (como: "iniciante", "amador", "profissional", "especialista"), você pode mapeá-las para números como vimos no caso binário. ```python column2_mapping = {'starter':0,'amateur':1,'professional':2,'expert':3} dataset['column2'] = dataset.column2.map(column2_mapping) ``` * Para **colunas alfabéticas** você pode ordená-las mais facilmente: ```python # First get all the uniq values alphabetically sorted possible_values_sorted = dataset.column2.sort_values().unique().tolist() # Assign each one a value possible_values_mapping = {value:idx for idx,value in enumerate(possible_values_sorted)} dataset['column2'] = dataset.column2.map(possible_values_mapping) ``` ### **Cíclico** Parece **como valor ordinal** porque há uma ordem, mas isso não significa que um é maior que o outro. Além disso, a **distância entre eles depende da direção** que você está contando. Exemplo: Os dias da semana, domingo não é "maior" que segunda-feira. * Existem **diferentes maneiras** de codificar características cíclicas, algumas podem funcionar apenas com alguns algoritmos. **Em geral, a codificação dummy pode ser usada** ```python column2_dummies = pd.get_dummies(dataset.column2, drop_first=True) dataset_joined = pd.concat([dataset[['column2']], column2_dummies], axis=1) ``` ### **Datas** Datas são **variáveis contínuas**. Podem ser vistas como **cíclicas** (porque se repetem) **ou** como **variáveis ordinais** (porque um momento é maior que o anterior). * Geralmente, datas são usadas como **índice** ```python # Transform dates to datetime dataset["column_date"] = pd.to_datetime(dataset.column_date) # Make the date feature the index dataset.set_index('column_date', inplace=True) print(dataset.head()) # Sum usage column per day daily_sum = dataset.groupby(df_daily_usage.index.date).agg({'usage':['sum']}) # Flatten and rename usage column daily_sum.columns = daily_sum.columns.get_level_values(0) daily_sum.columns = ['daily_usage'] print(daily_sum.head()) # Fill days with 0 usage idx = pd.date_range('2020-01-01', '2020-12-31') daily_sum.index = pd.DatetimeIndex(daily_sum.index) df_filled = daily_sum.reindex(idx, fill_value=0) # Fill missing values # Get day of the week, Monday=0, Sunday=6, and week days names dataset['DoW'] = dataset.transaction_date.dt.dayofweek # do the same in a different way dataset['weekday'] = dataset.transaction_date.dt.weekday # get day names dataset['day_name'] = dataset.transaction_date.apply(lambda x: x.day_name()) ``` ### Multi-categoria/nominal **Mais de 2 categorias** sem ordem relacionada. Use `dataset.describe(include='all')` para obter informações sobre as categorias de cada recurso. * Uma **string de referência** é uma **coluna que identifica um exemplo** (como o nome de uma pessoa). Isso pode ser duplicado (porque 2 pessoas podem ter o mesmo nome), mas a maioria será única. Esses dados são **inúteis e devem ser removidos**. * Uma **coluna chave** é usada para **vincular dados entre tabelas**. Neste caso, os elementos são únicos. Esses dados são **inúteis e devem ser removidos**. Para **codificar colunas de multi-categoria em números** (para que o algoritmo de ML as entenda), **codificação dummy é usada** (e **não codificação one-hot** porque não **evita multicolinearidade perfeita**). Você pode obter uma **coluna de multi-categoria codificada em one-hot** com `pd.get_dummies(dataset.column1)`. Isso transformará todas as classes em recursos binários, criando assim **uma nova coluna por classe possível** e atribuirá 1 **valor Verdadeiro a uma coluna**, e o restante será falso. Você pode obter uma **coluna de multi-categoria codificada em dummy** com `pd.get_dummies(dataset.column1, drop_first=True)`. Isso transformará todas as classes em recursos binários, criando assim **uma nova coluna por classe possível menos uma**, já que **as últimas 2 colunas serão refletidas como "1" ou "0" na última coluna binária criada**. Isso evitará multicolinearidade perfeita, reduzindo as relações entre colunas. # Colinear/Multicolinearidade Colinear aparece quando **2 recursos estão relacionados um com o outro**. Multicolinearidade aparece quando são mais de 2. Em ML **você quer que seus recursos estejam relacionados com os resultados possíveis, mas você não quer que eles estejam relacionados entre si**. É por isso que a **codificação dummy mistura as duas últimas colunas** disso e **é melhor do que a codificação one-hot**, que não faz isso, criando uma relação clara entre todos os novos recursos da coluna de multi-categoria. VIF é o **Fator de Inflação de Variância** que **mede a multicolinearidade dos recursos**. Um valor **acima de 5 significa que um dos dois ou mais recursos colineares deve ser removido**. ```python from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant #dummies_encoded = pd.get_dummies(dataset.column1, drop_first=True) onehot_encoded = pd.get_dummies(dataset.column1) X = add_constant(onehot_encoded) # Add previously one-hot encoded data print(pd.Series([variance_inflation_factor(X.values,i) for i in range(X.shape[1])], index=X.columns)) ``` # Desequilíbrio Categórico Isso ocorre quando **não há a mesma quantidade de cada categoria** nos dados de treinamento. ```python # Get statistic of the features print(dataset.describe(include='all')) # Get an overview of the features print(dataset.info()) # Get imbalance information of the target column print(dataset.target_column.value_counts()) ``` Em um desequilíbrio, sempre há uma **classe ou classes majoritárias** e uma **classe ou classes minoritárias**. Existem 2 maneiras principais de corrigir esse problema: * **Undersampling**: Remoção aleatória de dados da classe majoritária para que ela tenha o mesmo número de amostras que a classe minoritária. ```python from imblearn.under_sampling import RandomUnderSampler rus = RandomUserSampler(random_state=1337) X = dataset[['column1', 'column2', 'column3']].copy() y = dataset.target_column X_under, y_under = rus.fit_resample(X,y) print(y_under.value_counts()) #Confirm data isn't imbalanced anymore ``` * **Oversampling**: Gerar mais dados para a classe minoritária até que ela tenha tantas amostras quanto a classe majoritária. ```python from imblearn.under_sampling import RandomOverSampler ros = RandomOverSampler(random_state=1337) X = dataset[['column1', 'column2', 'column3']].copy() y = dataset.target_column X_over, y_over = ros.fit_resample(X,y) print(y_over.value_counts()) #Confirm data isn't imbalanced anymore ``` Você pode usar o argumento **`sampling_strategy`** para indicar a **porcentagem** que deseja **subamostrar ou sobreamostrar** (**por padrão é 1 (100%)**, o que significa igualar o número de classes minoritárias com as classes majoritárias) {% hint style="info" %} Subamostragem ou Sobreamostragem não são perfeitas se você obter estatísticas (com `.describe()`) dos dados sub/sobreamostrados e compará-los com os originais, você verá **que eles mudaram.** Portanto, sobreamostragem e subamostragem estão modificando os dados de treinamento. {% endhint %} ## Sobreamostragem com SMOTE **SMOTE** geralmente é uma **maneira mais confiável de sobreamostrar os dados**. ```python from imblearn.over_sampling import SMOTE # Form SMOTE the target_column need to be numeric, map it if necessary smote = SMOTE(random_state=1337) X_smote, y_smote = smote.fit_resample(dataset[['column1', 'column2', 'column3']], dataset.target_column) dataset_smote = pd.DataFrame(X_smote, columns=['column1', 'column2', 'column3']) dataset['target_column'] = y_smote print(y_smote.value_counts()) #Confirm data isn't imbalanced anymore ``` # Categorias Raramente Ocorrentes Imagine um conjunto de dados onde uma das classes alvo **ocorre muito poucas vezes**. Isso é como o desequilíbrio de categoria da seção anterior, mas a categoria raramente ocorrente está ocorrendo ainda menos que a "classe minoritária" naquele caso. Os métodos de **oversampling** e **undersampling** **puros** também podem ser usados aqui, mas geralmente essas técnicas **não fornecerão resultados realmente bons**. ## Pesos Em alguns algoritmos é possível **modificar os pesos dos dados alvo** para que alguns deles tenham por padrão mais importância ao gerar o modelo. ```python weights = {0: 10 1:1} #Assign weight 10 to False and 1 to True model = LogisticRegression(class_weight=weights) ``` Você pode **misturar os pesos com técnicas de sobreamostragem/subamostragem** para tentar melhorar os resultados. ## PCA - Análise de Componentes Principais É um método que ajuda a reduzir a dimensionalidade dos dados. Vai **combinar diferentes características** para **reduzir a quantidade** delas, gerando **características mais úteis** (_menos computação é necessária_). As características resultantes não são compreensíveis por humanos, então também **anonimiza os dados**. # Categorias de Rótulos Incongruentes Os dados podem ter erros devido a transformações mal-sucedidas ou simplesmente por erro humano ao escrever os dados. Portanto, você pode encontrar o **mesmo rótulo com erros de ortografia**, diferentes **capitalizações**, **abreviações** como: _BLUE, Blue, b, bule_. Você precisa corrigir esses erros de rótulo dentro dos dados antes de treinar o modelo. Você pode limpar esses problemas tornando tudo minúsculo e mapeando rótulos mal escritos para os corretos. É muito importante verificar que **todos os dados que você possui estão corretamente rotulados**, porque, por exemplo, um erro de ortografia nos dados, ao codificar as classes em dummies, gerará uma nova coluna nas características finais com **consequências ruins para o modelo final**. Este exemplo pode ser detectado facilmente codificando uma coluna em one-hot e verificando os nomes das colunas criadas. # Dados Ausentes Alguns dados do estudo podem estar ausentes. Pode acontecer de alguns dados completamente aleatórios estarem ausentes por algum erro. Esse tipo de dado é **Missing Completely at Random** (**MCAR**). Pode ser que alguns dados aleatórios estejam ausentes, mas há algo que torna mais provável que alguns detalhes específicos estejam ausentes, por exemplo, é mais frequente que homens revelem sua idade, mas não mulheres. Isso é chamado **Missing at Random** (**MAR**). Finalmente, pode haver dados **Missing Not at Random** (**MNAR**). O valor dos dados está diretamente relacionado com a probabilidade de ter os dados. Por exemplo, se você quer medir algo embaraçoso, quanto mais embaraçosa for a pessoa, menos provável é que ela compartilhe isso. As **duas primeiras categorias** de dados ausentes podem ser **ignoráveis**. Mas a **terceira** requer considerar **apenas partes dos dados** que não são impactadas ou tentar **modelar os dados ausentes de alguma forma**. Uma maneira de descobrir sobre dados ausentes é usar a função `.info()`, pois ela indicará o **número de linhas, mas também o número de valores por categoria**. Se alguma categoria tem menos valores do que o número de linhas, então há alguns dados ausentes: ```bash # Get info of the dataset dataset.info() # Drop all rows where some value is missing dataset.dropna(how='any', axis=0).info() ``` Geralmente é recomendado que se um recurso estiver **ausente em mais de 20%** do conjunto de dados, a **coluna deve ser removida:** ```bash # Remove column dataset.drop('Column_name', axis='columns', inplace=True) dataset.info() ``` {% hint style="info" %} Observe que **nem todos os valores ausentes estão faltando no conjunto de dados**. É possível que valores ausentes tenham sido atribuídos o valor "Desconhecido", "n/a", "", -1, 0... Você precisa verificar o conjunto de dados (usando `dataset.column`_`name.value`_`counts(dropna=False)` para verificar os possíveis valores). {% endhint %} Se alguns dados estão ausentes no conjunto de dados (e não são muitos), você precisa encontrar a **categoria dos dados ausentes**. Para isso, basicamente precisa saber se os **dados ausentes são aleatórios ou não**, e para isso você precisa descobrir se os **dados ausentes estavam correlacionados com outros dados** do conjunto de dados. Para descobrir se um valor ausente está correlacionado com outra coluna, você pode criar uma nova coluna que coloque 1s e 0s se o dado está ausente ou não e então calcular a correlação entre eles: ```bash # The closer it's to 1 or -1 the more correlated the data is # Note that columns are always perfectly correlated with themselves. dataset[['column_name', 'cloumn_missing_data']].corr() ``` Se você decidir ignorar os dados ausentes, ainda precisará decidir o que fazer com eles: Você pode **remover as linhas** com dados ausentes (os dados de treino para o modelo serão menores), pode **remover a característica** completamente, ou poderia **modelá-la**. Você deve **verificar a correlação entre a característica ausente com a coluna alvo** para ver quão importante essa característica é para o alvo, se for realmente **pequena** você pode **descartá-la ou preenchê-la**. Para preencher dados **contínuos** ausentes, você poderia usar: a **média**, a **mediana** ou usar um algoritmo de **imputação**. O algoritmo de imputação pode tentar usar outras características para encontrar um valor para a característica ausente: ```python from sklearn.impute import KNNImputer X = dataset[['column1', 'column2', 'column3']] y = dataset.column_target # Create the imputer that will fill the data imputer = KNNImputer(n_neightbors=2, weights='uniform') X_imp = imputer.fit_transform(X) # Check new data dataset_imp = pd.DataFrame(X_imp) dataset.columns = ['column1', 'column2', 'column3'] dataset.iloc[10:20] # Get some indexes that contained empty data before ``` Para preencher dados categóricos, primeiro você precisa pensar se há algum motivo pelo qual os valores estão ausentes. Se for por **escolha dos usuários** (eles não quiseram fornecer os dados), talvez você possa **criar uma nova categoria** indicando isso. Se for devido a erro humano, você pode **remover as linhas** ou a **característica** (verifique os passos mencionados antes) ou **preenchê-la com a moda, a categoria mais usada** (não recomendado). # Combinando Características Se você encontrar **duas características** que estão **correlacionadas** entre si, geralmente você deve **descartar** uma delas (aquela que é menos correlacionada com o alvo), mas você também pode tentar **combiná-las e criar uma nova característica**. ```python # Create a new feautr combining feature1 and feature2 dataset['new_feature'] = dataset.column1/dataset.column2 # Check correlation with target column dataset[['new_feature', 'column1', 'column2', 'target']].corr()['target'][:] # Check for collinearity of the 2 features and the new one X = add_constant(dataset[['column1', 'column2', 'target']]) # Calculate VIF pd.Series([variance_inflation_factor(X.values, i) for i in range(X.shape[1])], index=X.columns) ```
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)! Outras formas de apoiar o HackTricks: * Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com) * Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos * **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.** * **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).