Извлечение признаков — это процесс извлечения признаков (характеристик, свойств, атрибутов) из необработанных данных. Посмотрите примеры ниже.
Допустим, у нас есть такая переменная Timestamp:
Из этой переменной «Timestamp» мы можем извлечь год, месяц, день, час и название дня.
Теперь посмотрим на другой пример. Допустим, мы знаем имена пассажиров Титаника,
Из переменной «Имя» мы можем извлечь переменную «Название»,
Как видите, мы извлекли новые функции из необработанных данных. Конечно, мы можем извлечь больше функций. Единственное ограничение — наше воображение. Не обязательно всегда иметь смысл, просто попытайтесь извлечь новые функции. Возможно, вы не понимаете смысла вновь созданной функции, но на самом деле она имеет значение.
Давайте займемся кодированием!
import numpy as np import pandas as pd import seaborn as sns from datetime import date from matplotlib import pyplot as plt from statsmodels.stats.proportion import proportions_ztest pd.set_option('display.max_columns', None) pd.set_option('display.width', 500) def load(): data = pd.read_csv("titanic.csv") return data #Feature Extraction aims to reduce the number of features in a dataset by #creating new features from the existing ones (and then discarding the original #features). These new reduced set of features should then be able to summarize #most of the information contained in the original set of features. df = load() print(df.head()) ''' PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked 0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S 1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C 2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S 3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S 4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S '''
Извлечение двоичных признаков. Наша цель — сгенерировать новые двоичные переменные, такие как 0–1, True False или FLAG и BOOL, из существующих переменных.
#Let's try to create a new binary column which consists of 1 (Cabin is known) #and 0 (Cabin is unknown(NaN)) df["NEW_CABIN_BOOL"] = df["Cabin"].notnull().astype('int') print(df[['Cabin','NEW_CABIN_BOOL']].head()) ''' Cabin NEW_CABIN_BOOL 0 NaN 0 1 C85 1 2 NaN 0 3 C123 1 4 NaN 0 ''' #NOTE: Column name could be 'FLAG_NEW_CABIN' instead of 'NEW_CABIN_BOOL' #After creating our new column, let's analyze the relationship between #target(Survived) variable and this new column. print(df.groupby("NEW_CABIN_BOOL").agg({"Survived": "mean"})) ''' Survived NEW_CABIN_BOOL 0 0.299854 1 0.666667 '''
Как видите, если Cabin не NaN, то человек жив на 66%, в противном случае — на 29%. Похоже, что значение NEW_CABIN_BOOL влияет на столбец «Выживший». Нам необходимо проверить эту гипотезу.
Проверка гипотезы
Мы хотим проверить, оказывает ли наша новая переменная «NEW_CABIN_BOOL» существенное влияние на переменную «Выживший».
H0: NEW_CABIN_BOOL не оказывает существенного влияния на столбец «Выжившие».
HA: NEW_CABIN_BOOL оказывает значительное влияние на столбец «Выжившие».
Чтобы проверить альтернативную гипотезу, мы будем использовать функциюпропорции_ztest(). Он имеет два параметра: count и nobs.
Count покажет количество выживших людей, когда NEW_CABIN_BOOL равен 1 и 0. Nobs покажет количество строк (obsv), когда NEW_CABIN_BOOL = 1 и NEW_CABIN_BOOL = 0
А функция propotions_ztest() вернет нам test_stat и pvalue . Если значение p ниже критического значения (скажем, 0,05), то мы принимаем альтернативную гипотезу (HA).
test_stat, pvalue = proportions_ztest(count=[df.loc[df["NEW_CABIN_BOOL"] == 1, "Survived"].sum(), df.loc[df["NEW_CABIN_BOOL"] == 0, "Survived"].sum()], nobs=[df.loc[df["NEW_CABIN_BOOL"] == 1, "Survived"].shape[0], df.loc[df["NEW_CABIN_BOOL"] == 0, "Survived"].shape[0]]) #Since pvalue < 0.05 , we can accept that HA is true. In other words, #NEW_CABIN_BOOL has a significant effect on Survived column. print(f'Test stat: {test_stat:.4f}, P value: {pvalue:.4f}') # Test stat: 9.4597, P value: 0.0000
Теперь давайте попробуем извлечь еще один бинарный признак. Колонки Сибс и Парч связаны с количеством родственников человека. Итак, для человека, если Сибс+Парч = 0, то мы можем сказать, что он/она одинок,
df.loc[((df['SibSp'] + df['Parch']) > 0), "NEW_IS_ALONE"] = "NOT ALONE" df.loc[((df['SibSp'] + df['Parch']) == 0), "NEW_IS_ALONE"] = "ALONE" print(df.groupby("NEW_IS_ALONE").agg({"Survived": "mean"})) ''' Survived NEW_IS_ALONE ALONE 0.303538 NOT ALONE 0.505650 '''
Как видно, если человек не один, то он на 50% жив. Если человек один, то он/она жив на 30%. Таким образом, похоже, что одиночество или отсутствие человека может повлиять на нашу целевую переменную (Выжил). Поэтому нам нужно его протестировать:
H0: NEW_IS_ALONE не оказывает существенного влияния на столбец «Выжившие».
HA: NEW_IS_ALONE оказывает значительное влияние на столбец «Выжившие».
test_stat, pvalue = proportions_ztest(count=[df.loc[df["NEW_IS_ALONE"] == "ALONE", "Survived"].sum(), df.loc[df["NEW_IS_ALONE"] == "NOT ALONE", "Survived"].sum()], nobs=[df.loc[df["NEW_IS_ALONE"] == "ALONE", "Survived"].shape[0], df.loc[df["NEW_IS_ALONE"] == "NOT ALONE", "Survived"].shape[0]]) #Since pvalue < 0.05, we can accept HA. In other words, #being alone affects the person's survival probability. print(f'Test stat: {test_stat:.4f}, P value: {pvalue:.4f}') # Test stat: -6.0704, P value: 0.0000
Текст Извлечение функций:Мы попытаемся получить новые свойства из переменных, содержащих текст.
#Letter Count: Let's look at the number of letters in the 'Name' column #and create a new variable from there. df["NEW_NAME_LETTER_COUNT"] = df["Name"].str.len() #Word Count: Look at the word count in the 'Name' column. df["NEW_NAME_WORD_COUNT"] = df["Name"].apply(lambda x: len(str(x).split(" "))) print(df[['Name', 'NEW_NAME_LETTER_COUNT', 'NEW_NAME_WORD_COUNT']].head()) ''' Name NEW_NAME_LETTER_COUNT NEW_NAME_WORD_COUNT 0 Braund, Mr. Owen Harris 23 4 1 Cumings, Mrs. John Bradley (Florence Briggs Th... 51 7 2 Heikkinen, Miss. Laina 22 3 3 Futrelle, Mrs. Jacques Heath (Lily May Peel) 44 7 4 Allen, Mr. William Henry 24 4 ''' ########################################### #Extracting Special Titles ########################################### #Doctor: df["NEW_DR"] = df["Name"].apply( lambda x: len([x for x in x.split() if x.startswith("Dr")]) ) #As you can see, doctors are more likely to survive. However, #there are only 10 doctors, so we shouldn't be too confident to say #something for now. print(df.groupby("NEW_DR").agg({"Survived": ["mean", "count"]})) ''' Survived mean count NEW_DR 0 0.38252 881 1 0.50000 10 ''' #Miss, Mr, Mrs: #We will use RegEx to extact Miss, Mr and Mrs. #You can watch the video in the link to understand what is RegEx. #https://www.youtube.com/watch?v=rhzKDrUiJVk #We are looking for things like " Miss.", " Mr.", " Mrs." #If expand = False, it returns Series, #if it is True, it returns Dataframe. df["NEW_TITLE"] = df.Name.str.extract(" ([A-Za-z]+)\.", expand=False) #As can be seen, some titles have very high frequences such as #"Master", "Miss", "Mr", "Mrs" #Therefore, for example, we can use these titles having high counts #to fill the NaN values in the 'Age' column. print(df[["NEW_TITLE", "Survived", "Age"]].groupby(["NEW_TITLE"]).agg( {"Survived": "mean", "Age": ["count", "mean"]})) ''' Survived Age mean count mean NEW_TITLE Capt 0.000000 1 70.000000 Col 0.500000 2 58.000000 Countess 1.000000 1 33.000000 Don 0.000000 1 40.000000 Dr 0.428571 6 42.000000 Jonkheer 0.000000 1 38.000000 Lady 1.000000 1 48.000000 Major 0.500000 2 48.500000 Master 0.575000 36 4.574167 Miss 0.697802 146 21.773973 Mlle 1.000000 2 24.000000 Mme 1.000000 1 24.000000 Mr 0.156673 398 32.368090 Mrs 0.792000 108 35.898148 Ms 1.000000 1 28.000000 Rev 0.000000 6 43.166667 Sir 1.000000 1 49.000000 '''
Извлечение функций даты. Если у нас есть данные даты и времени, мы можем извлечь из них другие функции. Давайте посмотрим на файл Course_reviews.csv.
dff = pd.read_csv("course_reviews.csv") print(dff.head()) ''' Rating Timestamp Enrolled Progress Questions Asked Questions Answered 0 5.0 2021-02-05 07:45:55 2021-01-25 15:12:08 5.0 0.0 0.0 1 5.0 2021-02-04 21:05:32 2021-02-04 20:43:40 1.0 0.0 0.0 2 4.5 2021-02-04 20:34:03 2019-07-04 23:23:27 1.0 0.0 0.0 3 5.0 2021-02-04 16:56:28 2021-02-04 14:41:29 10.0 0.0 0.0 4 4.0 2021-02-04 15:00:24 2020-10-13 03:10:07 10.0 0.0 0.0 ''' #The dtype of 'Timestamp' is object. To manipulate a date, we need to save it #as datetime, therefore we will change the dtype. print(dff.info()) ''' RangeIndex: 4323 entries, 0 to 4322 Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Rating 4323 non-null float64 1 Timestamp 4323 non-null object 2 Enrolled 4323 non-null object 3 Progress 4323 non-null float64 4 Questions Asked 4323 non-null float64 5 Questions Answered 4323 non-null float64 dtypes: float64(4), object(2) ''' #With format parameter, we should write how date was formatted #Let's look at one of the dates: 2021-02-05 #It is saved as Year-month-day #So we should write this in format parameter. dff["Timestamp"] = pd.to_datetime(dff["Timestamp"], format="%Y-%m-%d") #Now dtype of 'Timestamp' variable is datetime. print(dff.dtypes) ''' Rating float64 Timestamp datetime64[ns] Enrolled object Progress float64 Questions Asked float64 Questions Answered float64 dtype: object ''' #Now we can easily manipulate and extract new features. #year dff["year"] = dff["Timestamp"].dt.year # month dff["month"] = dff["Timestamp"].dt.month # year diff dff["year_diff"] = date.today().year - dff["Timestamp"].dt.year # month diff dff["month_diff"] = ( (date.today().year - dff["Timestamp"].dt.year) * 12 + date.today().month - dff["Timestamp"].dt.month) # day name dff["day_name"] = dff["Timestamp"].dt.day_name() print(dff.head()) ''' Rating Timestamp Enrolled Progress Questions Asked Questions Answered year month year_diff month_diff day_name 0 5.0 2021-02-05 07:45:55 2021-01-25 15:12:08 5.0 0.0 0.0 2021 2 2 30 Friday 1 5.0 2021-02-04 21:05:32 2021-02-04 20:43:40 1.0 0.0 0.0 2021 2 2 30 Thursday 2 4.5 2021-02-04 20:34:03 2019-07-04 23:23:27 1.0 0.0 0.0 2021 2 2 30 Thursday 3 5.0 2021-02-04 16:56:28 2021-02-04 14:41:29 10.0 0.0 0.0 2021 2 2 30 Thursday 4 4.0 2021-02-04 15:00:24 2020-10-13 03:10:07 10.0 0.0 0.0 2021 2 2 30 Thursday '''
Взаимодействие функций.Мы получим новую переменную, попробовав все комбинации с другими переменными в рамках определенной логики или без какой-либо логики.
#We are working with the titanic dataset. df = load() df.head() #The smaller the product of age and pclass, the higher the probability #of survival. so we can generate a new variable from here. df["NEW_AGE_PCLASS"] = df["Age"] * df["Pclass"] #How many people from that family are on the ship? This could be an #important variable. df["NEW_FAMILY_SIZE"] = df["SibSp"] + df["Parch"] + 1 #We can create a new variable according to age range and gender. df.loc[(df["Sex"] == "male") & (df["Age"] <= 21), "NEW_SEX_CAT"] = "youngmale" df.loc[ (df["Sex"] == "male") & (df["Age"] > 21) & (df["Age"] < 50), "NEW_SEX_CAT"] = "maturemale" df.loc[(df["Sex"] == "male") & (df["Age"] >= 50), "NEW_SEX_CAT"] = "seniormale" df.loc[(df["Sex"] == "female") & (df["Age"] <= 21), "NEW_SEX_CAT"] = "youngfemale" df.loc[(df["Sex"] == "female") & (df["Age"] > 21) & (df["Age"] < 50), "NEW_SEX_CAT"] = "maturefemale" df.loc[(df["Sex"] == "female") & (df["Age"] >= 50), "NEW_SEX_CAT"] = "seniorfemale" print(df.head()) ''' PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked NEW_AGE_PCLASS NEW_FAMILY_SIZE NEW_SEX_CAT 0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S 66.0 2 maturemale 1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C 38.0 2 maturefemale 2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S 78.0 1 maturefemale 3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S 35.0 2 maturefemale 4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S 105.0 1 maturemale ''' #We can examine the survival probabilities with groupby(). print(df.groupby("NEW_SEX_CAT")["Survived"].mean()) ''' NEW_SEX_CAT maturefemale 0.774194 maturemale 0.199288 seniorfemale 0.909091 seniormale 0.134615 youngfemale 0.678571 youngmale 0.250000 Name: Survived, dtype: float64 '''
Спасибо, что прочитали…