《Kaggle教程 特征工程》系列课程目录
Kaggle教程 特征工程1 Baseline-Model
Kaggle教程 特征工程2 Categorical Encodings
Kaggle教程 特征工程3 Feature Generation
Kaggle教程 特征工程4 Feature Selection
1. 介绍
从原始数据创建新特性是改进模型的最佳方法之一。例如,您可以计算最后一周的项目总数和筹款周期的持续时间。您创建的功能对于每个数据集都是不同的,因此需要一点创造力和实验。我们在这里有点受限,因为我们只处理一个表。通常,您可以访问多个具有相关数据的表,您可以使用这些数据来创建新特性。
但是您仍然可以看到如何使用分类特性创建新特性,以及一些生成数值特性的示例。下面是加载数据并执行您已经看到的功能工程的代码。
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()
from sklearn.preprocessing import LabelEncoder
ks = pd.read_csv('../input/kickstarter-projects/ks-projects-201801.csv',
parse_dates=['deadline', 'launched'])
# Drop live projects
ks = ks.query('state != "live"')
# Add outcome column, "successful" == 1, others are 0
ks = ks.assign(outcome=(ks['state'] == 'successful').astype(int))
# Timestamp features
ks = ks.assign(hour=ks.launched.dt.hour,
day=ks.launched.dt.day,
month=ks.launched.dt.month,
year=ks.launched.dt.year)
# Label encoding
cat_features = ['category', 'currency', 'country']
encoder = LabelEncoder()
encoded = ks[cat_features].apply(encoder.fit_transform)
data_cols = ['goal', 'hour', 'day', 'month', 'year', 'outcome']
baseline_data = ks[data_cols].join(encoded)
Interactions (相互作用)
创建新特性的最简单方法之一是组合分类变量。例如,如果一个记录有国家“CA”和类别“Music”,您可以创建一个新值“CA_Music”。这是一个新的范畴特征,它可以提供有关范畴变量之间相互关系的信息。这种类型的特性通常称为交互。通常,您将从所有类别特性对构建交互特性。你也可以从三个或更多的功能中进行交互,但是你会得到递减的回报。
pandas允许我们像普通的Python字符串一样简单地添加字符串列。
interactions = ks['category'] + "_" + ks['country']
print(interactions.head(10))
'''
0 Poetry_GB
1 Narrative Film_US
2 Narrative Film_US
3 Music_US
4 Film & Video_US
5 Restaurants_US
6 Food_US
7 Drinks_US
8 Product Design_US
9 Documentary_US
dtype: object
'''
然后,标签编码交互特性并将其添加到数据中。
label_enc = LabelEncoder()
data_interaction = baseline_data.assign(category_country=label_enc.fit_transform(interactions))
data_interaction.head()
goal | hour | day | month | year | outcome | category | currency | country | category_country | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1000.0 | 12 | 11 | 8 | 2015 | 0 | 108 | 5 | 9 | 1900 |
1 | 30000.0 | 4 | 2 | 9 | 2017 | 0 | 93 | 13 | 22 | 1630 |
2 | 45000.0 | 0 | 12 | 1 | 2013 | 0 | 93 | 13 | 22 | 1630 |
3 | 5000.0 | 3 | 17 | 3 | 2012 | 0 | 90 | 13 | 22 | 1595 |
4 | 19500.0 | 8 | 4 | 7 | 2015 | 0 | 55 | 13 | 22 | 979 |
在下一个练习中,您将为所有类别特性对构建交互术语。
上周的项目数量
首先,我将向您展示如何计算每个记录在前一周启动的项目数量。我将在一个系列中使用.rolling方法,并将“started”列作为索引。我将使用ks创建这个系列。推出作为指数和ks。索引为值,然后对时间排序。使用时间序列作为索引,我们可以根据小时、天、周等定义滚动窗口的大小。
# First, create a Series with a timestamp index
launched = pd.Series(ks.index, index=ks.launched, name="count_7_days").sort_index()
launched.head(20)
'''
launched
1970-01-01 01:00:00 94579
1970-01-01 01:00:00 319002
1970-01-01 01:00:00 247913
1970-01-01 01:00:00 48147
1970-01-01 01:00:00 75397
1970-01-01 01:00:00 2842
1970-01-01 01:00:00 273779
2009-04-21 21:02:48 169268
2009-04-23 00:07:53 322000
2009-04-24 21:52:03 138572
2009-04-25 17:36:21 325391
2009-04-27 14:10:39 122662
2009-04-28 13:55:41 213711
2009-04-29 02:04:21 345606
2009-04-29 02:58:50 235255
2009-04-29 04:37:37 98954
2009-04-29 05:26:32 342226
2009-04-29 06:43:44 275091
2009-04-29 13:52:03 284115
2009-04-29 22:08:13 32898
Name: count_7_days, dtype: int64
'''
有7个项目的启动日期显然是错误的,但我们将忽略它们。同样,这是您在清理数据时需要处理的事情,但它不是这个迷你课程的重点。
使用timeseries索引,可以使用.rolling选择时间段作为窗口。例如,launched.rolling(‘7d’)创建一个包含前7天所有数据的滚动窗口。该窗口包含当前记录,因此如果我们想要计算所有以前的项目而不是当前的项目,我们需要减去1。我们将绘制结果以确保它看起来是正确的。
count_7_days = launched.rolling('7d').count() - 1
print(count_7_days.head(20))
# Ignore records with broken launch dates
plt.plot(count_7_days[7:]);
plt.title("Competitions in the last 7 days");
'''
launched
1970-01-01 01:00:00 0.0
1970-01-01 01:00:00 1.0
1970-01-01 01:00:00 2.0
1970-01-01 01:00:00 3.0
1970-01-01 01:00:00 4.0
1970-01-01 01:00:00 5.0
1970-01-01 01:00:00 6.0
2009-04-21 21:02:48 0.0
2009-04-23 00:07:53 1.0
2009-04-24 21:52:03 2.0
2009-04-25 17:36:21 3.0
2009-04-27 14:10:39 4.0
2009-04-28 13:55:41 5.0
2009-04-29 02:04:21 5.0
2009-04-29 02:58:50 6.0
2009-04-29 04:37:37 7.0
2009-04-29 05:26:32 8.0
2009-04-29 06:43:44 9.0
2009-04-29 13:52:03 10.0
2009-04-29 22:08:13 11.0
Name: count_7_days, dtype: float64
'''
现在我们有了计数,我们需要调整索引,以便与其他训练数据连接。
count_7_days.index = launched.values
count_7_days = count_7_days.reindex(ks.index)
count_7_days.head(10)
'''
0 1409.0
1 957.0
2 739.0
3 907.0
4 1429.0
5 1284.0
6 1119.0
7 1391.0
8 1043.0
9 3199.0
Name: count_7_days, dtype: float64
'''
现在使用.join将新特性与其他数据连接起来,因为我们已经匹配了索引。
baseline_data.join(count_7_days).head(10)
goal | hour | day | month | year | outcome | category | currency | country | count_7_days | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1000.0 | 12 | 11 | 8 | 2015 | 0 | 108 | 5 | 9 | 1409.0 |
1 | 30000.0 | 4 | 2 | 9 | 2017 | 0 | 93 | 13 | 22 | 957.0 |
2 | 45000.0 | 0 | 12 | 1 | 2013 | 0 | 93 | 13 | 22 | 739.0 |
3 | 5000.0 | 3 | 17 | 3 | 2012 | 0 | 90 | 13 | 22 | 907.0 |
4 | 19500.0 | 8 | 4 | 7 | 2015 | 0 | 55 | 13 | 22 | 1429.0 |
5 | 50000.0 | 13 | 26 | 2 | 2016 | 1 | 123 | 13 | 22 | 1284.0 |
6 | 1000.0 | 18 | 1 | 12 | 2014 | 1 | 58 | 13 | 22 | 1119.0 |
7 | 25000.0 | 20 | 1 | 2 | 2016 | 0 | 41 | 13 | 22 | 1391.0 |
8 | 125000.0 | 18 | 24 | 4 | 2014 | 0 | 113 | 13 | 22 | 1043.0 |
9 | 65000.0 | 21 | 11 | 7 | 2014 | 0 | 39 | 13 | 22 | 3199.0 |
时间自上次项目同类别
同一类别的项目会争夺捐助者吗?如果你想投资一款电子游戏,而另一个游戏项目刚刚启动,你可能不会得到那么多钱。我们可以通过计算同一类别中上一次启动项目以来的时间来捕捉这一点。
在组中执行操作的简便方法是使用.groupby然后.transform。transform方法接受一个函数,然后为每个组向该函数传递一个序列或dataframe。这将返回一个与原始dataframe索引相同的dataframe。在本例中,我们将对“类别”执行groupby,并使用transform来计算每个类别的时差。
def time_since_last_project(series):
# Return the time in hours
return series.diff().dt.total_seconds() / 3600.
df = ks[['category', 'launched']].sort_values('launched')
timedeltas = df.groupby('category').transform(time_since_last_project)
timedeltas.head(20)
launched | |
---|---|
94579 | NaN |
319002 | NaN |
247913 | NaN |
48147 | NaN |
75397 | NaN |
2842 | 0.000000 |
273779 | NaN |
169268 | NaN |
322000 | NaN |
138572 | NaN |
325391 | NaN |
122662 | 137.130833 |
213711 | NaN |
345606 | 145.941111 |
235255 | NaN |
98954 | 344715.626944 |
342226 | NaN |
275091 | NaN |
284115 | NaN |
32898 | NaN |
我们在这里获得了NaNs的项目是在他们的类别的第一个。我们需要用均值或中位数来表示。我们还需要重置索引,以便将它与其他数据连接起来。
# 上次项目结束后的最后一次
timedeltas = timedeltas.fillna(timedeltas.median()).reindex(baseline_data.index)
timedeltas.head(20)
launched | |
---|---|
0 | 18.606111 |
1 | 5.592778 |
2 | 1.313611 |
3 | 0.635000 |
4 | 16.661389 |
5 | 2.629722 |
6 | 0.367500 |
7 | 12.286111 |
8 | 14.243611 |
9 | 0.174722 |
10 | 1.372222 |
11 | 8.524444 |
12 | 0.015833 |
13 | 9.884444 |
14 | 1.725556 |
15 | 3.806111 |
16 | 2.654167 |
17 | 26.531667 |
18 | 12.273611 |
19 | 9.288889 |
Transforming numerical features (转换数值特征)
“goal”中的价值分布表明,大多数项目的目标都在5000美元以下。然而,有一长串的目标是达到10万美元。当特征是正态分布时,一些模型工作得更好,因此它可能有助于转换目标值。通常的选择是平方根和自然对数。这些转换还可以帮助约束异常值。
在这里,我将使用平方根和日志函数转换目标特性,然后拟合一个模型,看看它是否有帮助
plt.hist(ks.goal, range=(0, 100000), bins=50);
plt.title('Goal');
plt.hist(np.sqrt(ks.goal), range=(0, 400), bins=50);
plt.title('Sqrt(Goal)');
plt.hist(np.log(ks.goal), range=(0, 25), bins=50);
plt.title('Log(Goal)');
日志转换对我们的模型没有帮助,因为基于树的模型是规模不变的。然而,如果我们有一个线性模型或神经网络,这应该会有所帮助。
其他的变换包括平方和其他幂,指数,等等。这些可能有助于区分模型,就像支持向量机的内核技巧一样。同样,需要一些实验来看看什么是有效的。一种方法是创建一堆新特性,然后用特性选择算法选择最好的特性。
===========================================================
来源:CSDN
作者:liuqing-Zhang
链接:https://blog.csdn.net/weixin_40477535/article/details/103587229