楼主: CDA网校
4775 32

[每天一个数据分析师] 万字长文!一文学会Python特征工程 [推广有奖]

管理员

大师

62%

还不是VIP/贵宾

-

威望
3
论坛币
31793 个
通用积分
3041.6696
学术水平
260 点
热心指数
268 点
信用等级
235 点
经验
194744 点
帖子
5102
精华
19
在线时间
3689 小时
注册时间
2019-9-13
最后登录
2024-4-26

初级热心勋章

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

引言

前面几节的内容,为大家入门数据分析奠定了一定的基础,《Python统计学极简入门》帮你解释了如何从统计的角度来刻画数据,《SQL数据分析极简入门》帮你从数据库提取业务需要的数据、《Python数据分析极简入门》帮你如何用Pandas快速处理数据。

接下来的内容,我们往机器学习领域延伸一下,按照一贯的“MPV(最小可行化产品)”思路,先学机器学习的重中之重————特征工程。在机器学习方法的的实施流程里面,我们拿到了原始数据,做了各种数据清洗后,就需要掌握特征工程的知识,以便于更好地服务于后面的机器学习模型。

众所周知,关于数据与特征,业界广为流传着两句话:“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限”、“garbage in,garbage out”。前者从机器学习的角度,较为严谨地指出,数据与特征的重要性要大于模型和算法;后者近似戏谑般地表达突出了数据的重要性。

但是市面上关于特征工程的书,却只有寥寥几本 《特征工程入门与实践》、《精通特征工程》、《数据准备及特征工程》不仅数量少,而且里面的方法大部分也都是大家耳熟能详的内容:缺失值填补、归一化、one-hot,只看这些内容对于日常做特征的小伙伴们肯定是意犹未尽,总想着有没有更全面一些的内容,这个系列就尝试着给大家梳理一下这部分内容。

学习入口:《Python特征工程极简入门》

为了使得内容更聚焦一些,我们本次暂不涉及文本及图像特征,如有需要后续会单独写两个教程来总结。本次教程结构如下:

  • 时间特征
    • 离散时间
    • 连续时间
  • 空间特征
  • 数值特征
    • 归一化(MinMax Scaling)
    • 标准化(Standardization)
    • 正则化(Normalization)
  • 类别特征
    • 序号编码(Ordinal Encoding)
    • 独热编码(One-Hot Encoding)
    • 二进制编码(Binary Encoding)
    • 标签编码(Label Encoding)
  • 统计及组合特征
    • 统计特征
    • 业务特征
    • 组合特征
  • 特征变换
    • 对数变换
    • 指数变换
    • Box_Cox变换

特征工程是对原始数据进行一系列工程处理,将其提炼为特征,作为输入供算法和模型使用。

如果把机器学习模型简化一下:用已有的xxyy 来训练一个模型 f(x)f(x)。那么,特征工程就是利用原有的xx去构造新的xx’,同时配合模型调参,使得最终模型的效果达到最优。我们把通过已有数学公式: xx,构建新的xx’以取得更好的建模效果,这个特征构建的过程称为特征工程

大道至简,我们日常分析的数据,尤其是互联网数据,大多数是由人类的生产活动产生的,那就离不开时间、空间的概念——宇宙,“宇”无限空间,“宙”无限时间。我们做特征工程也是一样,时间、空间地理特征也是重中之重,是用来刻画客观世界极其重要的维度,我们先来看一下时间特征:

时间特征

离散时间

  • 一天中的哪个时间段
def get_time_period(hour):
    if 0 <= hour < 6:
        return "凌晨"
    elif 6 <= hour < 12:
        return "上午"
    elif 12 <= hour < 18:
        return "下午"
    else:
        return "晚上"
# get_time_period(8)
  • 早高峰、晚高峰
def get_peak_hour(hour):
    if 7 <= hour < 10:
        return "早高峰"
    elif 17 <= hour < 20:
        return "晚高峰"
    else:
        return "非高峰时段"
# get_peak_hour(7)
  • 是否工作日
from datetime import datetime

def is_weekday(date_string):
    date = datetime.strptime(date_string, '%Y-%m-%d')
    if date.weekday() < 5:  # Monday to Friday are considered weekdays (0 to 4)
        return True
    else:
        return False
# is_weekday('2023-01-16')
  • 一周中的星期几
from datetime import datetime

def get_day_of_week(date_string):
    date = datetime.strptime(date_string, '%Y-%m-%d')
    return date.strftime("%A")  # 返回星期几的字符串表示,比如"Monday"、"Tuesday"等

# get_day_of_week('2023-01-16')
  • 一年中的哪个星期
from datetime import datetime

def get_week_of_year(date_string):
    date = datetime.strptime(date_string, '%Y-%m-%d')
    return date.strftime("%U")  # 返回一年中的第几个星期,从0开始计数

# get_week_of_year('2023-01-16')
  • 一年中的哪个月
from datetime import datetime

def get_month_of_year(date_string):
    date = datetime.strptime(date_string, '%Y-%m-%d')
    return date.strftime("%B")  # 返回月份的字符串表示,比如"January"、"February"等
    
# get_month_of_year('2023-07-15')
  • 一年中的哪个季度
from datetime import datetime

def get_quarter_of_year(date_string):
    date = datetime.strptime(date_string, '%Y-%m-%d')
    quarter = (date.month - 1) // 3 + 1
    return quarter

# get_quarter_of_year('2023-07-15')
  • 是否中国节假日
# !pip3 install chinese_calendar
# !pip3 install --upgrade pip
from chinese_calendar import is_holiday

def is_chinese_holiday(date_string):
    date = datetime.strptime(date_string, '%Y-%m-%d').date()
    return is_holiday(date)

# is_chinese_holiday('2023-10-01')
  • 是否中国农历二十四节气
# !pip3 install lunardate
from lunardate import LunarDate

def is_lunar_solar_term(date_string):
    date = LunarDate.fromSolarDate(*map(int, date_string.split('-')))
    return date.getSolarTerm()
    
# is_lunar_solar_term('2023-10-01')
  • 是否是美国公共假日
# !pip3 install holidays
import datetime
import holidays
# 你可以根据你的需要修改国家或地区,以及日期格式。
us_holidays = holidays.US()

def is_public_holiday(date):
    return date in us_holidays


date_to_check = datetime.date(2022, 1, 1)
print(is_public_holiday(date_to_check))  # 输出 True 或 False
True

连续时间

  • 持续时间(单个页面的浏览时长)
import pandas as pd
from datetime import datetime

# 假设这是你的数据
data = {
    'userid': [1, 1, 1, 2, 2],
    'url': ['page1', 'page2', 'page1', 'page3', 'page1'],
    'create_time': ['2023-05-01 08:30:00', '2023-05-01 08:35:00', '2023-05-01 08:40:00', '2023-05-01 09:00:00', '2023-05-01 09:10:00']
}
df = pd.Datafr ame(data)
df['create_time'] = pd.to_datetime(df['create_time'])
# 首先按照用户ID和URL进行排序
df = df.sort_values(by=['userid', 'url', 'create_time'])
# 计算停留时间
df['duration'] = df.groupby(['userid', 'url'])['create_time'].diff().dt.total_seconds().fillna(0)
print(df)
   userid    url         create_time  duration
0       1  page1 2023-05-01 08:30:00       0.0
2       1  page1 2023-05-01 08:40:00     600.0
1       1  page2 2023-05-01 08:35:00       0.0
4       2  page1 2023-05-01 09:10:00       0.0
3       2  page3 2023-05-01 09:00:00       0.0

空间特征

  • 判断一、二、三线城市
def city_tier(city_name):
    first_tier_cities = ['北京', '上海', '广州', '深圳']
    second_tier_cities = ['杭州', '南京', '武汉', '成都', '重庆', '西安', '郑州', '长沙', '青岛', '沈阳', '大连', '厦门', '福州', '哈尔滨', '济南', '宁波', '无锡', '常州', '东莞', '佛山', '珠海', '汕头', '南宁', '昆明', '贵阳', '石家庄', '太原', '合肥', '南昌', '长春', '哈尔滨']
    third_tier_cities = ['其他城市']

    if city_name in first_tier_cities:
        return '一线城市'
    elif city_name in second_tier_cities:
        return '二线城市'
    else:
        return '三线城市或其他城市'

# 举例:判断城市'杭州'的城市等级
city = '杭州'
result = city_tier(city)
print(f'城市{city}为:{result}')
城市杭州为:二线城市
  • ip数据转城市
import re
import requests

def get_city_by_ip(ip):
    url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip
    response = requests.get(url)
    data = response.json()
    if data['code'] == 0:
        city = data['data']['city']
        return city
    else:
        return '未知城市'
# 举例:查询IP地址对应的城市
ip_address = '8.8.8.8'  # 你可以替换成你要查询的IP地址
city = get_city_by_ip(ip_address)
print(f'IP地址{ip_address}对应的城市为:{city}')
IP地址8.8.8.8对应的城市为:未知城市
  • 经纬度数据转城市
import requests
def get_city_by_location(lat, lng):
    ak = 'your_baidu_map_api_key'  
    # 你需要替换成你自己的百度地图API密钥
    url = f'http://api.map.baidu.com/reverse_geocoding/v3/?ak={ak}&output=json&coordtype=wgs84ll&location={lat},{lng}'
    response = requests.get(url)
    data = response.json()
    if data['status'] == 0:
        city = data['result']['addressComponent']['city']
        return city
    else:
        return '未知城市'
# # 举例:查询经纬度对应的城市
# latitude = 39.4    # 纬度
# longitude = 115.7  # 经度
# city = get_city_by_location(latitude, longitude)
# print(f'经纬度({latitude},{longitude})对应的城市为:{city}')
  • 地址转化省份、城市、县区数据
import requests

def parse_address(address):
    ak = 'your_baidu_map_api_key'  # 你需要替换成你自己的百度地图API密钥
    url = f'http://api.map.baidu.com/geocoding/v3/?address={address}&output=json&ak={ak}'
    response = requests.get(url)
    data = response.json()
    if data['status'] == 0:
        result = data['result']
        province = result['addressComponent']['province']
        city = result['addressComponent']['city']
        district = result['addressComponent']['district']
        return province, city, district
    else:
        return '未知', '未知', '未知'
# # 举例:解析地址并获取省市县区信息
# address = '北京市海淀区中关村大街1号'
# province, city, district = parse_address(address)
# print(f'地址"{address}"对应的省份为:{province},城市为:{city},区/县为:{district}')
  • 经纬度转化球面
import pyproj
# 定义投影坐标系
wgs84 = pyproj.Proj(init='epsg:4326')  # WGS84经纬度坐标系
mercator = pyproj.Proj(init='epsg:3857')  # Web墨卡托投影坐标系
# 经纬度坐标转换为球面坐标
longitude = 116.4074  # 经度
latitude  = 39.9042   # 纬度
x, y = pyproj.transform(wgs84, mercator, longitude, latitude)
print(f'经纬度({longitude},{latitude})转换为球面坐标({x},{y})')
经纬度(116.4074,39.9042)转换为球面坐标(12958412.492568914,4852030.634814578)
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:python Python数据分析 Standard Garbage ordinal

已有 5 人评分经验 收起 理由
kongqingbao280 + 100 精彩帖子
kychan + 30 精彩帖子
np84 + 100 精彩帖子
zl89 + 80 精彩帖子
cheetahfly + 100 精彩帖子

总评分: 经验 + 410   查看全部评分

沙发
CDA网校 学生认证  发表于 2024-3-4 10:34:34 |只看作者 |坛友微信交流群

数值特征

归一化(MinMax Scaling)

为什么要进行归一化?简单来说就是将数据缩放在[0,1]区间,防止量纲不一致。公式如下:
xnew=xxminxmaxxminx_{\text{new}} = \frac{x - x_{\text{min}}}{x_{\text{max}} - x_{\text{min}}}
其中,xx 是原始数据,xnewx_{\text{new}} 是正则化后的数据,xminx_{\text{min}}xmaxx_{\text{max}} 分别是数据的最小值和最大值。
代码如下:

from sklearn import preprocessing
min_max_scaler= preprocessing.MinMaxScaler()
x = np.array([[ 1., -1.,  2.],
              [ 2.,  0.,  0.],
              [ 0.,  1., -1.]])
x_new = min_max_scaler.fit_transform(x)
x_new
array([[0.5       , 0.        , 1.        ],
       [1.        , 0.5       , 0.33333333],
       [0.        , 1.        , 0.        ]])

使用sklearn.preprocessing.StandardScaler类也可以实现,使用该类的好处在于可以保存训练集中的参数(均值、方差)

scaler = preprocessing.StandardScaler().fit(x)
scaler
StandardScaler()
scaler.mean_
array([1.        , 0.        , 0.33333333])
scaler.var_
array([0.66666667, 0.66666667, 1.55555556])
x_new = scaler.transform(x)   
x_new
array([[ 0.        , -1.22474487,  1.33630621],
       [ 1.22474487,  0.        , -0.26726124],
       [-1.22474487,  1.22474487, -1.06904497]])

标准化(Standardization)

为什么要进行标准化?数据经过零-均值标准化后均值为0,方差为1,更方便利于标准正态分布的性质。

将数据转换为均值为0,标准差为1的标准正态分布。

xnew=xμσx_{\text{new}} = \frac{x - \mu}{\sigma}
使用sklearn.preprocessing.scale()函数,可以直接将给定数据进行标准化。代码如下:

from sklearn import preprocessing
min_max_scaler= preprocessing.MinMaxScaler()
x = np.array([[ 1., -1.,  2.],
              [ 2.,  0.,  0.],
              [ 0.,  1., -1.]])
x_new = min_max_scaler.fit_transform(x)
x_new
array([[0.5       , 0.        , 1.        ],
       [1.        , 0.5       , 0.33333333],
       [0.        , 1.        , 0.        ]])

使用sklearn.preprocessing.StandardScaler类也可以实现,使用该类的好处在于可以保存训练集中的参数(均值、方差)

scaler = preprocessing.StandardScaler().fit(x)
scaler

scaler.mean_
scaler.var_
 
x_new = scaler.transform(x)   
x_new
array([[ 0.        , -1.22474487,  1.33630621],
       [ 1.22474487,  0.        , -0.26726124],
       [-1.22474487,  1.22474487, -1.06904497]])

正则化(Normalization)

为什么要进行正则化?正则化可以帮助防止过拟合,提高模型的泛化能力。

常见的正则化的方式有l1l_1正则化和l2l_2正则化

这里我们先看看lpl_p范数怎么计算?(这段如果理解不了,可以跳过,原理没有实践重要)

p-范数的计算公式:
xp=(x1p+x2p++xnp)1p ||x||_p = \left( |x_1|^p + |x_2|^p + \ldots + |x_n|^p \right)^{\frac{1}{p}}

其中,xx是一个n维向量,xix_i 表示向量的第i个元素,pp 是范数的阶数。特别地,当p=2p=2时,称为欧几里得范数(Euclidean norm);当>p=1p=1时,称为曼哈顿范数(Manhattan norm)。

比如当我们有一个二维向量 x=[3 4]x = \begin{bmatrix} 3 \ 4 \end{bmatrix},我们可以计算不同p-范数的值。
p=1p=1 时,曼哈顿范数(Manhattan norm):
x1=3+4=7||x||_1 = |3| + |4| = 7
p=2p=2时,欧几里得范数(Euclidean norm):
x2=32+42=5||x||_2 = \sqrt{3^2 + 4^2} = 5

有了范数,我们看看范数正则化的数学原理:

给定一个矩阵 X,其中每一行表示一个样本,每一列表示一个特征。

对于矩阵 X 中的每一行 x_i,L1 范数正则化的过程如下:

  1. 计算每一行的 L1 范数,即将该行中各个元素的绝对值相加。
  2. 将每个元素除以该行的 L1 范数,从而得到一个新的矩阵 x_new。

具体来说,对于矩阵 X 中的第 i 行,L1 范数正则化的数学过程如下:
xnew,i=xij=1nxij x_{\text{new},i} = \frac{x_{i}}{\sum_{j=1}^{n} |x_{ij}|}

其中,xix_i 表示矩阵 XX 中的第 ii 行,$ x_{\text{new},i} $ 表示正则化后的第 i 行,n 表示矩阵 X 的列数。通过这个过程,矩阵 X 中的每一行都被重新缩放,使得每一行的元素之和为1,从而实现了 L1 范数正则化。

可以使用preprocessing.normalize()函数对指定数据进行转换,代码如下:

x =  [[ 1., -1.,  2.],
      [ 2.,  0.,  0.],
      [ 0.,  1., -1.]]
x_new = preprocessing.normalize(x, norm='l1')
x_new
array([[ 0.25, -0.25,  0.5 ],
       [ 1.  ,  0.  ,  0.  ],
       [ 0.  ,  0.5 , -0.5 ]])

也可以使用processing.Normalizer()类实现对训练集和测试集的拟合和转换,代码如下:

x =  [[ 1., -1.,  2.],
      [ 2.,  0.,  0.],
      [ 0.,  1., -1.]]
x_new = preprocessing.normalize(x, norm='l2')
x_new
array([[ 0.40824829, -0.40824829,  0.81649658],
       [ 1.        ,  0.        ,  0.        ],
       [ 0.        ,  0.70710678, -0.70710678]])

统计值特征(max, min, mean, std)

import pandas as pd
# 创建一个示例数据集
data = {
    'A': [1, 2, 3, 4, 5],
    'B': [10, 20, 30, 40, 50]
}
df = pd.Datafr ame(data)
# 计算均值
mean_values = df['A'].mean()
# 计算标准差
std_values = df['A'].std()
# 计算最大值
max_values = df['A'].max()
# 计算最小值
min_values = df['A'].min()

分箱离散化

import pandas as pd

# 创建一个示例数据集
data = {
    'age': [25, 30, 35, 40, 45, 50, 55, 60, 65, 70],
    'income': [35000, 50000, 65000, 80000, 95000, 110000, 125000, 140000, 155000, 170000]
}
df = pd.Datafr ame(data)
# 定义分箱的边界
bins = [20, 30, 40, 50, 60, 70]
# 使用cut函数进行分箱离散化
df['age_bin'] = pd.cut(df['age'], bins)
# 打印结果
print(df['age_bin'])
0    (20, 30]
1    (20, 30]
2    (30, 40]
3    (30, 40]
4    (40, 50]
5    (40, 50]
6    (50, 60]
7    (50, 60]
8    (60, 70]
9    (60, 70]
Name: age_bin, dtype: category
Categories (5, interval[int64, right]): [(20, 30] < (30, 40] < (40, 50] < (50, 60] < (60, 70]]

每个类别下对应的变量统计值histogram(分布状况)

import pandas as pd
# 创建一个示例数据集
data = {
    'category': ['A', 'B', 'A', 'B', 'A', 'B'],
    'value': [10, 20, 30, 40, 50, 60]
}
df = pd.Datafr ame(data)
# 按照类别进行分组,然后计算每个类别下对应的变量的统计值
grouped = df.groupby('category')['value'].agg(['mean', 'median', 'std'])
grouped
mean median std
category
A 30.0 30.0 20.0
B 40.0 40.0 20.0

数值型转化为类别型

import pandas as pd

# 创建一个示例数据集
data = {
    'age': [25, 30, 35, 40, 45, 50, 55, 60, 65, 70],
    'income': [35000, 50000, 65000, 80000, 95000, 110000, 125000, 140000, 155000, 170000]
}
df = pd.Datafr ame(data)

# 将数值型变量进行分箱离散化
bins = [20, 30, 40, 50, 60, 70]
df['age_bin'] = pd.cut(df['age'], bins)

# 使用get_dummies函数生成哑变量
dummy_variables = pd.get_dummies(df['age_bin'], prefix='age_bin')
# 将生成的哑变量与原始数据集合并
df = pd.concat([df, dummy_variables], axis=1)
df
age income age_bin age_bin_(20, 30] age_bin_(30, 40] age_bin_(40, 50] age_bin_(50, 60] age_bin_(60, 70]
0 25 35000 (20, 30] 1 0 0 0 0
1 30 50000 (20, 30] 1 0 0 0 0
2 35 65000 (30, 40] 0 1 0 0 0
3 40 80000 (30, 40] 0 1 0 0 0
4 45 95000 (40, 50] 0 0 1 0 0
5 50 110000 (40, 50] 0 0 1 0 0
6 55 125000 (50, 60] 0 0 0 1 0
7 60 140000 (50, 60] 0 0 0 1 0
8 65 155000 (60, 70] 0 0 0 0 1
9 70 170000 (60, 70] 0 0 0 0 1

类别特征

序号编码(Ordinal Encoding)

from sklearn.preprocessing import OrdinalEncoder

data = [['low'], ['medium'], ['high']]
encoder = OrdinalEncoder()
encoded_data = encoder.fit_transform(data)
print(encoded_data)
[[1.]
 [2.]
 [0.]]

独热编码(One-Hot Encoding)

# 使用OneHotEncoder实现


import pandas as pd
from sklearn.preprocessing import OneHotEncoder

data = {'ID': [1, 2, 3],
        'Color': ['红色', '蓝色', '绿色']}
df = pd.Datafr ame(data)
encoder = OneHotEncoder()
encoded_data = encoder.fit_transform(df[['Color']]).toarray()
df_encoded = pd.Datafr ame(encoded_data, columns=encoder.get_feature_names(['Color']))
df = pd.concat([df, df_encoded], axis=1)
df
ID Color Color_红色 Color_绿色 Color_蓝色
0 1 红色 1.0 0.0 0.0
1 2 蓝色 0.0 0.0 1.0
2 3 绿色 0.0 1.0 0.0
# 使用OneHotEncoder实现

# 使用get_dummies实现
data = {'ID': [1, 2, 3],
        'Color': ['红色', '蓝色', '绿色']}
df = pd.Datafr ame(data)
dummies = pd.get_dummies(df['Color'], prefix='Color')
df = pd.concat([df, dummies], axis=1)
df
ID Color Color_红色 Color_绿色 Color_蓝色
0 1 红色 1 0 0
1 2 蓝色 0 0 1
2 3 绿色 0 1 0
df_1 = pd.get_dummies(df['Color'], prefix='Color',drop_first=True)
df_1
Color_绿色 Color_蓝色
0 0 0
1 0 1
2 1 0

二进制编码(Binary Encoding)

from sklearn.preprocessing import LabelEncoder

data = [1,5,67,100]
encoder = LabelEncoder()
encoded_data = encoder.fit(data).transform([1,1,100,67,5])
print(encoded_data)
[0 0 3 2 1]
# import category_encoders as ce

# data = ['A', 'B', 'C']
# encoder = ce.BinaryEncoder(cols=['category'])
# encoded_data = encoder.fit_transform(data)
# print(encoded_data)

标签编码(Label Encoding)

from sklearn.preprocessing import LabelEncoder

data = ['cat', 'dog', 'rabbit']
encoder = LabelEncoder()
encoded_data = encoder.fit_transform(data)
print(encoded_data)

[0 1 2]
from sklearn.preprocessing import LabelEncoder

data = [1,5,67,100]
encoder = LabelEncoder()
encoded_data = encoder.fit(data).transform([1,1,100,67,5])
print(encoded_data)
[0 0 3 2 1]

数值型特征工程小结:

  • 连续型特征(Continuous Feature):归一化(Min-Max Scaling)、零均值标准化(Z-Score Standardization)、L_p范数正则化(L_p Normalization)
  • 离散型特征(Categorical Feature):序号编码(Ordinal Encoding)、独热编码(One-hot Encoding)、 二进制编码(Binary Encoding)
已有 1 人评分经验 收起 理由
cheetahfly + 50 精彩帖子

总评分: 经验 + 50   查看全部评分

使用道具

藤椅
CDA网校 学生认证  发表于 2024-3-4 10:39:25 |只看作者 |坛友微信交流群

统计及组合特征

除了前面常见的统计特征包括平均值、方差、最大值、最小值、中位数、偏度、峰度等。还有一部分特征是业务统计特征,这部分需要结合到业务场景做具体统计,可以帮助我们更好地理解数据的分布和特点,为后续的模型训练和预测提供有用的信息。

另外,组合特征是指将原始特征进行组合,生成新的特征。通过组合不同的特征,可以发现特征之间的关联性,提高模型的表现。常见的组合特征包括特征相加、相乘、相除、取平均值等操作。通过合理地组合特征,可以提高模型的泛化能力和预测准确度。

综合利用业务的统计特征和组合特征可以帮助我们更好地挖掘数据的潜在信息,提高模型的性能和效果。在特征工程的过程中,需要根据具体的问题和数据特点来选择合适的统计特征和组合特征,从而提高模型的预测能力。

统计特征

  • 加减平均:商品价格高于平均价格多少,用户在某个品类下消费超过平均多少,用户连续登录天数超过平均多少…
  • 分位线:商品属于售出商品价格的多少分位线处
  • 次序型:排在第几位
  • 比例类:电商中,好/中/差评比例

业务特征

  • 前一天的购物车商品很有可能第二天就被购买 =>规则
  • 剔除掉在30天里从来不买东西的人 => 数据清洗
  • 加车N件,只买了一件的,剩余的不会买 => 规则
  • 购物车购买转化率 =>用户维度统计特征
  • 商品热度 =>商品维度统计特征
  • 对不同item 点击/收藏/购物车/购买的总计 => 商品维度统计特征
  • 对不同item 点击/收藏/购物车/购买平均每个user的计数 => 用户维
    度统计特征
  • 变热门的品牌/商品 => 商品维度统计特征(差值型)
  • 最近第1/2/3/7天的行为数与平均行为数的比值 => 用户维度统计
    特征(比例型)
  • 商品在类别中的排序 => 商品维度统计特征(次序型)
  • 商品交互的总人数 => 商品维度统计特征(求和型)
  • 商品的购买转化率及转化率与类别平均转化率的比值=>
  • 商品行为/同类同行为均值=> 商品维度统计特征(比例型)
  • 最近1/2/3天的行为(按4类统计)=>时间型+用户维度统计特征
  • 最近的交互离现在的时间=>时间型
  • 总交互的天数=>时间型
  • 用户A对品牌B的总购买数/收藏数/购物车数=>用户维度统计特征
  • 用户A对品牌B的点击数的平方 =>用户维度统计特征
  • 用户A对品牌B的购买数的平方=>用户维度统计特征
  • 用户A对品牌B的点击购买比=>用户维度统计特征(比例型)
  • 用户交互本商品前/后,交互的商品数=>时间型+用户维度统计特征
  • 用户前一天最晚的交互行为时间=>时间型
  • 用户购买商品的时间(平均,最早,最晚)=>时间型

组合特征

简单组合特征
  • count:A_COUNT、B_COUNT、A_B_COUNT
  • nunique: A_nunqiue_B (按B对称的下文省略)
  • ratio: A_B_COUNT/A_COUNT 在A里各个B类所占的比例
  • average:A_COUNT/A_nunqiue_B A里各个B类的平均数
  • most: A_most_B 在A类里出现最高的B是哪个
  • pivot: A_B1_count、A_B2_count A和B类里特定的B1、B2的联合统计
  • pivot2: A_B1_count-A_B2_count A的B1行为和B2行为的加减乘除
  • stat1: A_stat_A_B_COUNT 基于A_B_COUNT对A的描述,
  • stat2 :A_stat_B_COUNT 基于B_COUNT对A的描述,
  • 序列化:初步LDA,NMF,SVD,进一步Word2Vec,doc2vec 再进一步 图神经网络deepwalk,pPRoNE

再比如,我们把category A和B替换成user ,item

  • count:user_COUNT(用户活跃度)、item_COUNT(商品热度)、user_item_COUNT(用户对特定商品的喜爱)
  • nunique: user_nunqiue_item (一个用户购买多少种商品) item nunique_user (一个商品被多少个不同用户购买)
  • ratio: user_item_COUNT/user_COUNT (某个商品在user购买中的比例,喜爱程度)
    average:user_COUNT/user_nunqiue_item (平均每类商品的购买量)
  • most: user_most_item (用户最喜爱的品类)
  • pivot: user_item1_count、user_item2_count (用户和特定商品的交互)
  • pivot2: user_item1_count-user_item2_count (用户不同行为的差值,比如生活用品和娱乐用品的比例)
  • stat1: user_stat_user_item_COUNT (max:买的最多的商品的数量,std:不同商品的分散度,是专宠还是偏爱)
  • stat2 :user_stat_item_COUNT (mean:用户是喜欢热门商品还是冷门商品)
  • 序列化:初步LDA,NMF,SVD(用商品描述用户画像)进一步Word2Vec,doc2vec 再进一步 图神经网络deepwalk,pPRoNE(刻画商品和用户的共现性和相似性)
模型特征组合
  • 用GBDT产出特征组合路径
  • 组合特征和原始特征一起放进LR训练
  • 利用特征重要性挖掘强特,然后用各种category,numeric特征去描述
    • 如果看到一个数值特征特征重要性很强,我们也可以用类别特征和其交叉。如果一个统计特征很重要,我们可以增加一个时区维度,比如最近一周,最近一个月的相应统计特征。如果距离上次时间很重要,我们可以增加距离上两次,上次三次的时间特征等等。进一步,特征重要性表也可以知道深度学习模型子结构的选择,序列特征对应rnn类,交叉特征对应fm类,文本特征对应nlp类,如果特征不重要,就不用上相应的结构了,如果重要,就可以对将特定的特征输入对应的子结构

特征变换

对数变换

import pandas as pd
import numpy as np
from sklearn.preprocessing import FunctionTransformer
from scipy.stats import boxcox

# 创建一个包含数值特征的数据集
data = {
'A': [1, 2, 3, 4, 5,6,7,8,9,10]
}
df = pd.Datafr ame(data)
df
A
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
# 对数变换
np.log2(df['A'])
0    0.000000
1    1.000000
2    1.584963
3    2.000000
4    2.321928
5    2.584963
6    2.807355
7    3.000000
8    3.169925
9    3.321928
Name: A, dtype: float64

指数变换

# 指数变换
np.exp(df['A'])
0        2.718282
1        7.389056
2       20.085537
3       54.598150
4      148.413159
5      403.428793
6     1096.633158
7     2980.957987
8     8103.083928
9    22026.465795
Name: A, dtype: float64

Box-Cox变换的数学公式如下:
对于输入数据 x,Box-Cox变换的公式为:

y(λ)={xλ1λ,if λ0log(x),if λ=0y(\lambda) = \begin{cases} \frac{x^\lambda - 1}{\lambda}, &amp; \text{if } \lambda \neq 0 \\log(x), &amp; \text{if } \lambda = 0 \end{cases}

其中,λ 是Box-Cox变换的参数。在实际应用中,通常会通过最大似然估计等方法来确定最优的λ值。

Box-Cox变换

# Box-Cox变换
from sklearn.preprocessing import FunctionTransformer
from scipy.stats import boxcox
boxcox_features= boxcox(df['A'])
boxcox_features[0]
array([0.        , 0.89952679, 1.67649212, 2.38322967, 3.04195194,
       3.66477652, 4.25925123, 4.83048782, 5.38215513, 5.91700147])

参考链接

  1. 特征工程到底是什么? - 砍手豪的回答 - 知乎
    https://www.zhihu.com/question/29316149/answer/2346832545
  2. 特征工程到底是什么? - 城东的回答 - 知乎
    https://www.zhihu.com/question/29316149/answer/110159647
  3. 作者: Sinan Ozdemir / Divya Susarla《特征工程入门与实践》https://book.douban.com/subject/33474864/
  4. 作者: [美] 爱丽丝·郑 / [美] 阿曼达·卡萨丽《精通特征工程入门与实践》 https://book.douban.com/subject/33400236/
  5. 作者:齐伟《数据准备与特征工程》https://book.douban.com/subject/35017311/comments/

九层之台,起于累土;千里之行,始于足下——《道德经》。诸位加油,我们下个系列见!

已有 2 人评分经验 收起 理由
kychan + 30 精彩帖子
cheetahfly + 50 精彩帖子

总评分: 经验 + 80   查看全部评分

使用道具

使用道具

报纸
chmlx 发表于 2024-3-5 08:38:11 |只看作者 |坛友微信交流群

使用道具

地板
sunnyzhxg 发表于 2024-3-5 09:19:17 |只看作者 |坛友微信交流群
谢谢分享

使用道具

7
babylaugh 发表于 2024-3-5 11:13:06 |只看作者 |坛友微信交流群
点赞分享

使用道具

8
takeyourcap 发表于 2024-3-5 21:09:49 |只看作者 |坛友微信交流群
一直在学习python,这篇长文真是太好了

使用道具

9
hsinfu 发表于 2024-3-6 01:17:46 |只看作者 |坛友微信交流群

使用道具

10
hsinfu 发表于 2024-3-6 01:19:36 |只看作者 |坛友微信交流群

使用道具

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-4-27 18:33