Home avatar

"There is only one heroism in the world: to see the world as it is, and to love it." - Romain Rolland

淺談Python的複製

這裡用三個不同list來示範不同copy的差別:

1
2
3
4
import copy
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]

  1. Using normal assignment operatings to copy:
    1
    2
    3
    
    d = c
    print (id(c) == id(d))          # True - d和c是相同object
    print (id(c[0]) == id(d[0]))    # True - d[0]和c[0]是相同object
  2. Using a shallow copy:
    1
    2
    3
    
    d = copy.copy(c)
    print (id(c) == id(d))          # False - d是新的object,跟c不同
    print (id(c[0]) == id(d[0]))    # True - d[0]和c[0]是相同object
  3. Using a deep copy:
    1
    2
    3
    
    d = copy.deepcopy(c)
    print (id(c) == id(d))          # False - d是新的object,跟c不同
    print (id(c[0]) == id(d[0]))    # False - d[0]是新的object,跟c[0]不同

Pandas ufuncs小技巧

Pandasufuncs為什麼比apply command還建議使用?

Pandas 有一個apply function讓你可以針對所有在column的值執行任何functions。注意apply只是比python內建的loop還要快一點點而已!這就是為什麼pandas的內建ufuncs比較推薦使用在columns的預處理(preprocessing)。
ufuncs是特殊functions(建構在numpy的library)裡面並由C來實行,這就是為何ufuncs會如此之快。以下會介紹幾種ufuncs的例子(.diff, .shift, .cumsum, .cumcount, .str commands (作用在字串), .dt commands (作用在日期))

這裡透過下面的數據集來演示pandas的ufuncs(同一個人可以在不同的時間軸上進行不同的活動) pandas_ufuncs_dataset

這裏假設我們的任務是要基於上面的數據集去預測誰是最有趣的同學

pandas_ufuncs_dataset 如果我們想要對字串做切割的話,string commands (which are Ufuncs)是最推薦的:

1
df['name'] = df.name.str.split(" ", expand=True)
pandas_ufuncs_dataset 除此之外你可以用pandas.Series.str.replace來更有效的清理字串。

透過groupbyvalue_counts我們可以輕鬆數出每個人做過多少次活動:

1
df.groupby('name')['activity'].value_counts()
pandas_ufuncs_dataset 這稱為multi index,它可以讓我們同時在dataframe中擁有不同的index層,在圖片中人名就是level 0而activity是level 1。

我們也可以創建每個人的活動計數特徵,透過unstack方法,可以將行與列互換,unstack會把最低level的index轉換成cloumns,每個人的活動計數會變成cloumns,對於沒有從事該活動的人欄位會維持缺失值NaN,可以對其進行缺失值填補。

1
df.groupby('name')['activity'].value_counts().unstack().fillna(0)
pandas_ufuncs_dataset

如果能了解一個人在活動中從事的時間,必定能對我們了解誰是最有趣的人有幫助。誰在party待最久?誰在海邊待最久? 對於時間長短最有效的推算方式就是先使用groupby歸類人名,再用diff()算出時間差:

1
2
df = df.sort_values(by=['name','timestamp'])
df['time_diff'] = df.groupby('name')['timestamp'].diff()
pandas_ufuncs_dataset 如果你有大量的數據集,你可以跳過groupby,只做資料排序,刪除每個人的第一行(不相關的)後直接用diff
1
2
3
df = df.sort_values(by=['name','timestamp'])
df['time_diff'] = df['timestamp'].diff()
df.loc[df.name != df.name.shift(), 'time_diff'] = None
順便一提 ⎯ .shift可以將每一行向下移動ㄧ格,所以我們就可以用df.name!=df.name.shift()看看哪一行有改變。 然後.loc是最推薦用來在特定的indices的columns中set values的選擇!
接著我們把time_diff單位改成秒:
1
df['time_diff'] = df.time_diff.dt.total_seconds()
獲取每個row的持續時間:
1
df['row_duration'] = df.time_diff.shift(-1)
pandas_ufuncs_dataset

Kaggle Tatanic Stacking 學習模型整理

在看過Kaggle上Titanic的一些kernels,其中不乏用SVM, RandomForest, LogisticRegression, etc. 而這Kernel有趣的是他利用6種不同的learning models去建模。
Introduction to Ensembling/Stacking in Python Using data from Titanic: Machine Learning from Disaster
在Level 1用了RandomForestClassifier, AdaBoostClassifier, GradientBoostingClassifier, ExtraTreesClassifier, SVM,而Level 2用了XGBoost。 我大致畫出整個模型的流程架構,以便於理解,光看源碼其實很難短時間理解在幹嘛,作者靈活的用了class來降低在jupyter notebook內程式碼的複雜度,方便之後修改和整理。

tatanic_stacking_flow

這裡最重要的是數據處理,Kaggle只給了我們兩份資料:train & test,其中train.shape = (891, 12),test.shape = (418,11)。 用交叉驗證切割train之後會有178 x 5的數據集,由於我們n-fold有五組,Model也有五個,這裡容易腦袋打結。所以我們先用model 1(隨機森林)舉例:train切成五組之後丟給隨機森林訓練,第一組有713人當訓練集,178人當驗證集(validation set),第二組則換成另一批713人當訓練集,178人當驗證集,以此類推。驗證出來的答案打包成一組178 x 4+179剛好成為891的et_oof_train(注意!KFold把我們的資料切成5份時,實際上是把第一份切成712 x 179,其餘切成713 x 178,我就是卡在這裡..囧)。 當我們對每個模型這樣做的時候,就得到五個out-of-fold_train,結合在一起就變成891 x 5的訓練集(就是XGBoost的訓練集)! 那XGBoost的測試集哪裡來?再用剛剛隨機森林做例子:我們把訓練集分成五組,所訓練出來的隨機森林就有5個,現在就用5個隨機森林去對test預測,所預測出來的結果就是之5 x 418! 我們在將其求平均並reshape(-1, 1),所得到的結果是(418,)的ndarray,數值介在0~1之間。 又我們有5個models,所以最後的x_test有418 x 5個數。

最後在XGBoost中剩下調參數了,放一下作者的參數做參考

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
gbm = xgb.XGBClassifier(
 #learning_rate = 0.02,
 n_estimators= 2000,
 max_depth= 4,
 min_child_weight= 2,
 #gamma=1,
 gamma=0.9, 
 subsample=0.8,
 colsample_bytree=0.8,
 objective= binary:logistic,
 nthread= -1,
 scale_pos_weight=1).fit(x_train, y_train)
predictions = gbm.predict(x_test)
這樣基本上就完成我們的stacking模型了~ 如有錯誤歡迎指正

Pandas cut與qcut函數

如果我們今天有一些連續性的數值,可以使用cutqcut進行離散化。 cut函数是利用數值區間將數值分類,qcut則是用分位數。 換句話說,cut用在長度相等的類別,qcut用在大小相等的類別。

假設我們有一些人的年齡
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32, 101]
我們如果想要離散化這些數列,分成“18到25”、“25到35”、“35到60”以及“60以上”,可以使用cut函數

1
2
3
4
5
6
7
bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins)
cats
output:
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (60, 100], (35, 60], (35, 60], (25, 35], NaN]
Length: 13
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
第一個list是指每個年齡分別在哪個範圍內,如果超出了就變成NaN缺失值,cats有兩個屬性:
1
2
3
cats.labels
output:
array([ 0,  0,  0,  1,  0,  0,  2,  1,  3,  2,  2,  1, -1], dtype=int8)
我們也可以賦予範圍標籤,比如:
1
2
3
4
5
6
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)
output:
[Youth, Youth, Youth, YoungAdult, Youth, ..., Senior, MiddleAged, MiddleAged, YoungAdult, NaN]
Length: 13
Categories (4, object): [MiddleAged < Senior < YoungAdult < Youth]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
data = np.random.randn(1000) #高斯分佈
cats = pd.qcut(data, 4) #按四分位數分類,也可以用[0, .25, .5, .75, 1.]
cats
output:
[(0.624, 3.928], (-0.691, -0.0144], (-0.691, -0.0144], (-0.0144, 0.624], (0.624, 3.928], ..., (-0.0144, 0.624], (-0.0144, 0.624], [-2.949, -0.691], (-0.0144, 0.624], (0.624, 3.928]] Length: 1000 Categories (4, object): [[-2.949, -0.691] < (-0.691, -0.0144] < (-0.0144, 0.624] < (0.624, 3.928]]
pd.value_counts(cats) #計算每個區間的數值個數
output:
(0.624, 3.928]       250 
(-0.0144, 0.624]     250 
(-0.691, -0.0144]    250 
[-2.949, -0.691]     250 
dtype: int64
會發現qcut把所有數值平均分配了,當然如果不想用四分位的話還可以自行輸入list,只要範圍介在0~1,例如[0, 0.1, 0.5, 0.9, 1.]