引言
前面几节的内容,为大家入门数据分析奠定了一定的基础,《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变换
特征工程是对原始数据进行一系列工程处理,将其提炼为特征,作为输入供算法和模型使用。
如果把机器学习模型简化一下:用已有的和 来训练一个模型 。那么,特征工程就是利用原有的去构造新的,同时配合模型调参,使得最终模型的效果达到最优。我们把通过已有数学公式: ,构建新的以取得更好的建模效果,这个特征构建的过程称为特征工程。
大道至简,我们日常分析的数据,尤其是互联网数据,大多数是由人类的生产活动产生的,那就离不开时间、空间的概念——宇宙,“宇”无限空间,“宙”无限时间。我们做特征工程也是一样,时间、空间地理特征也是重中之重,是用来刻画客观世界极其重要的维度,我们先来看一下时间特征:
时间特征
离散时间
- 一天中的哪个时间段
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)