數據挖掘實戰:特徵工程python實戰

特徵工程是數據科學和機器學習中的重要技巧,對機器模型性能和EDA(exploratory data analysis)的質量有重要影響。本文介紹幾種特徵工程技巧

詳見 notebook

什麼是特徵工程

  • 使用領域知識來創造特徵,使得機器學習算法發揮作用
  • 從原始數據提取特徵以轉換爲算法需要的格式
  • 需要領域知識、數學和編程技巧

數據集

本文使用貸款違約預測數據和送貨平臺數據,可直接參考 notebook

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore') # 關閉警告


#load loan datasets
loan_demographics = pd.read_csv('traindemographics.csv')
loan_prev = pd.read_csv('trainprevloans.csv')
loan_perf = pd.read_csv('trainperf.csv')
#load logistics dataset
sendy_data = pd.read_csv('sendy_logistics.csv')
# 貸款統計信息
# customerid (顧客id)
# birthdate (顧客生日)
# bank_account_type (銀行賬號類型)
# longitude_gps(經度)
# latitude_gps (緯度)
# bank_name_clients (n銀行名稱)
# bank_branch_clients (支行,非必填,所以很多缺失值)
# employment_status_clients (職業狀態)
# level_of_education_clients (教育程度)
loan_demographics.head().append(loan_demographics.tail()) 
customerid birthdate bank_account_type longitude_gps latitude_gps bank_name_clients bank_branch_clients employment_status_clients level_of_education_clients
0 8a858e135cb22031015cbafc76964ebd 1973-10-10 00:00:00.000000 Savings 3.319219 6.528604 GT Bank NaN NaN NaN
1 8a858e275c7ea5ec015c82482d7c3996 1986-01-21 00:00:00.000000 Savings 3.325598 7.119403 Sterling Bank NaN Permanent NaN
2 8a858e5b5bd99460015bdc95cd485634 1987-04-01 00:00:00.000000 Savings 5.746100 5.563174 Fidelity Bank NaN NaN NaN
3 8a858efd5ca70688015cabd1f1e94b55 1991-07-19 00:00:00.000000 Savings 3.362850 6.642485 GT Bank NaN Permanent NaN
4 8a858e785acd3412015acd48f4920d04 1982-11-22 00:00:00.000000 Savings 8.455332 11.971410 GT Bank NaN Permanent NaN
4341 8a858f155554552501555588ca2b3b40 1985-12-13 00:00:00.000000 Other 3.236753 7.030168 Stanbic IBTC NaN Permanent Graduate
4342 8a858fc65cf978f4015cf97cee3a02ce 1982-07-01 00:00:00.000000 Savings 7.013749 4.875662 GT Bank NaN NaN NaN
4343 8a858f4f5b66de3a015b66fc83c61902 1989-09-26 00:00:00.000000 Savings 6.295530 7.092508 GT Bank NaN Permanent NaN
4344 8aaae7a74400b28201441c8b62514150 1985-09-06 00:00:00.000000 Savings 3.354206 6.539070 GT Bank HEAD OFFICE Permanent Primary
4345 8a85896653e2e18b0153e69c1b90265c 1975-06-05 00:00:00.000000 Savings 6.661014 7.472700 UBA NaN Permanent NaN
# trainperf.csv 顧客連續貸款記錄,需根據顧客以前貸款信息和其自身信息預測是否會違約
# customerid (顧客id)
# systemloanid (貸款id. 每個顧客每次貸款有一個id)
# loannumber (需要預測的貸款數)
# approveddate (貸款批准日期)
# creationdate (貸款申請日期)
# loanamount (貸款金額)
# totaldue (總需還款金額,貸款金額+利息+其他費用)
# termdays (貸款期限)
# referredby (擔保人id,空則沒擔保)
# good_bad_flag (good = 按時結清貸款; bad = 未按時結清貸款) - 是要預測的目標值
loan_perf.head().append(loan_perf.tail())
customerid systemloanid loannumber approveddate creationdate loanamount totaldue termdays referredby good_bad_flag
0 8a2a81a74ce8c05d014cfb32a0da1049 301994762 12 2017-07-25 08:22:56.000000 2017-07-25 07:22:47.000000 30000.0 34500.0 30 NaN Good
1 8a85886e54beabf90154c0a29ae757c0 301965204 2 2017-07-05 17:04:41.000000 2017-07-05 16:04:18.000000 15000.0 17250.0 30 NaN Good
2 8a8588f35438fe12015444567666018e 301966580 7 2017-07-06 14:52:57.000000 2017-07-06 13:52:51.000000 20000.0 22250.0 15 NaN Good
3 8a85890754145ace015429211b513e16 301999343 3 2017-07-27 19:00:41.000000 2017-07-27 18:00:35.000000 10000.0 11500.0 15 NaN Good
4 8a858970548359cc0154883481981866 301962360 9 2017-07-03 23:42:45.000000 2017-07-03 22:42:39.000000 40000.0 44000.0 30 NaN Good
4363 8a858e6d58b0cc520158beeb14b22a5a 302003163 2 2017-07-30 09:19:42.000000 2017-07-30 08:18:30.000000 10000.0 13000.0 30 NaN Bad
4364 8a858ee85cf400f5015cf44ab1c42d5c 301998967 2 2017-07-27 15:35:47.000000 2017-07-27 14:35:40.000000 10000.0 13000.0 30 NaN Bad
4365 8a858f365b2547f3015b284597147c94 301995576 3 2017-07-25 16:25:57.000000 2017-07-25 15:24:47.000000 10000.0 11500.0 15 NaN Bad
4366 8a858f935ca09667015ca0ee3bc63f51 301977679 2 2017-07-14 13:50:27.000000 2017-07-14 12:50:21.000000 10000.0 13000.0 30 8a858eda5c8863ff015c9dead65807bb Bad
4367 8a858fd458639fcc015868eb14b542ad 301967124 8 2017-07-06 21:01:06.000000 2017-07-06 20:01:01.000000 30000.0 34500.0 30 NaN Bad
# trainprevloans.csv: 本數據及是顧客在此次貸款前的所有貸款記錄.
# customerid (顧客id)
# systemloanid (貸款id,每個顧客每次貸款都有)
# loannumber (T需要預測的貸款數)
# approveddate (貸款審批通過日期)
# creationdate (貸款申請日期)
# loanamount (貸款金額)
# totaldue (總需還款金額,貸款金額+利息+其他費用) 
# termdays (貸款期限)
# closeddate (貸款還清日期)
# referredby (擔保人id,若空則無擔保)
# firstduedate (貸款期限超30天的首次應付款日期)
# firstrepaiddate (實際首次付款日期)
loan_prev.head().append(loan_prev.tail())
customerid systemloanid loannumber approveddate creationdate loanamount totaldue termdays closeddate referredby firstduedate firstrepaiddate
0 8a2a81a74ce8c05d014cfb32a0da1049 301682320 2 2016-08-15 18:22:40.000000 2016-08-15 17:22:32.000000 10000.0 13000.0 30 2016-09-01 16:06:48.000000 NaN 2016-09-14 00:00:00.000000 2016-09-01 15:51:43.000000
1 8a2a81a74ce8c05d014cfb32a0da1049 301883808 9 2017-04-28 18:39:07.000000 2017-04-28 17:38:53.000000 10000.0 13000.0 30 2017-05-28 14:44:49.000000 NaN 2017-05-30 00:00:00.000000 2017-05-26 00:00:00.000000
2 8a2a81a74ce8c05d014cfb32a0da1049 301831714 8 2017-03-05 10:56:25.000000 2017-03-05 09:56:19.000000 20000.0 23800.0 30 2017-04-26 22:18:56.000000 NaN 2017-04-04 00:00:00.000000 2017-04-26 22:03:47.000000
3 8a8588f35438fe12015444567666018e 301861541 5 2017-04-09 18:25:55.000000 2017-04-09 17:25:42.000000 10000.0 11500.0 15 2017-04-24 01:35:52.000000 NaN 2017-04-24 00:00:00.000000 2017-04-24 00:48:43.000000
4 8a85890754145ace015429211b513e16 301941754 2 2017-06-17 09:29:57.000000 2017-06-17 08:29:50.000000 10000.0 11500.0 15 2017-07-14 21:18:43.000000 NaN 2017-07-03 00:00:00.000000 2017-07-14 21:08:35.000000
18178 8a858899538ddb8e0153a2b555421fc5 301611754 2 2016-04-16 13:36:34.000000 2016-04-16 12:36:28.000000 10000.0 13000.0 30 2016-05-14 00:04:52.000000 NaN 2016-05-16 00:00:00.000000 2016-05-13 18:05:07.000000
18179 8a858899538ddb8e0153a2b555421fc5 301761267 9 2016-11-18 14:26:07.000000 2016-11-18 13:25:51.000000 30000.0 34400.0 30 2016-12-13 16:08:57.000000 NaN 2016-12-19 00:00:00.000000 2016-12-13 15:53:48.000000
18180 8a858899538ddb8e0153a2b555421fc5 301631653 4 2016-06-12 15:30:56.000000 2016-06-12 14:30:50.000000 10000.0 13000.0 30 2016-07-09 15:39:00.000000 NaN 2016-07-12 00:00:00.000000 2016-07-09 15:23:56.000000
18181 8a858f0656b7820c0156c92ca3ba436f 301697691 1 2016-08-27 20:03:45.000000 2016-08-27 19:03:34.000000 10000.0 13000.0 30 2016-10-15 10:17:54.000000 NaN 2016-09-26 00:00:00.000000 2016-10-15 10:02:45.000000
18182 8a858faf5679a838015688de3028143d 301715255 2 2016-09-14 23:42:14.000000 2016-09-14 22:42:05.000000 10000.0 13000.0 30 2016-09-29 19:51:04.000000 NaN 2016-10-14 00:00:00.000000 2016-09-29 19:35:55.000000
# 送貨數據
sendy_data.head().append(sendy_data.tail())
Order No User Id Vehicle Type Platform Type Personal or Business Placement - Day of Month Placement - Weekday (Mo = 1) Placement - Time Confirmation - Day of Month Confirmation - Weekday (Mo = 1) ... Destination Lat Destination Long Rider Id Time from Pickup to Arrival speed manhattan_dist haversine_dist bearing center_latitude center_longitude
0 Order_No_4211 User_Id_633 Bike 3 Business 9 5 9:35:46 AM 9 5 ... -1.300406 36.829741 Rider_Id_432 745.0 0.002160 0.017978 1.930333 -2.076903 -1.309080 36.830056
1 Order_No_25375 User_Id_2285 Bike 3 Personal 12 5 11:16:16 AM 12 5 ... -1.295004 36.814358 Rider_Id_856 1993.0 0.001422 0.141406 11.339849 -56.392163 -1.323229 36.856837
2 Order_No_1899 User_Id_265 Bike 3 Business 30 2 12:39:25 PM 30 2 ... -1.300921 36.828195 Rider_Id_155 455.0 0.003047 0.022588 1.880079 -64.183866 -1.304603 36.835807
3 Order_No_9336 User_Id_1402 Bike 3 Business 15 5 9:25:34 AM 15 5 ... -1.257147 36.795063 Rider_Id_855 1341.0 0.001717 0.061487 4.943458 -57.091553 -1.269224 36.813730
4 Order_No_27883 User_Id_1737 Bike 1 Personal 13 1 9:55:18 AM 13 1 ... -1.295041 36.809817 Rider_Id_770 1214.0 0.001897 0.046143 3.724829 148.114398 -1.280819 36.800968
21196 Order_No_8834 User_Id_2001 Bike 3 Personal 20 3 3:54:38 PM 20 3 ... -1.275285 36.802702 Rider_Id_953 9.0 0.154033 0.018968 1.890335 -172.912798 -1.266849 36.803751
21197 Order_No_22892 User_Id_1796 Bike 3 Business 13 6 10:13:34 AM 13 6 ... -1.331619 36.847976 Rider_Id_155 770.0 0.002701 0.047443 3.731709 136.829614 -1.319381 36.836493
21198 Order_No_2831 User_Id_2956 Bike 3 Business 7 4 5:06:16 PM 7 4 ... -1.258414 36.804800 Rider_Id_697 2953.0 0.001031 0.120338 10.756212 -73.420487 -1.272216 36.851167
21199 Order_No_6174 User_Id_2524 Bike 1 Personal 4 3 9:31:39 AM 4 3 ... -1.279209 36.794872 Rider_Id_347 1380.0 0.001912 0.108474 9.393189 -110.208129 -1.264620 36.834520
21200 Order_No_9836 User_Id_718 Bike 3 Business 26 2 2:19:47 PM 26 2 ... -1.320157 36.830887 Rider_Id_177 2128.0 0.001205 0.113651 9.026448 143.161318 -1.287673 36.806545

10 rows × 35 columns

這裏主要有三種特徵:數值、類別、日期特徵,以下特徵工程沒有指定順序,也不一定適用所有場景,需根據實際情況選擇

缺失值處理

缺失值的出現可能是顧客沒有提供,或因爲錯誤留空,或因爲難以估量. 缺失值可能對模型有重大影響,這裏列出幾種處理方式。
處理方式依賴特徵類型

類別特徵缺失值處理方法

有:衆數填充、順序填充(用前面/後面的值填充)、編碼填充.
在貸款統計信息 loan_demographics 中,有三個類別特徵:bank_branch_clients, employment_status_clients, level_of_education_clients 包含缺失值

## 查看缺失值情況
loan_demographics.isna().sum()
customerid                       0
birthdate                        0
bank_account_type                0
longitude_gps                    0
latitude_gps                     0
bank_name_clients                0
bank_branch_clients           4295
employment_status_clients      648
level_of_education_clients    3759
dtype: int64
## 查看類別分佈
loan_demographics['employment_status_clients'].value_counts()
Permanent        3146
Self-Employed     348
Student           142
Unemployed         57
Retired             4
Contract            1
Name: employment_status_clients, dtype: int64

可以看到在職狀態中,Permanent 出現的最多,因此可以用這個值填充缺失值

## 衆數填充 (Permanent)
loan_demographics['employment_status_clients'] = loan_demographics['employment_status_clients'].fillna(value='Permanent')
loan_demographics.isna().sum()
customerid                       0
birthdate                        0
bank_account_type                0
longitude_gps                    0
latitude_gps                     0
bank_name_clients                0
bank_branch_clients           4295
employment_status_clients        0
level_of_education_clients    3759
dtype: int64

數值特徵缺失值處理

1、用均值、衆數、中位數填充
2、時序填充(前向/後向)
3、用機器學習模型填充:訓練機器學習模型填充缺失值
Sendy logistics 數據集中有兩項數值特徵:Temperature 和 Precipitation in millimeters,以此爲例:

sendy_data.head().append(sendy_data.tail())
Order No User Id Vehicle Type Platform Type Personal or Business Placement - Day of Month Placement - Weekday (Mo = 1) Placement - Time Confirmation - Day of Month Confirmation - Weekday (Mo = 1) ... Arrival at Destination - Time Distance (KM) Temperature Precipitation in millimeters Pickup Lat Pickup Long Destination Lat Destination Long Rider Id Time from Pickup to Arrival
0 Order_No_4211 User_Id_633 Bike 3 Business 9 5 9:35:46 AM 9 5 ... 10:39:55 AM 4 20.4 NaN -1.317755 36.830370 -1.300406 36.829741 Rider_Id_432 745
1 Order_No_25375 User_Id_2285 Bike 3 Personal 12 5 11:16:16 AM 12 5 ... 12:17:22 PM 16 26.4 NaN -1.351453 36.899315 -1.295004 36.814358 Rider_Id_856 1993
2 Order_No_1899 User_Id_265 Bike 3 Business 30 2 12:39:25 PM 30 2 ... 1:00:38 PM 3 NaN NaN -1.308284 36.843419 -1.300921 36.828195 Rider_Id_155 455
3 Order_No_9336 User_Id_1402 Bike 3 Business 15 5 9:25:34 AM 15 5 ... 10:05:27 AM 9 19.2 NaN -1.281301 36.832396 -1.257147 36.795063 Rider_Id_855 1341
4 Order_No_27883 User_Id_1737 Bike 1 Personal 13 1 9:55:18 AM 13 1 ... 10:25:37 AM 9 15.4 NaN -1.266597 36.792118 -1.295041 36.809817 Rider_Id_770 1214
21196 Order_No_8834 User_Id_2001 Bike 3 Personal 20 3 3:54:38 PM 20 3 ... 4:20:17 PM 3 28.6 NaN -1.258414 36.804800 -1.275285 36.802702 Rider_Id_953 9
21197 Order_No_22892 User_Id_1796 Bike 3 Business 13 6 10:13:34 AM 13 6 ... 10:46:17 AM 7 26.0 NaN -1.307143 36.825009 -1.331619 36.847976 Rider_Id_155 770
21198 Order_No_2831 User_Id_2956 Bike 3 Business 7 4 5:06:16 PM 7 4 ... 6:40:05 PM 20 29.2 NaN -1.286018 36.897534 -1.258414 36.804800 Rider_Id_697 2953
21199 Order_No_6174 User_Id_2524 Bike 1 Personal 4 3 9:31:39 AM 4 3 ... 10:08:15 AM 13 15.0 NaN -1.250030 36.874167 -1.279209 36.794872 Rider_Id_347 1380
21200 Order_No_9836 User_Id_718 Bike 3 Business 26 2 2:19:47 PM 26 2 ... 3:17:23 PM 12 30.9 NaN -1.255189 36.782203 -1.320157 36.830887 Rider_Id_177 2128

10 rows × 29 columns

# 注意:以此只用一種填充方式,衆數可能有多個,因此帶下標0
mean_df = round(sendy_data['Temperature'].mean())
mode_df = round(sendy_data['Temperature'].mode()[0])
median_df = round(sendy_data['Temperature'].median())

#Fill with mean
print("Filling with mean value of {}".format(mean_df))
sendy_data['Temperature'] = sendy_data['Temperature'].fillna(mean_df)

#Fill with mode
print("Filling with modal value of {}".format(mode_df))
sendy_data['Temperature'] = sendy_data['Temperature'].fillna(mode_df)

#Fill with median
print("Filling with median value of {}".format(median_df))
sendy_data['Temperature'] = sendy_data['Temperature'].fillna(median_df)
Filling with mean value of 23.0
Filling with modal value of 23.0
Filling with median value of 23.0

使用模型填充缺失值

這裏對Precipitation in millimeters特徵進行模型填充,先找到與目標特徵相關的特徵值加以利用.
The Seaborn 熱圖有助於找到相關特徵.

plt.figure(figsize = (15,10))
sns.heatmap(sendy_data.corr())
<matplotlib.axes._subplots.AxesSubplot at 0x15c46e85cc0>

在這裏插入圖片描述

上圖看出,大部分特徵都與之不相關,這裏使用最後三個特徵: Destination Lat,Destination Long,Time from Pickup to Arrival

from sklearn.linear_model import LinearRegression

lr = LinearRegression()

to_train = ['Precipitation in millimeters', 'Destination Lat', 'Destination Long', 'Time from Pickup to Arrival']
temp_df = sendy_data[to_train]

#Split dataset with missing values and no missing values as test and train set respectively.
x_train = temp_df[temp_df['Precipitation in millimeters'].notnull()].drop(columns='Precipitation in millimeters')
y_train = temp_df[temp_df['Precipitation in millimeters'].notnull()]['Precipitation in millimeters']
x_test = temp_df[temp_df['Precipitation in millimeters'].isnull()].drop(columns='Precipitation in millimeters')

#Fit a simple linear model to the dataset
lr.fit(x_train, y_train)
pred = lr.predict(x_test)

#print fill values
print(np.round(pred, 5))

#Perform filling
sendy_data['Precipitation in millimeters'][sendy_data['Precipitation in millimeters'].isnull()] = pred
[8.03056 8.18609 7.86142 ... 8.46164 7.45028 8.60717]

也可用sklearn.experimental 模塊的IterativeImputer 來自動填充

missing_indx = list(sendy_data['Temperature'][sendy_data['Temperature'].isna()].index)
from sklearn.experimental import enable_iterative_imputer  
from sklearn.impute import IterativeImputer
from sklearn.ensemble import RandomForestRegressor

# 使用帶隨機森林的IterativeImputer
imp = IterativeImputer(RandomForestRegressor(n_estimators=5), max_iter=5, random_state=1)
to_train = ['Temperature', 'Destination Lat', 'Destination Long','Time from Pickup to Arrival']

#填充
sendy_data[to_train] = pd.DataFrame(imp.fit_transform(sendy_data[to_train]), columns=to_train)

sendy_data['Temperature'][missing_indx].head(10)
2     23.70
8     26.06
14    21.66
15    20.42
16    21.46
32    23.84
42    22.32
46    27.18
49    22.34
53    21.36
Name: Temperature, dtype: float64

類別特徵處理

類別特徵只取有限的值. 如某地點/事物的熱度或者app的評分等級(1,2,3,4,5).
本文中,loan_demographics信息的教育程度level_of_education_clients是類別特徵,取值有:Secondary, Graduate, Post-Graduate, Primary.
機器學習算法不能直接作用域類別特徵原始形式,因此必須轉爲數值形式,此過程成爲編碼
編碼有多種方式,具體取決於類別特徵類型.

類別特徵類型

1、有序類別特徵:有自然順序,如評分等級 (1,2,3,4,5).
2、無序類別特徵:無先後之分。例如食物:(大米、意麪、通心粉).
有序特徵編碼:若類別數較少,如教育程度 level_of_education_clients ,可人工編碼.

loan_demographics['level_of_education_clients'].unique()
array([nan, 'Secondary', 'Graduate', 'Post-Graduate', 'Primary'],
      dtype=object)
#use a simple map function
map_education = {"Primary" : 1, "Secondary": 2, "Graduate": 3, "Post-Graduate": 4}
    
loan_demographics['level_of_education_clients'] = loan_demographics['level_of_education_clients'].map(map_education)
loan_demographics['level_of_education_clients'].value_counts()
3.0    420
2.0     89
4.0     68
1.0     10
Name: level_of_education_clients, dtype: int64

若需自動編碼,可以用categorical_encoders庫,有大量編碼方式。

!pip install category_encoders
Collecting category_encoders
  Downloading category_encoders-2.1.0-py2.py3-none-any.whl (100 kB)
Requirement already satisfied: numpy>=1.11.3 in c:\anaconda3\envs\mytf\lib\site-packages (from category_encoders) (1.18.1)
Collecting patsy>=0.4.1
  Downloading patsy-0.5.1-py2.py3-none-any.whl (231 kB)
Requirement already satisfied: scipy>=0.19.0 in c:\anaconda3\envs\mytf\lib\site-packages (from category_encoders) (1.4.1)
Requirement already satisfied: scikit-learn>=0.20.0 in c:\anaconda3\envs\mytf\lib\site-packages (from category_encoders) (0.22.2.post1)
Requirement already satisfied: pandas>=0.21.1 in c:\anaconda3\envs\mytf\lib\site-packages (from category_encoders) (1.0.2)
Collecting statsmodels>=0.6.1
  Downloading statsmodels-0.11.1-cp37-none-win_amd64.whl (8.2 MB)
Requirement already satisfied: six in c:\anaconda3\envs\mytf\lib\site-packages (from patsy>=0.4.1->category_encoders) (1.14.0)
Requirement already satisfied: joblib>=0.11 in c:\anaconda3\envs\mytf\lib\site-packages (from scikit-learn>=0.20.0->category_encoders) (0.14.1)
Requirement already satisfied: python-dateutil>=2.6.1 in c:\anaconda3\envs\mytf\lib\site-packages (from pandas>=0.21.1->category_encoders) (2.8.1)
Requirement already satisfied: pytz>=2017.2 in c:\anaconda3\envs\mytf\lib\site-packages (from pandas>=0.21.1->category_encoders) (2019.3)
Installing collected packages: patsy, statsmodels, category-encoders
Successfully installed category-encoders-2.1.0 patsy-0.5.1 statsmodels-0.11.1

有序特徵若類別數較多,可用標籤編碼(Label Encoding),對每個類別標記唯一的數值
這裏以特徵bank_name_clients和bank_branch_clients 作展示,兩者類別數量較多

cat_cols = loan_demographics.select_dtypes(include='object').columns # object 爲類別特徵
for col in cat_cols:
    print("Number of classes in {}".format(col))
    print(loan_demographics[col].nunique())
    print('--------------------------')
Number of classes in customerid
4334
--------------------------
Number of classes in birthdate
3297
--------------------------
Number of classes in bank_account_type
3
--------------------------
Number of classes in bank_name_clients
18
--------------------------
Number of classes in bank_branch_clients
45
--------------------------
Number of classes in employment_status_clients
6
--------------------------
loan_demographics.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4346 entries, 0 to 4345
Data columns (total 9 columns):
 #   Column                      Non-Null Count  Dtype  
---  ------                      --------------  -----  
 0   customerid                  4346 non-null   object 
 1   birthdate                   4346 non-null   object 
 2   bank_account_type           4346 non-null   object 
 3   longitude_gps               4346 non-null   float64
 4   latitude_gps                4346 non-null   float64
 5   bank_name_clients           4346 non-null   object 
 6   bank_branch_clients         51 non-null     object 
 7   employment_status_clients   4346 non-null   object 
 8   level_of_education_clients  587 non-null    float64
dtypes: float64(3), object(6)
memory usage: 305.7+ KB

categorical_encoders 庫的OrdinalEncoder 函數可用於編碼

import category_encoders as ce

#Label encoding
cat_cols = ['bank_name_clients', 'bank_branch_clients']
encoder = ce.OrdinalEncoder(cols=cat_cols)
loan_demographics = encoder.fit_transform(loan_demographics)
loan_demographics.bank_name_clients.value_counts()
1     1598
11     590
4      432
8      353
9      301
10     282
5      150
13     145
6      121
3      103
7      102
2       55
18      28
12      23
15      22
16      19
14      15
17       7
Name: bank_name_clients, dtype: int64

獨特編碼

使用二進制值表示類別,每種類別創建一個特徵,在類別特徵可取值變多時,效率降低。

cats = ['bank_account_type','level_of_education_clients']
one_hot_enc = ce.OneHotEncoder(cols=cats)
loan_demographics = one_hot_enc.fit_transform(loan_demographics)
loan_demographics.head().T
0 1 2 3 4
customerid 8a858e135cb22031015cbafc76964ebd 8a858e275c7ea5ec015c82482d7c3996 8a858e5b5bd99460015bdc95cd485634 8a858efd5ca70688015cabd1f1e94b55 8a858e785acd3412015acd48f4920d04
birthdate 1973-10-10 00:00:00.000000 1986-01-21 00:00:00.000000 1987-04-01 00:00:00.000000 1991-07-19 00:00:00.000000 1982-11-22 00:00:00.000000
bank_account_type_1 1 1 1 1 1
bank_account_type_2 0 0 0 0 0
bank_account_type_3 0 0 0 0 0
longitude_gps 3.31922 3.3256 5.7461 3.36285 8.45533
latitude_gps 6.5286 7.1194 5.56317 6.64249 11.9714
bank_name_clients 1 2 3 1 1
bank_branch_clients 1 1 1 1 1
employment_status_clients Permanent Permanent Permanent Permanent Permanent
level_of_education_clients_1 1 1 1 1 1
level_of_education_clients_2 0 0 0 0 0
level_of_education_clients_3 0 0 0 0 0
level_of_education_clients_4 0 0 0 0 0
level_of_education_clients_5 0 0 0 0 0

哈希編碼

是一種快速、節省空間的特徵編碼方式. 在類別特徵取值多時比較高效。對特徵值施加hash函數

cat_cols = ['bank_name_clients', 'bank_branch_clients']
hash_enc = ce.HashingEncoder(cols=cat_cols, n_components=10)
loan_demographics = hash_enc.fit_transform(loan_demographics)
loan_demographics.head()
customerid birthdate bank_account_type longitude_gps latitude_gps bank_name_clients bank_branch_clients employment_status_clients level_of_education_clients
0 8a858e135cb22031015cbafc76964ebd 1973-10-10 00:00:00.000000 Savings 3.319219 6.528604 1 1 Permanent NaN
1 8a858e275c7ea5ec015c82482d7c3996 1986-01-21 00:00:00.000000 Savings 3.325598 7.119403 2 1 Permanent NaN
2 8a858e5b5bd99460015bdc95cd485634 1987-04-01 00:00:00.000000 Savings 5.746100 5.563174 3 1 Permanent NaN
3 8a858efd5ca70688015cabd1f1e94b55 1991-07-19 00:00:00.000000 Savings 3.362850 6.642485 1 1 Permanent NaN
4 8a858e785acd3412015acd48f4920d04 1982-11-22 00:00:00.000000 Savings 8.455332 11.971410 1 1 Permanent NaN

更多編碼詳見:https://contrib.scikit-learn.org/categorical-encoding/

數值/連續特徵的處理

數值/連續特徵是數據集中最常見特徵的形式。關於此類特徵的工程往往基於領域知識,可做以下處理:
對數變換:中心化 (正則分佈),可讓大部分算法效果更好.
注意:當對目標特徵做對數變換時,在做模型解釋時需要加上指數變換。
對數變換常用於有偏特徵,或左偏或有偏,偏度可用圖像觀察。
這裏對sendy數據集的Distance 特徵做分析,爲右偏

sns.distplot(sendy_data['Distance (KM)'])
plt.title("Histogram of Distance (KM)")
plt.show()

在這裏插入圖片描述

sendy_data['Distance (KM)'] =  np.log1p(sendy_data['Distance (KM)']) # 使用加1後去對數,避免對0取對數
sns.distplot(sendy_data['Distance (KM)'])
plt.title("Log-Transformed plot of Distance (KM)")
plt.show()

在這裏插入圖片描述

使用領域知識構造特徵

當對數據的領域瞭解時,可以依此創建一些對模型有幫助的特徵,示例如下:
1、利息 Interest elapsed:爲總需還款金額totaldue 與貸款金額loanamount之差

loan_prev['interest_elapsed'] = loan_prev['totaldue'] - loan_prev['loanamount']
loan_prev['interest_elapsed']
0        3000.0
1        3000.0
2        3800.0
3        1500.0
4        1500.0
          ...  
18178    3000.0
18179    4400.0
18180    3000.0
18181    3000.0
18182    3000.0
Name: interest_elapsed, Length: 18183, dtype: float64
loan_prev
customerid systemloanid loannumber approveddate creationdate loanamount totaldue termdays closeddate referredby firstduedate firstrepaiddate interest_elapsed
0 8a2a81a74ce8c05d014cfb32a0da1049 301682320 2 2016-08-15 18:22:40.000000 2016-08-15 17:22:32.000000 10000.0 13000.0 30 2016-09-01 16:06:48.000000 NaN 2016-09-14 00:00:00.000000 2016-09-01 15:51:43.000000 3000.0
1 8a2a81a74ce8c05d014cfb32a0da1049 301883808 9 2017-04-28 18:39:07.000000 2017-04-28 17:38:53.000000 10000.0 13000.0 30 2017-05-28 14:44:49.000000 NaN 2017-05-30 00:00:00.000000 2017-05-26 00:00:00.000000 3000.0
2 8a2a81a74ce8c05d014cfb32a0da1049 301831714 8 2017-03-05 10:56:25.000000 2017-03-05 09:56:19.000000 20000.0 23800.0 30 2017-04-26 22:18:56.000000 NaN 2017-04-04 00:00:00.000000 2017-04-26 22:03:47.000000 3800.0
3 8a8588f35438fe12015444567666018e 301861541 5 2017-04-09 18:25:55.000000 2017-04-09 17:25:42.000000 10000.0 11500.0 15 2017-04-24 01:35:52.000000 NaN 2017-04-24 00:00:00.000000 2017-04-24 00:48:43.000000 1500.0
4 8a85890754145ace015429211b513e16 301941754 2 2017-06-17 09:29:57.000000 2017-06-17 08:29:50.000000 10000.0 11500.0 15 2017-07-14 21:18:43.000000 NaN 2017-07-03 00:00:00.000000 2017-07-14 21:08:35.000000 1500.0
... ... ... ... ... ... ... ... ... ... ... ... ... ...
18178 8a858899538ddb8e0153a2b555421fc5 301611754 2 2016-04-16 13:36:34.000000 2016-04-16 12:36:28.000000 10000.0 13000.0 30 2016-05-14 00:04:52.000000 NaN 2016-05-16 00:00:00.000000 2016-05-13 18:05:07.000000 3000.0
18179 8a858899538ddb8e0153a2b555421fc5 301761267 9 2016-11-18 14:26:07.000000 2016-11-18 13:25:51.000000 30000.0 34400.0 30 2016-12-13 16:08:57.000000 NaN 2016-12-19 00:00:00.000000 2016-12-13 15:53:48.000000 4400.0
18180 8a858899538ddb8e0153a2b555421fc5 301631653 4 2016-06-12 15:30:56.000000 2016-06-12 14:30:50.000000 10000.0 13000.0 30 2016-07-09 15:39:00.000000 NaN 2016-07-12 00:00:00.000000 2016-07-09 15:23:56.000000 3000.0
18181 8a858f0656b7820c0156c92ca3ba436f 301697691 1 2016-08-27 20:03:45.000000 2016-08-27 19:03:34.000000 10000.0 13000.0 30 2016-10-15 10:17:54.000000 NaN 2016-09-26 00:00:00.000000 2016-10-15 10:02:45.000000 3000.0
18182 8a858faf5679a838015688de3028143d 301715255 2 2016-09-14 23:42:14.000000 2016-09-14 22:42:05.000000 10000.0 13000.0 30 2016-09-29 19:51:04.000000 NaN 2016-10-14 00:00:00.000000 2016-09-29 19:35:55.000000 3000.0

18183 rows × 13 columns

2、貸款數量Loan count:根據顧客id分組,累計loannumber

loan_prev.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 18183 entries, 0 to 18182
Data columns (total 13 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   customerid        18183 non-null  object 
 1   systemloanid      18183 non-null  int64  
 2   loannumber        18183 non-null  int64  
 3   approveddate      18183 non-null  object 
 4   creationdate      18183 non-null  object 
 5   loanamount        18183 non-null  float64
 6   totaldue          18183 non-null  float64
 7   termdays          18183 non-null  int64  
 8   closeddate        18183 non-null  object 
 9   referredby        1026 non-null   object 
 10  firstduedate      18183 non-null  object 
 11  firstrepaiddate   18183 non-null  object 
 12  interest_elapsed  18183 non-null  float64
dtypes: float64(3), int64(3), object(7)
memory usage: 1.8+ MB
#Groupby customer id and calculate their total loans taken
loannumber_count = loan_prev.groupby(by='customerid').agg(['count'])[['loannumber']].reset_index()

#merge back to dataset on customer_id
loan_prev = loan_prev.merge(right=loannumber_count, how='left', on='customerid')
loan_prev.head()
customerid systemloanid loannumber approveddate creationdate loanamount totaldue termdays closeddate referredby firstduedate firstrepaiddate (loannumber, count)
0 8a2a81a74ce8c05d014cfb32a0da1049 301682320 2 2016-08-15 18:22:40.000000 2016-08-15 17:22:32.000000 10000.0 13000.0 30 2016-09-01 16:06:48.000000 NaN 2016-09-14 00:00:00.000000 2016-09-01 15:51:43.000000 11
1 8a2a81a74ce8c05d014cfb32a0da1049 301883808 9 2017-04-28 18:39:07.000000 2017-04-28 17:38:53.000000 10000.0 13000.0 30 2017-05-28 14:44:49.000000 NaN 2017-05-30 00:00:00.000000 2017-05-26 00:00:00.000000 11
2 8a2a81a74ce8c05d014cfb32a0da1049 301831714 8 2017-03-05 10:56:25.000000 2017-03-05 09:56:19.000000 20000.0 23800.0 30 2017-04-26 22:18:56.000000 NaN 2017-04-04 00:00:00.000000 2017-04-26 22:03:47.000000 11
3 8a8588f35438fe12015444567666018e 301861541 5 2017-04-09 18:25:55.000000 2017-04-09 17:25:42.000000 10000.0 11500.0 15 2017-04-24 01:35:52.000000 NaN 2017-04-24 00:00:00.000000 2017-04-24 00:48:43.000000 6
4 8a85890754145ace015429211b513e16 301941754 2 2017-06-17 09:29:57.000000 2017-06-17 08:29:50.000000 10000.0 11500.0 15 2017-07-14 21:18:43.000000 NaN 2017-07-03 00:00:00.000000 2017-07-14 21:08:35.000000 2
loan_prev.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 18183 entries, 0 to 18182
Data columns (total 13 columns):
 #   Column               Non-Null Count  Dtype  
---  ------               --------------  -----  
 0   customerid           18183 non-null  object 
 1   systemloanid         18183 non-null  int64  
 2   loannumber           18183 non-null  int64  
 3   approveddate         18183 non-null  object 
 4   creationdate         18183 non-null  object 
 5   loanamount           18183 non-null  float64
 6   totaldue             18183 non-null  float64
 7   termdays             18183 non-null  int64  
 8   closeddate           18183 non-null  object 
 9   referredby           1026 non-null   object 
 10  firstduedate         18183 non-null  object 
 11  firstrepaiddate      18183 non-null  object 
 12  (loannumber, count)  18183 non-null  int64  
dtypes: float64(2), int64(4), object(7)
memory usage: 1.9+ MB

3、速度Speed:發貨距離Distance (KM)與時間Time from Pickup to Arrival之比

#create feature speed in sendy dataset
sendy_data['speed'] = sendy_data['Distance (KM)'] / sendy_data['Time from Pickup to Arrival']
sendy_data.head().T
0 1 2 3 4
Order No Order_No_4211 Order_No_25375 Order_No_1899 Order_No_9336 Order_No_27883
User Id User_Id_633 User_Id_2285 User_Id_265 User_Id_1402 User_Id_1737
Vehicle Type Bike Bike Bike Bike Bike
Platform Type 3 3 3 3 1
Personal or Business Business Personal Business Business Personal
Placement - Day of Month 9 12 30 15 13
Placement - Weekday (Mo = 1) 5 5 2 5 1
Placement - Time 9:35:46 AM 11:16:16 AM 12:39:25 PM 9:25:34 AM 9:55:18 AM
Confirmation - Day of Month 9 12 30 15 13
Confirmation - Weekday (Mo = 1) 5 5 2 5 1
Confirmation - Time 9:40:10 AM 11:23:21 AM 12:42:44 PM 9:26:05 AM 9:56:18 AM
Arrival at Pickup - Day of Month 9 12 30 15 13
Arrival at Pickup - Weekday (Mo = 1) 5 5 2 5 1
Arrival at Pickup - Time 10:04:47 AM 11:40:22 AM 12:49:34 PM 9:37:56 AM 10:03:53 AM
Pickup - Day of Month 9 12 30 15 13
Pickup - Weekday (Mo = 1) 5 5 2 5 1
Pickup - Time 10:27:30 AM 11:44:09 AM 12:53:03 PM 9:43:06 AM 10:05:23 AM
Arrival at Destination - Day of Month 9 12 30 15 13
Arrival at Destination - Weekday (Mo = 1) 5 5 2 5 1
Arrival at Destination - Time 10:39:55 AM 12:17:22 PM 1:00:38 PM 10:05:27 AM 10:25:37 AM
Distance (KM) 1.60944 2.83321 1.38629 2.30259 2.30259
Temperature 20.4 26.4 23.7 19.2 15.4
Precipitation in millimeters NaN NaN NaN NaN NaN
Pickup Lat -1.31775 -1.35145 -1.30828 -1.2813 -1.2666
Pickup Long 36.8304 36.8993 36.8434 36.8324 36.7921
Destination Lat -1.30041 -1.295 -1.30092 -1.25715 -1.29504
Destination Long 36.8297 36.8144 36.8282 36.7951 36.8098
Rider Id Rider_Id_432 Rider_Id_856 Rider_Id_155 Rider_Id_855 Rider_Id_770
Time from Pickup to Arrival 745 1993 455 1341 1214
speed 0.00216032 0.00142158 0.0030468 0.00171707 0.00189669

多項式(交叉)特徵

多項式特徵是在特徵之間創建關聯,有助於捕捉特徵之間的關係,減少模型偏差(只要不過擬合).
可以通過特徵之間的加、乘、除來創建交叉特徵.
這裏用sklearn的特徵模塊,從loan previous 數據集的loannumber, totaldue,termdays 創建特徵

#Use Sklearn Polynomial Features
from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures()
to_cross = ['loannumber', 'totaldue', 'termdays']
crossed_feats = poly.fit_transform(loan_prev[to_cross].values)

#Convert to Pandas DataFrame and merge to original dataset
crossed_feats = pd.DataFrame(crossed_feats)
loan_prev = pd.concat([loan_prev, crossed_feats], axis=1)

loan_prev.head().T
0 1 2 3 4
customerid 8a2a81a74ce8c05d014cfb32a0da1049 8a2a81a74ce8c05d014cfb32a0da1049 8a2a81a74ce8c05d014cfb32a0da1049 8a8588f35438fe12015444567666018e 8a85890754145ace015429211b513e16
systemloanid 301682320 301883808 301831714 301861541 301941754
loannumber 2 9 8 5 2
approveddate 2016-08-15 18:22:40.000000 2017-04-28 18:39:07.000000 2017-03-05 10:56:25.000000 2017-04-09 18:25:55.000000 2017-06-17 09:29:57.000000
creationdate 2016-08-15 17:22:32.000000 2017-04-28 17:38:53.000000 2017-03-05 09:56:19.000000 2017-04-09 17:25:42.000000 2017-06-17 08:29:50.000000
loanamount 10000 10000 20000 10000 10000
totaldue 13000 13000 23800 11500 11500
termdays 30 30 30 15 15
closeddate 2016-09-01 16:06:48.000000 2017-05-28 14:44:49.000000 2017-04-26 22:18:56.000000 2017-04-24 01:35:52.000000 2017-07-14 21:18:43.000000
referredby NaN NaN NaN NaN NaN
firstduedate 2016-09-14 00:00:00.000000 2017-05-30 00:00:00.000000 2017-04-04 00:00:00.000000 2017-04-24 00:00:00.000000 2017-07-03 00:00:00.000000
firstrepaiddate 2016-09-01 15:51:43.000000 2017-05-26 00:00:00.000000 2017-04-26 22:03:47.000000 2017-04-24 00:48:43.000000 2017-07-14 21:08:35.000000
(loannumber, count) 11 11 11 6 2
0 1 1 1 1 1
1 2 9 8 5 2
2 13000 13000 23800 11500 11500
3 30 30 30 15 15
4 4 81 64 25 4
5 26000 117000 190400 57500 23000
6 60 270 240 75 30
7 1.69e+08 1.69e+08 5.6644e+08 1.3225e+08 1.3225e+08
8 390000 390000 714000 172500 172500
9 900 900 900 225 225

特徵標準化

標準化將數值特徵分佈修改到正常範圍,不破壞數據取值範圍和信息。
正則化對於基於距離的模型如KNN十分重要,也有助於提高神經網絡的訓練速度.
sklearn 提供的標準化函數:
StandardScaler: 減去均值、縮放到單位方差.
RobustScaler: 使用對異常值有魯棒性的統計信息來縮放特徵
MinMaxScaler: 將特徵縮放到指定範圍(範圍可自定義).
注意: 不要將標準化函數在測試/驗證集上訓練。
sklearn的 標準化函數對缺失值不具有魯棒性,因此使用前要做缺失值填充.

from sklearn.preprocessing import StandardScaler

feats = ['loannumber', 'totaldue', 'termdays']
sc = StandardScaler()
sc_data = sc.fit_transform(loan_prev[feats])
sc_data
array([[-0.67377132, -0.62877649,  0.30213166],
       [ 1.48047238, -0.62877649,  0.30213166],
       [ 1.17272328,  0.40432506,  0.30213166],
       ...,
       [-0.05827312, -0.62877649,  0.30213166],
       [-0.98152042, -0.62877649,  0.30213166],
       [-0.67377132, -0.62877649,  0.30213166]])
sc_data.shape
(18183, 3)
from sklearn.preprocessing import RobustScaler
robsc = RobustScaler()

rb_data = robsc.fit_transform(loan_prev[feats])
rb_data
array([[-0.25      ,  0.        ,  0.        ],
       [ 1.5       ,  0.        ,  0.        ],
       [ 1.25      ,  0.83076923,  0.        ],
       ...,
       [ 0.25      ,  0.        ,  0.        ],
       [-0.5       ,  0.        ,  0.        ],
       [-0.25      ,  0.        ,  0.        ]])
from sklearn.preprocessing import MinMaxScaler

minsc = MinMaxScaler(feature_range=(0,2))
minmax_data = minsc.fit_transform(loan_prev[feats])
minmax_data
array([[0.08      , 0.29543697, 0.4       ],
       [0.64      , 0.29543697, 0.4       ],
       [0.56      , 0.6295437 , 0.4       ],
       ...,
       [0.24      , 0.29543697, 0.4       ],
       [0.        , 0.29543697, 0.4       ],
       [0.08      , 0.29543697, 0.4       ]])

日期特徵處理

對時間特徵可做很多處理,如時間差
對loan performance 數據集的申請時間creationdate 和審批通過時間做處理

#First convert to pandas datetime format
loan_perf['approveddate'] = pd.to_datetime(loan_perf['approveddate'])
loan_perf['creationdate'] = pd.to_datetime(loan_perf['creationdate'])
loan_perf['date_elapsed_in_secs'] = (loan_perf['approveddate'] - loan_perf['creationdate']) / np.timedelta64(1,'s') #can subtitute with [h,m,s]
loan_perf.head()
customerid systemloanid loannumber approveddate creationdate loanamount totaldue termdays referredby good_bad_flag date_elapsed_in_secs
0 8a2a81a74ce8c05d014cfb32a0da1049 301994762 12 2017-07-25 08:22:56 2017-07-25 07:22:47 30000.0 34500.0 30 NaN 0 3609.0
1 8a85886e54beabf90154c0a29ae757c0 301965204 2 2017-07-05 17:04:41 2017-07-05 16:04:18 15000.0 17250.0 30 NaN 0 3623.0
2 8a8588f35438fe12015444567666018e 301966580 7 2017-07-06 14:52:57 2017-07-06 13:52:51 20000.0 22250.0 15 NaN 0 3606.0
3 8a85890754145ace015429211b513e16 301999343 3 2017-07-27 19:00:41 2017-07-27 18:00:35 10000.0 11500.0 15 NaN 0 3606.0
4 8a858970548359cc0154883481981866 301962360 9 2017-07-03 23:42:45 2017-07-03 22:42:39 40000.0 44000.0 30 NaN 0 3606.0

以下展示如何提取時間特徵如:天、周、小時、秒等:

#First convert to pandas datetime format
loan_perf['approveddate'] = pd.to_datetime(loan_perf['approveddate'])

#use pandas built in functions
loan_perf['approved_day'] = loan_perf['approveddate'].dt.day
loan_perf['approved_week'] = loan_perf['approveddate'].dt.week
loan_perf['approved_hour'] = loan_perf['approveddate'].dt.hour

Pandas date特徵還有其他屬性可嘗試.
還可以提取一天中的階段(morning, afternoon, evenings)

def map_hours(x):
    if x in [0,1,2,3,4,5,6,7,8,9,10,11,12]:
        return 'morning'
    elif x in [13,14,15,16]:
        return 'afternoon'
    else:
        return 'evening'
    
loan_perf['period_of_day'] = loan_perf['approved_hour'].map(map_hours)
loan_perf.head()
customerid systemloanid loannumber approveddate creationdate loanamount totaldue termdays referredby good_bad_flag date_elapsed_in_secs approved_day approved_week approved_hour period_of_day
0 8a2a81a74ce8c05d014cfb32a0da1049 301994762 12 2017-07-25 08:22:56 2017-07-25 07:22:47 30000.0 34500.0 30 NaN 0 3609.0 25 30 8 morning
1 8a85886e54beabf90154c0a29ae757c0 301965204 2 2017-07-05 17:04:41 2017-07-05 16:04:18 15000.0 17250.0 30 NaN 0 3623.0 5 27 17 evening
2 8a8588f35438fe12015444567666018e 301966580 7 2017-07-06 14:52:57 2017-07-06 13:52:51 20000.0 22250.0 15 NaN 0 3606.0 6 27 14 afternoon
3 8a85890754145ace015429211b513e16 301999343 3 2017-07-27 19:00:41 2017-07-27 18:00:35 10000.0 11500.0 15 NaN 0 3606.0 27 30 19 evening
4 8a858970548359cc0154883481981866 301962360 9 2017-07-03 23:42:45 2017-07-03 22:42:39 40000.0 44000.0 30 NaN 0 3606.0 3 27 23 evening

datasist庫還有更多時間處理函數,詳見:https://towardsdatascience.com/https-medium-com-risingdeveloper-easy-data-analysis-visualization-and-modeling-using-datasist-part1-8b26526dbe01

地理位置特徵處理

數據集中的經緯度,地址都是地理位置特徵.
對於經緯度特徵可以做很多處理,可以用Geojson或 Geopy的庫將位置特徵值轉爲地圖上的地址.
但這類方法很慢,本文展示更簡單且更快的方法
以下示例來自於Kaggle.
曼哈頓距離: 兩點間的水平距離和垂直距離之和

#曼哈頓距離
def manhattan_distance(lat1, lng1, lat2, lng2):
    a = np.abs(lat2 -lat1)
    b = np.abs(lng1 - lng2)
    return a + b
sendy_data['manhattan_dist'] = manhattan_distance(sendy_data['Pickup Lat'].values, sendy_data['Pickup Long'].values,
                                               sendy_data['Destination Lat'].values, sendy_data['Destination Long'].values)
sendy_data.head()
Order No User Id Vehicle Type Platform Type Personal or Business Placement - Day of Month Placement - Weekday (Mo = 1) Placement - Time Confirmation - Day of Month Confirmation - Weekday (Mo = 1) ... Temperature Precipitation in millimeters Pickup Lat Pickup Long Destination Lat Destination Long Rider Id Time from Pickup to Arrival speed manhattan_dist
0 Order_No_4211 User_Id_633 Bike 3 Business 9 5 9:35:46 AM 9 5 ... 20.4 NaN -1.317755 36.830370 -1.300406 36.829741 Rider_Id_432 745.0 0.002160 0.017978
1 Order_No_25375 User_Id_2285 Bike 3 Personal 12 5 11:16:16 AM 12 5 ... 26.4 NaN -1.351453 36.899315 -1.295004 36.814358 Rider_Id_856 1993.0 0.001422 0.141406
2 Order_No_1899 User_Id_265 Bike 3 Business 30 2 12:39:25 PM 30 2 ... 23.7 NaN -1.308284 36.843419 -1.300921 36.828195 Rider_Id_155 455.0 0.003047 0.022588
3 Order_No_9336 User_Id_1402 Bike 3 Business 15 5 9:25:34 AM 15 5 ... 19.2 NaN -1.281301 36.832396 -1.257147 36.795063 Rider_Id_855 1341.0 0.001717 0.061487
4 Order_No_27883 User_Id_1737 Bike 1 Personal 13 1 9:55:18 AM 13 1 ... 15.4 NaN -1.266597 36.792118 -1.295041 36.809817 Rider_Id_770 1214.0 0.001897 0.046143

5 rows × 31 columns

Haversine距離:是兩點在球體上的距離, 在導航中很重要.

#Haversine distance
def haversine_array(lat1, lng1, lat2, lng2):
    lat1, lng1, lat2, lng2 = map(np.radians, (lat1, lng1, lat2, lng2))
    AVG_EARTH_RADIUS = 6371  # in km
    lat = lat2 - lat1
    lng = lng2 - lng1
    d = np.sin(lat * 0.5) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(lng * 0.5) ** 2
    h = 2 * AVG_EARTH_RADIUS * np.arcsin(np.sqrt(d))
    return h
sendy_data['haversine_dist'] = haversine_array(sendy_data['Pickup Lat'].values, sendy_data['Pickup Long'].values,
                                                   sendy_data['Destination Lat'].values, sendy_data['Destination Long'].values)
sendy_data.head()
Order No User Id Vehicle Type Platform Type Personal or Business Placement - Day of Month Placement - Weekday (Mo = 1) Placement - Time Confirmation - Day of Month Confirmation - Weekday (Mo = 1) ... Precipitation in millimeters Pickup Lat Pickup Long Destination Lat Destination Long Rider Id Time from Pickup to Arrival speed manhattan_dist haversine_dist
0 Order_No_4211 User_Id_633 Bike 3 Business 9 5 9:35:46 AM 9 5 ... NaN -1.317755 36.830370 -1.300406 36.829741 Rider_Id_432 745.0 0.002160 0.017978 1.930333
1 Order_No_25375 User_Id_2285 Bike 3 Personal 12 5 11:16:16 AM 12 5 ... NaN -1.351453 36.899315 -1.295004 36.814358 Rider_Id_856 1993.0 0.001422 0.141406 11.339849
2 Order_No_1899 User_Id_265 Bike 3 Business 30 2 12:39:25 PM 30 2 ... NaN -1.308284 36.843419 -1.300921 36.828195 Rider_Id_155 455.0 0.003047 0.022588 1.880079
3 Order_No_9336 User_Id_1402 Bike 3 Business 15 5 9:25:34 AM 15 5 ... NaN -1.281301 36.832396 -1.257147 36.795063 Rider_Id_855 1341.0 0.001717 0.061487 4.943458
4 Order_No_27883 User_Id_1737 Bike 1 Personal 13 1 9:55:18 AM 13 1 ... NaN -1.266597 36.792118 -1.295041 36.809817 Rider_Id_770 1214.0 0.001897 0.046143 3.724829

5 rows × 32 columns

  1. Bearing角度: 是從起始點到目的地的轉向角度, 必須在 0 到 360之間.
#Bearing
def bearing_array(lat1, lng1, lat2, lng2):
    AVG_EARTH_RADIUS = 6371  # in km
    lng_delta_rad = np.radians(lng2 - lng1)
    lat1, lng1, lat2, lng2 = map(np.radians, (lat1, lng1, lat2, lng2))
    y = np.sin(lng_delta_rad) * np.cos(lat2)
    x = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(lng_delta_rad)
    return np.degrees(np.arctan2(y, x))
sendy_data['bearing'] = bearing_array(sendy_data['Pickup Lat'].values, sendy_data['Pickup Long'].values,
                                                   sendy_data['Destination Lat'].values, sendy_data['Destination Long'].values)
sendy_data.head()
Order No User Id Vehicle Type Platform Type Personal or Business Placement - Day of Month Placement - Weekday (Mo = 1) Placement - Time Confirmation - Day of Month Confirmation - Weekday (Mo = 1) ... Pickup Lat Pickup Long Destination Lat Destination Long Rider Id Time from Pickup to Arrival speed manhattan_dist haversine_dist bearing
0 Order_No_4211 User_Id_633 Bike 3 Business 9 5 9:35:46 AM 9 5 ... -1.317755 36.830370 -1.300406 36.829741 Rider_Id_432 745.0 0.002160 0.017978 1.930333 -2.076903
1 Order_No_25375 User_Id_2285 Bike 3 Personal 12 5 11:16:16 AM 12 5 ... -1.351453 36.899315 -1.295004 36.814358 Rider_Id_856 1993.0 0.001422 0.141406 11.339849 -56.392163
2 Order_No_1899 User_Id_265 Bike 3 Business 30 2 12:39:25 PM 30 2 ... -1.308284 36.843419 -1.300921 36.828195 Rider_Id_155 455.0 0.003047 0.022588 1.880079 -64.183866
3 Order_No_9336 User_Id_1402 Bike 3 Business 15 5 9:25:34 AM 15 5 ... -1.281301 36.832396 -1.257147 36.795063 Rider_Id_855 1341.0 0.001717 0.061487 4.943458 -57.091553
4 Order_No_27883 User_Id_1737 Bike 1 Personal 13 1 9:55:18 AM 13 1 ... -1.266597 36.792118 -1.295041 36.809817 Rider_Id_770 1214.0 0.001897 0.046143 3.724829 148.114398

5 rows × 33 columns

中心點:計算兩點經緯度之間的中點

#Get center of lat and longitude
sendy_data['center_latitude'] = (sendy_data['Pickup Lat'].values + sendy_data['Destination Lat'].values) / 2
sendy_data['center_longitude'] = (sendy_data['Pickup Long'].values + sendy_data['Destination Long'].values) / 2
sendy_data.head()
Order No User Id Vehicle Type Platform Type Personal or Business Placement - Day of Month Placement - Weekday (Mo = 1) Placement - Time Confirmation - Day of Month Confirmation - Weekday (Mo = 1) ... Destination Lat Destination Long Rider Id Time from Pickup to Arrival speed manhattan_dist haversine_dist bearing center_latitude center_longitude
0 Order_No_4211 User_Id_633 Bike 3 Business 9 5 9:35:46 AM 9 5 ... -1.300406 36.829741 Rider_Id_432 745.0 0.002160 0.017978 1.930333 -2.076903 -1.309080 36.830056
1 Order_No_25375 User_Id_2285 Bike 3 Personal 12 5 11:16:16 AM 12 5 ... -1.295004 36.814358 Rider_Id_856 1993.0 0.001422 0.141406 11.339849 -56.392163 -1.323229 36.856837
2 Order_No_1899 User_Id_265 Bike 3 Business 30 2 12:39:25 PM 30 2 ... -1.300921 36.828195 Rider_Id_155 455.0 0.003047 0.022588 1.880079 -64.183866 -1.304603 36.835807
3 Order_No_9336 User_Id_1402 Bike 3 Business 15 5 9:25:34 AM 15 5 ... -1.257147 36.795063 Rider_Id_855 1341.0 0.001717 0.061487 4.943458 -57.091553 -1.269224 36.813730
4 Order_No_27883 User_Id_1737 Bike 1 Personal 13 1 9:55:18 AM 13 1 ... -1.295041 36.809817 Rider_Id_770 1214.0 0.001897 0.046143 3.724829 148.114398 -1.280819 36.800968

5 rows × 35 columns

datasist庫有更多用法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章