第一次作業
第一題
題目描述
現已使用 Pandas 讀取數據集 challenge.csv
- 請提取該數據集的字段名稱,將結果存為 cols
- 請獲取給數據的字段和樣本數量,將結果分別存為 col_num 和 sam_num
- 請獲取該數據集的前五行記錄,將最后的 DataFrame 存為 five_data
代碼:
import pandas as pd
path = "./data/insurance.csv"
titannic = pd.read_csv(path) #將這個csv文件讀取進來
'''查看該數據集的前2條數據'''
# print(titannic.head(0)) #看一下前20行
'''提取該數據集的字段名稱,將結果保存為cols'''
cols = titannic.columns
cols = [col.lower() for col in cols]
print(cols)
'''獲取給出的數據的字段和樣本數量,將結果分別存為col_num和sam_num'''
col_num = titannic.shape[1]
sam_num = titannic.shape[0]; sam_num = len(titannic)
print("col_num is:", col_num)
print("sam_num is: ", sam_num)
'''獲取數據集的前五行記錄,將最后的DataFrame存為five_data'''
five_data = titannic.head(5)
print(five_data)
總結與思考
- pandas庫中里面有很多封裝好的對表格數據進行操作的函數;
- pd.read_csv(path):讀取path文件,創建一個pandas對象;
- head(x):讀取數據中的前面x行數據;
- .columns: 讀取數據集中的字段
- .shape:獲取數據集中的(行,列)
- len(pandas對象):得到這個對象的行的數量
第二題
題目描述
使用 scipy 庫中的 stats 模塊,對生成的數據進行正態性檢驗,將檢驗的結果存為 model
代碼:
import numpy as np
from scipy import stats
test_data = np.random.random(size = 100)
'''輸出結果中第一個為統計量,第二個為P值(統計量越接近1越表明數據和正態分布擬合的好,
P值大於指定的顯著性水平,接受原假設,認為樣本來自服從正態分布的總體)'''
model = stats.shapiro(test_data); print(model)
'''輸出結果中第一個為統計量,第二個為P值(注:統計量越接近0就越表明數據和標准正態分布擬合的越好,
如果P值大於顯著性水平,通常是0.05,接受原假設,則判斷樣本的總體服從正態分布)'''
model = stats.kstest(test_data, 'norm')
print(model)
'''輸出結果中第一個為統計量,第二個為P值(注:p值大於顯著性水平0.05,認為樣本數據符合正態分布)'''
model = stats.normaltest(test_data)
print(model)
總結與思考
- 有三個函數都可以進行正態分布檢驗,分別為:stats.shapiro(), stats.kstest(), stats.normaltest()
第三題
題目描述
3. 下列屬於衡量數據整體散度的是(b,c):
a. 歐式距離
b. 標准差
c. 分位數
d. 眾數
總結與思考
- 歐氏距離:L2范式,絕對距離,是歐幾里得空間中兩點間“普通”(即直線)距離;根據公式,我認為這個是用來衡量兩個樣本之間的距離。
- 算術平均值,中值,最大值,最小值,分位數,方差都分別是一種度量 數據中心趨勢和離散程度,描述數據匯總的圖形顯示 的手段
第四題
題目描述
現已使用 Pandas 生成 Series 對象 example_data
- 請使用 isnull()函數確定 example_data 是否含有缺失值,將最后的結果存為 boolean_array
- 請使用 fillna()函數使用字符串 missing 替換缺失值,將替換后的 Series 對象存為 new_data
代碼
import pandas as pd
import numpy as np
example_data = pd.Series([1,2,3,np.nan,4])
# 判斷是否含有缺失值
boolean_array = example_data.isnull()
print(boolean_array)
# 缺失值替換
new_data = example_data.fillna("missing")
print(new_data)
總結與思考
- Pandas生成的數據集對象可以直接調用isnull()函數,isnull()函數對於此處為缺失值,返回True,此處不為缺失值,則返回False
- DataFrame.fillna();可以將所有NaN元素進行替換,可以向前或向后傳播非null值,也可以指定填充連續數量的NaN元素。
第五題
題目描述
現已使用 Pandas 讀取數據集 birthrate.csv
請對該數據集的 birth_rates 特征使用四分位數作為切分點,通過 qcut()函數完成等頻離散化; 將最后的結果存為 data_qcut
該數據集詳情為
代碼
import pandas as pd
data = pd.read_csv('./data/birthrate.csv')
#請在下面作答
data_qcut = pd.qcut(data["birth_rates"], [0,0.25,0.5,0.75,1])
print(data_qcut)
data_qcut = pd.qcut(data["birth_rates"], 4)
print(data_qcut)
總結
- pd.qcut(x, q, labels=None, retbins=False, precision=3, duplicates='raise');x :一維數組或者Serise;q : 表示分位數的整數或者數組;如果是分位數的整數,例如10用於十分位,4用於四分位;如果是分位數數組,例如[0,0.25,0.5,0.75,1]用於四分位數
- 等頻離散化,將特征分成k個區間,每個區間里面的樣本數量是一樣的(區間長度可能不一致)。等距離散化,將特征分成k個區間,每個區間長度一樣。等頻離散化的好處:基於模型需要啊,更加容易理解等缺點:容易使相同的特征值分到不同的區間。
第六題
題目描述
實現
總結與思考
最小二乘法和加大似然估計方法,最后都變成了對同一個函數求其偏導,由於這個線性模型是可以直接使用求偏導的形式求出其解析解;但是像一些混合模型,則不能直接得到解析解,例如高斯混合模型,這時候對於含有隱變量的模型,可以考慮使用EM算法等方法通過求其近似解來逼近其最優解。
第七題
題目描述
實現1
這里我是直接使用了最小二乘法進行求解;通過最小二乘法,分別求出參數a和參數b的偏導,得到a與b的解析解;代碼中a與b我通過直接表達其算式,來得到;求出a與b之后,直接帶入方程;
最后,通過描出數據集的點()
畫出確定了a與b擬合的方程
import matplotlib.pyplot as plt
# 數據
X = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12]
Y = [42, 44, 51, 48, 51, 54, 57, 54, 57, 63, 61, 69, 70, 70, 70, 72, 74, 83, 84, 81, 84, 85, 91, 86, 91, 95]
# 計算X,Y的平均值
Y_ = sum(Y) / len(Y); X_ = sum(X) / len(X)
# 計算Y = a + b * X 中的 a 與 b
temp_mu = 0; temp_zi = 0
for tmp_zip in zip(X, Y):
x = tmp_zip[0]; y = tmp_zip[1]
temp_mu = x * x + temp_mu
temp_zi = x * y + temp_zi
b = (temp_zi - len(X) * X_ * Y_) / (temp_mu - len(X) * X_ * X_)
a = Y_ - b * X_
print(a); print(b)
# 計算預測值
Y_predict = []
for x in X:
tmp_y = a + b * x
Y_predict.append(tmp_y)
# 畫出藍色的數據點,紅色的擬合曲線
plt.scatter(X, Y, c="blue")
plt.plot(X, Y_predict, c="red")
plt.show()
'''代碼是可以直接運行的'''
實現二
直接調用sklearn中的LinearRegression
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import numpy as np
X = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,12, 12]
Y = [42, 44, 51, 48, 51, 54, 57, 54, 57, 63, 61, 69, 70, 70, 70, 72, 74, 83, 84, 81, 84, 85, 91, 86, 91, 95]
# LinearRegression需要列式數據
X_train = np.array(X).reshape(len(X), 1)
Y_train = np.array(Y).reshape(len(Y), 1)
'''求解線性回歸方程參數時,首先判斷訓練集X是否是稀疏矩陣,如果是,就用Golub&Kanlan雙對角線化過程方法來求解;
否則調用C庫中LAPACK中的用基於分治法的奇異值分解來求解;進行訓練,sklearn中並不是使用梯度下降法求解線性回歸,
而是使用最小二乘法求解(源碼里面可以看到'''
'''fit_intercept:模型是否存在截距
normalize:模型是否對數據進行標准化(在回歸之前,對X減去平均值再除以二范數),如果fit_intercept被設置為False時,該參數將忽略。
該函數有屬性:coef_可供查看模型訓練后得到的估計系數,如果獲取的估計系數太大,說明模型有可能過擬合。'''
LineModule = LinearRegression()
LineModule.fit(X_train, Y_train)
#進行預測
Y_predict = LineModule.predict(X_train)
#畫圖
plt.scatter(X, Y, c = "blue")
plt.plot(X, Y_predict, c = "red")
plt.show()
'''可以直接運行'''
總結與思考
- 求解線性回歸方程參數時,首先判斷訓練集X是否是稀疏矩陣,如果是,就用Golub&Kanlan雙對角線化過程方法來求解;否則調用C庫中LAPACK中的用基於分治法的奇異值分解來求解;進行訓練,sklearn中並不是使用梯度下降法求解線性回歸,而是使用最小二乘法求解。
- fit_intercept:模型是否存在截距
normalize:模型是否對數據進行標准化(在回歸之前,對X減去平均值再除以二范數),如果fit_intercept被設置為False時,該參數將忽略。 - coef_可供查看模型訓練后得到的估計系數,如果獲取的估計系數太大,說明模型有可能過擬合。
- 我手動實現的和直接調庫的結果圖是一樣的。
第八題
題目描述
實現
- 這道題目,我通過求出題目中f(x)的原函數f(x);從而求f(x)=0,變成了求F(x)的極值點;(這樣就可以使用到梯度下降法)
- 首先觀察,可以發現x = 0是在所有極值點的左邊的,然后F(x)函數只有三個極值點;
- 我首先去確定了x的初始值為x1 = 0;學習率 = 0.1;通過X_fi來存儲迭代過程中找到的極值點;
- 確定了初始值和學習率后;就需要開始迭代了,由於F(X)有三個極值點,通過觀察其有着 極小值點,極大值點,極小值點;所以,x1 = x1 - (gradient * learning_rate),x1 = x1 + (gradient * learning_rate),x1 = x1 - (gradient * learning_rate);我通過設置了一個布爾類型f1來控制算式的變換;
- 在實現的過程中,還有一個很重要的問題是,當找到一個極值點后,如何跳躍到后邊的點以找到下一個極值點;對於這個問題,我采用的方法是:第一次中,我找到斜率的絕對值接近0.3,將這個時候的x記錄下來;當找到第一個極值點后,通過其對稱性,對稱到另一邊(x1 = x1 + (x1 - x01)),此時記錄下跳躍后的點,為下一次跳躍做准備;下一次同理,依然是記錄上一次跳躍的點,即x01。
- 在上一個步驟中,值得一提的是為什么我指定斜率為0.3;這個地方,我是直接去調試了代碼,發現每次跳躍后,他們的斜率的絕對值都存在着大於0.3的情況(大概是0.3...,小於0.4),所以我就直接指定0.3了(最開始我指定的是1,然后沒得到答案,后來發現原來后邊的斜率絕對值就全部都小於1)
- 由於知道這個函數的極值點數量為3,所以當我找到了3個極值點后,我就直接終止迭代,最終得到最后的答案
- 梯度下降的過程,我使用藍色的點繪畫了出來,並使用了藍色的線鏈接了起來;為了方便觀察,我使用了紅色的線畫出了F(X)的圖像。
- 最后的三個答案,我在控制台打印了出來
答案是:x = 1, x = 2, x = 3 (經過驗證,是正確的)
import matplotlib.pyplot as plt
# 初始化x1
x1 = 0
x01 = x1
learning_rate = 0.1
X_fi = []
X = []; Y = []
f1 = True
cnt = 0
def f(x):
return 0.25 * (x ** 4) - 2 * (x ** 3) + 11 * (x ** 2) / 2 - 6 * x
def qiudao(x):
return x * x * x - 6 * x * x + 11 * x - 6
for i in range(1000):
X.append(x1); Y.append(f(x1))
if(cnt == 3):
break
dao_x1 = qiudao(x1)
print("dao_x1: ", dao_x1)
if(cnt == 0):
if(abs(dao_x1) >= 0.3):
x01 = x1
if(f1):
x1 = x1 - learning_rate * dao_x1
if(dao_x1 >= -1e-4):
f1 = False
X_fi.append(x1)
cnt += 1
x1 = x1 + (x1 - x01)
x01 = x1
else:
x1 = x1 + learning_rate * dao_x1
if(dao_x1 <= 1e-4):
f1 = True
X_fi.append(x1)
cnt += 1
x1 = x1 + (x1 - x01)
x01 = x1
print(X_fi)
'''畫圖'''
# 函數
X_f, Y_f = [], []
x = -0.2
while(x <= 3.7):
X_f.append(x)
Y_f.append(f(x))
x += 0.01
plt.plot(X_f, Y_f, c = "red")
# 梯度下降過程
plt.scatter(X, Y, s = 12, c = "blue")
plt.plot(X, Y, c = "blue")
plt.show()
總結與思考
- 梯度下降是一種通過迭代的方法來找到函數的局部最值點;但是當函數有多個極值點時,需要去改變迭代的符號,因為每次尋找到的梯度方向是數值增加最快的方向;所以,極小值點要減去它,極大值點要加上它;
- 梯度下降的過程中,學習率和迭代次數都是超參數,有時候需要進行調試,才能找到一個合適的(我第一次設置成0.001,10000000,跑了很久沒跑出來)
- 在這次的作業中,我是手寫的(以前沒寫過,寫起來還是花了很長時間,算法思路也變換了幾次,最后才找到這個我覺得比較合適的);
- 應該還有矩陣計算的方式來求解(更具有普遍性,因為需要考慮多元函數的情況),通過調用numpy以及封裝好的庫,由於時間關系,我就沒有繼續去寫這個版本的代碼了,后邊有時間需要補上。
第九題
題目描述
9. [自學牛頓方法] 牛頓方法和梯度下降法有什么異同點?請寫出牛頓方法的推導過程,編程實現牛頓方法求解上一題,並編程繪圖展示迭代計算過程。
梯度下降與牛頓迭代方法的異同點
相同點:
- 都是通過迭代的方式進行求解
- 都可以求局部的最值點
不同點:
- 牛頓迭代的過程中,每一步都指定了一定的步長;而梯度下降的學習率是我們自己指定的,且 一直不變;所以,牛頓迭代在時間上會更快一些。
- 牛頓法,利用到了函數的二階導,收斂速度相對於梯度下降會快很多(多元的話就是海塞矩陣的逆對應着梯度下降的學習率)
牛頓方法的推導過程
算法實現
算法思想1:
我首先設置x = 0找到最左邊的零點;然后設置一個比較大的x(可以是100,考慮到要進行畫圖,我設置的4)找到最右邊的零點;最后取這兩個零點的平均值,最后再進行一次牛頓迭代,得到中間的零點。
算法思路2:
雖然思路不同,但是代碼卻可以是一樣的(這個思路是我在看牛頓法和梯度下降法的區別時發現的)
和上一題同樣,這道題目中求零點的問題,同樣可以轉化為求f(x)的原函數F(x)的極值點問題;此時代碼中的f(x):看作是F(x)的一階導;gra(x)看作是F(x)的二階導。此時通過泰勒二階展開,可以得到推導公式:x = x - f'(x) / f''(x)。
import matplotlib.pyplot as plt
def f(x):
return (x ** 3) - 6 * (x ** 2) + 11 * x - 6
def gra(x):
return 3 * (x ** 2) - 12 * x + 11
X = []
x = 0
cnt = 0
'''畫圖'''
X_, Y_ = [], []
X_niudun, Y_niudun = [], []
x = 0
while(x <= 4):
X_.append(x)
Y_.append(f(x))
x += 0.1
'''算法核心'''
x = 0
while(cnt == 0):
fx = f(x); grax = gra(x)
X_niudun.append(x)
if(abs(fx / grax) <= 1e-4):
X.append(x)
cnt += 1
break
x = x - fx / grax
x = 4
while(cnt == 1):
fx = f(x); grax = gra(x)
X_niudun.append(x)
if(abs(fx / grax) <= 1e-4):
X.append(x)
cnt += 1
break
x = x - fx / grax
x = (X[0] + X[1]) / 2
while(cnt == 2):
fx = f(x); grax = gra(x)
X_niudun.append(x)
if (abs(fx / grax) <= 1e-4):
X.append(x)
cnt += 1
break
x = x - fx / grax
print(X)
sorted(X_niudun)
for i in X_niudun:
Y_niudun.append(f(i))
'''函數 + 牛頓迭代'''
plt.plot(X_, Y_, c = "red")
# 梯度下降過程
plt.scatter(X_niudun, Y_niudun, s = 12, c = "blue")
for (i, j) in zip(X_niudun, Y_niudun):
plt.plot([i + (0 - j) / gra(i), i], [0, j], c = "blue", linestyle=':')
plt.plot([0, 5], [0, 0], c = "blue")
plt.show()
總結與思考
- 梯度下降和牛頓法都屬於優化算法;其中當求極值點時,牛頓法的收斂速度會更快一些,因為它考慮到了函數的二階導,而梯度下降只考慮到了函數的一階導
- 聽說牛頓法更消耗資源,因為海塞矩陣的緣故;梯度下降法也可以通過步長不斷降低來優化,但是降低多少也算是一個超參數了(沒寫過,直覺);
- 牛頓法如果用來求最值點,應該會受到鞍點的影響(沒寫過);梯度下降法求最值點,也會有到一個局部最值點的問題。這道題目由於函數的緣故,一看就知道有幾個點;但是如果繼續進行擴展的話,就不好說了;這兩個算法都有其局限性,
- 梯度:步長問題,局部最值點問題;牛頓:計算資源問題,鞍點問題。
第十題
題目描述
10. 數據標准化是將數據按比例縮放到一個特定區間,其主要包括數據同趨化處理和無量綱化處理兩個方面。數據標准化的方法有很多種,常用的有最小-最大標准化和 z-score 標准化。請用戶對本題中的變量(不包括變量 ID)進行 z-score 標准化;數據說明:本題數據來自 KEEL,數據集一共包含 1 列 ID,4 列特征變量,共100 個樣本點。
實現
首先我通過random隨機取生成0 - 200.0的數據並保存到keel.csv文件中;然后,讀取csv文件中的數據,並對其進行修改,最終保存。
import csv
import numpy as np
import pandas as pd
'''
生成數據並保存到csv文件中
'''
# 1.創建文件對象
f = open('./data/keel.csv', 'w', encoding='utf-8', newline="")
# 2.基於文件對象構建csv寫入對象
csv_write = csv.writer(f)
# 3.構建列表頭
csv_write.writerow(['ID', 'CT', 'FA', 'WT', 'SP'])
# 4. 隨機生成數據
random = np.random.RandomState(0)#RandomState生成隨機數種子
A = []
for i in range(500):#隨機數個數
a = random.uniform(0, 200.0)#隨機數范圍
round(a, 1)#隨機數精度要求
A.append(a)
# 5.將數據寫入csv文件
for i in range(100):
csv_write.writerow([i + 1, A[i], A[i + 100], A[i + 200], A[i + 400]])
# 6.關閉文件
f.close()
'''讀取文件中的數據,並進行z-score操作'''
data = pd.read_csv('./data/keel.csv')
print(data.head(10))
E, FD = [], []
cols = data.columns
# 求均值
for i in range(4):
x = data[cols[i + 1]]
tmp = 0
for j in x:
tmp += j
E.append(tmp / len(data))
# 求標准差
for i in range(4):
x = data[cols[i + 1]]
tmp = 0
for j in x:
tmp += (j - E[i - 1]) ** 2
FD.append((tmp / len(data)) ** 0.5)
f = True
dic = {'CT': 0, 'FA': 1, 'WT': 2, 'SP': 3}
dic_reverse = {0: 'CT', 1: 'FA', 2: 'WT', 3: 'SP'}
feature = ['CT', 'FA', 'WT', 'SP']
for i in range(len(data)):
for j in feature:
data.loc[i, j] = (data.loc[i, j] - E[dic[j]]) / FD[dic[j]]
data.to_csv('./data/keel.csv')
print(data.head(10))
分別查看數據處理前后,成功修改了文件
總結與思考
- 這道題目,算法思路沒啥難度,關鍵在於要知道Pandas如何對文件進行操作,如讀操作,修改操作。
- 通過Uniform()函數可以指定random()生成數據的范圍;使用round()函數可以修改生成的數據的精度。
- C語言中可以直接用map進行映射,對應到Python中則是使用字典可以隨意映射
- 通過data.loc可以對讀取的數據保存的DataFrame文件進行修改
- 通過to_csv()函數,可以將DataFrame對象保存到指定的文件中。