Когато изграждаме модели за машинно обучение, често тези характеристики, с които разполагаме в извадката, не са достатъчни и в същото време данните позволяват да конструираме нови характеристики.
Какви резултати ще получим от модела за машинно обучение зависи от това какви входни данни ще му подадем. Конструирането на нови променливи е един ефективен начин, чрез който можем да подобрим работата на моделите, като за тази цел е необходима и известна доза креативност.
За да можем да конструираме нови характеристики са нужни знания за конкретната предметна област – да знаем от къде идват данните и какво би било полезно, за да можем да решим конкретната бизнес задача. Нужно е да се премине през множество експерименти по време на този етап докато се създадат най-релевантните характеристики.
В тази статия ще ви запозная с 5 начина, чрез които можете да конструирате нови променливи, като извадката, която ще използваме в примерите, се състои от 1076 реда и 8 колони, съдържащи данни за покупки от онлайн магазин.
Първите 5 реда от таблицата изглеждат по следния начин:
InvoiceNo | StockCode | Description | Quantity | InvoiceDate | UnitPrice | CustomerID | Country |
---|---|---|---|---|---|---|---|
536423 | 84029E | RED WOOLLY HOTTIE HEART. | 8 | 12/1/2010 12:08 | 3.75 | 18085 | United Kingdom |
536532 | 22962 | JAM JAR WITH PINK LID | 12 | 12/1/2010 13:24 | 0.85 | 12433 | Norway |
536401 | 84625A | PINK NEW BAROQUECANDLESTICK CANDLE | 3 | 12/1/2010 11:21 | 2.95 | 15862 | United Kingdom |
536784 | 21499 | BLUE POLKADOT WRAP | 50 | 12/2/2010 15:20 | 0.34 | 15061 | United Kingdom |
C536822 | 21430 | SET/3 RED GINGHAM ROSE STORAGE BOX | -1 | 12/2/2010 17:19 | 3.75 | 14625 | United Kingdom |
Цялостния пример можете да изтеглите от тук.
Кои са различните начини за създаване на нови променливи?
Конструирането на нови променливи оказва голямо влияние върху работата на модела за машинно обучение и също позволява по-качествено да анализираме данните, с които разполагаме.
В примерите по-надолу ще видим как можем да:
- създаваме изчислени колони чрез комбиниране на 2 или повече характеристики
- разделяме съществуващи променливи
- извличаме допълнителни данни от съществуващи характеристики
- създаваме бинарни променливи
- създаваме нови характеристики чрез агрегиране на данните
Комбиниране на характеристики
Често ако комбинираме няколко характеристики можем да извлечем по-важна информация от данните, отколкото ако ги използваме индивидуално. Можем например да създадем изчислени колони ако данните, с които разполагаме, са числови, или да обединим категорийни променливи в една.
В нашия пример ще видим един от начините за създаване на нова променлива, която да съдържа данни за общата стойност на продажбите.
Поръчките, които са отказани, в извадката присъстват с отрицателна стойност за количеството продукти.
Новата променлива се изчислява като произведение между количество продадени стоки и единична цена. Ако дадена поръчка е отказана, общите продажби за нея са 0, тъй като не трябва да включваме стойността на отказаните стоки в тях.
# Създаване на калкулирана променлива за общите продажби
df['TotalSales'] = df.apply(lambda x: x.Quantity * x.UnitPrice
if x.Quantity > 0
else 0,
axis=1)
Ето така изглеждат 5 случайни реда от новата характеристика:
TotalSales |
---|
10.2 |
0.65 |
14.75 |
3.3 |
0.0 |
Разделяне на съществуващи променливи
Понякога в извадката има определени характеристики, в които се съдържа допълнителна информация, отнасяща се до нещо важно, което би помогнало при определяне на целевата променлива.
В нашия пример характеристиката, която съдържа описанието на продуктите в онлайн магазина, има в себе си допълнителна информация, отнасяща се до цвета на артикула. Във фрагментите от код по-надолу ще я разделим, като в новата характеристика ще съхраним наименованията на цветовете за всеки продукт. За тези артикули, при които не е посочен конкретен цвят в описанието, ще зададем \’NOT SPECIFIED\’
# Създаване на променлива за цветовете, които ще се използват в регулярния израз
colors = 'WHITE|BLACK|RED|YELLOW|GREEN|ORANGE|PURPLE|BLUE|BROWN|PINK|GREY|GRAY|IVORY|CHOC|SILVER|CREAM'
# Създаване на празен масив, в който ще се съхраняват наименования на цветовете
color_col = []
# Откриване на наименованията на цветовете в описанието на продуктите
for index, row in df[['Description']].iterrows():
find_color = re.search(r'(^|\s+)(?:' + colors + ')(\s+|\/(?:' + colors + ')\s+|\+(?:' + colors + ')\s+|$|)', row['Description'])
if find_color:
color_col.append(find_color.group(0).strip())
else:
color_col.append('NOT SPECIFIED')
# Запазване на наименованията на цветовете в нова променлива
df['Color'] = color_col
Color | Count |
---|---|
RED | 388 |
WHITE | 187 |
PINK | 154 |
BLUE | 100 |
BLACK | 73 |
GREEN | 66 |
GREY | 22 |
NOT SPECIFIED | 19 |
YELLOW | 14 |
ORANGE | 11 |
IVORY | 10 |
PURPLE | 9 |
BLACK/BLUE | 5 |
CREAM | 3 |
BROWN | 3 |
BLACK+SILVER | 2 |
RED/WHITE | 2 |
IVORY/RED | 2 |
CHOC+BLUE | 1 |
BLUE/CREAM | 1 |
PINK/WHITE | 1 |
PINK/YELLOW | 1 |
BLACK+WHITE | 1 |
WHITE/RED | 1 |
Можете да видите, че най-много закупени продукти са с червен цвят – 388 на брой, а най-малко са тези, където има комбинация от 2 цвята – по 1-2 закупени продукти.
След като сме запазили наименованията на цветовете в новата променлива, те съвсем спокойно могат да бъдат премахнати от колоната с описанието.
# Премахване на наименованията на цветовете от колоната с описанията
for i in df['Color'].values:
df['Description'] = df['Description'].apply(lambda x: x.replace(i,''))
Данните след обработката изглеждат по следния начин:
Description | Color |
---|---|
METAL LANTERN | WHITE |
JUMBO BAG POLKADOT | PINK |
FLOCK LOVE HEART PHOTO FRAME | RED |
LARGE HONEYCOMB PAPER BELL | WHITE |
KITCHEN SCALES | RED |
Извличане на допълнителни данни от съществуващи променливи
Понякога данните може да не са в идеалния формат и да е по-добре, ако са представени по по-различен начин. Например ако имаме дата или ЕГН, бихме могли да извлечем от тях допълнителни данни като месец, година, възраст и т.н. и да ги съхраним в нови променливи.
Точно това ще направим и в примера по-надолу. Имаме колона, съдържаща дата на фактура, от която можем да извлечем данни за ден, месец, година и час.
# Смяна на типа на колоната InvoiceDate в datetime
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
# Извличане на ден, месец, година и час от дата и запазване в отделни променливи
df['Day'] = df['InvoiceDate'].dt.day
df['Month'] = df['InvoiceDate'].dt.month
df['Year'] = df['InvoiceDate'].dt.year
df['Time'] = df['InvoiceDate'].dt.time
Day | Month | Year | Time |
---|---|---|---|
2 | 12 | 2010 | 11:21:00 |
1 | 12 | 2010 | 16:21:00 |
1 | 12 | 2010 | 11:45:00 |
1 | 12 | 2010 | 11:49:00 |
2 | 12 | 2010 | 14:40:00 |
Като имаме вече отделни променливи за ден, месец, година и час, можем да ги използваме по време на етапа на предварителен анализ, за да добием по-добра представа за данните.
Създаване на бинарни променливи
Можем да отделим ключова информация за данните, като създаваме бинарни променливи. По този начин помагаме на модела да намери това, което е по-важно.
В нашия пример ще създадем 2 бинарни променливи.
Първата е IsLocal, която показва дали купувачите, които поръчват от онлайн магазина, са местни жители (живеят във Великобритания) или са чуждестранни (живеят извън Великобритания).
# Създаване на променлива за местни купувачи и такива в чужбина
df['IsLocal'] = df.apply(lambda x: 1 if x.Country == 'United Kingdom' else 0, axis = 1)
Новата променлива изглежда по следния начин:
IsLocal |
---|
1 |
1 |
1 |
1 |
0 |
Другата нова променлива – IsCancelled е свързана с покупките на клиентите и показва дали дадена поръчка е отказана или не.
# Създаване на променлива за отказани поръчки
df['IsCancelled'] = df['InvoiceNo'].apply(lambda x: 1 if 'C' in x else 0 )
Променливата изглежда така:
IsCancelled |
---|
0 |
0 |
0 |
0 |
1 |
Агрегиране
Можем също така да създадем и нови променливи, като групираме и агрегираме данни във вече съществуващи. В библиотеката Pandas има интерфейс за изчисления с агрегиращи функции, който работи на същия принцип като при базите данни.
В нашия пример ще създадем 4 нови характеристики, като ще групираме данните по CustomerID и по InvoiceNo.
Първата – NumOrders ще я групираме само на ниво CustomerID, за да бъде изчислен общият брой фактури за всеки отделен клиент.
# Изчисляване на броя поръчки
df_orders = df.groupby('CustomerID', as_index=False)['InvoiceNo'].count()
# Преименуване на колоната
df_orders.rename(columns={'InvoiceNo': 'NumOrders'}, inplace=True)
# Обединяване на df_orders с оригиналния DataFrame
df = pd.merge(df, df_orders, on='CustomerID')
Останалите 3 нови променливи – TotalOrdersAmount, MinOrderAmount и MaxOrderAmount ще групираме по 2 колони – CustomerID и InvoiceNo, за да можем да изчислим общата сума на всяка фактура за конкретен клиент, както и неговата най-ниска и най-висока по стойност поръчка.
# Групиране на данните по CustomerID и InvoiceNo и използване на агрегиращи функции
df_temp = df.groupby(by=['CustomerID','InvoiceNo'], as_index=False)['TotalSales'].agg(['sum', 'min', 'max'])
# Смяна на имената на колоните
df_temp.rename(columns={'sum':'TotalOrdersAmount', 'min':'MinOrderAmount', 'max':'MaxOrderAmount'}, inplace=True)
# Нулиране на индекса
df_temp.reset_index(inplace=True)
# Обединяване на df_temp с оригиналния DataFrame
df = df.merge(df_temp, left_on=['CustomerID','InvoiceNo'], right_on=['CustomerID','InvoiceNo'])
Новите характеристики изглеждат по следния начин:
CustomerID | InvoiceNo | NumOrders | TotalOrdersAmount | MinOrderAmount | MaxOrderAmount |
---|---|---|---|---|---|
15574 | 536796 | 25 | 80.48 | 0.29 | 8.5 |
17802 | 536747 | 5 | 34.35 | 3.75 | 12.6 |
16244 | 536638 | 20 | 310 | 10.2 | 22.5 |
15363 | 536698 | 3 | 50.45 | 14.75 | 19.8 |
14307 | 536408 | 11 | 176.85 | 10.2 | 22.5 |
Тези променливи не трябва да се използват като входни данни за моделите, тъй като има много силна зависимост (корелация) между тях и останалите характеристики. Те са предназначени за използване в процеса на първоначален анализ, като извличаме описателни статистики или търсим отговори на конкретни бизнес въпроси. Благодарение на тях можем да добием по-добра представа за данните, с които разполагаме.
Извод
Като създаваме нови характеристики можем открием определени зависимости в данните и да получим по-точни резултати от моделите. По какъв начин ще конструираме новите характеристики зависи от данните, които имаме, както и от конкретната бизнес задача, която се опитваме да решим.
Искате да научите повече за машинното обучение?
Включете се в курса по машинно обучение и анализ на данни с Python.
Автор: Десислава Христова