Python機器學習實例:數據競賽-足球運動員身價估計

前言

1,背景介紹

  每個足球運動員在轉會市場都有各自的價碼。本次數據練習的目的是根據球員的各項信息和能力來預測該球員的市場價值。

2,數據來源

  FIFA2018

3,數據文件說明

  數據文件分為三個:

train.csv         訓練集     文件大小為2.20MB

test.csv      預測集    文件大小為1.44KB

sample_submit.csv   提交示例 文件大小為65KB

  訓練集共有10441條樣本,預測集中有7000條樣本。每條樣本代表一位球員,數據中每個球員

有61項屬性。數據中含有缺失值。

4,數據變量說明

id行編號,沒有實際意義

club該球員所屬的俱樂部。該信息已經被編碼。

league該球員所在的聯賽。已被編碼。

birth_date生日。格式為月/日/年。

height_cm身高(釐米)

weight_kg體重(公斤)

nationality國籍。已被編碼。

potential球員的潛力。數值變量。

pac球員速度。數值變量。

sho射門(能力值)。數值變量。

pas傳球(能力值)。數值變量。

dri帶球(能力值)。數值變量。

def防守(能力值)。數值變量。

phy身體對抗(能力值)。數值變量。

international_reputation國際知名度。數值變量。

skill_moves技巧動作。數值變量。

weak_foot非慣用腳的能力值。數值變量。

work_rate_att球員進攻的傾向。分類變量,Low, Medium, High。

work_rate_def球員防守的傾向。分類變量,Low, Medium, High。

preferred_foot慣用腳。1表示右腳、2表示左腳。

crossing傳中(能力值)。數值變量。

finishing完成射門(能力值)。數值變量。

heading_accuracy頭球精度(能力值)。數值變量。

short_passing短傳(能力值)。數值變量。

volleys凌空球(能力值)。數值變量。

dribbling盤帶(能力值)。數值變量。

curve弧線(能力值)。數值變量。

free_kick_accuracy定位球精度(能力值)。數值變量。

long_passing長傳(能力值)。數值變量。

ball_control控球(能力值)。數值變量。

acceleration加速度(能力值)。數值變量。

sprint_speed衝刺速度(能力值)。數值變量。

agility靈活性(能力值)。數值變量。

reactions反應(能力值)。數值變量。

balance身體協調(能力值)。數值變量。

shot_power射門力量(能力值)。數值變量。

jumping彈跳(能力值)。數值變量。

stamina體能(能力值)。數值變量。

strength力量(能力值)。數值變量。

long_shots遠射(能力值)。數值變量。

aggression侵略性(能力值)。數值變量。

interceptions攔截(能力值)。數值變量。

positioning位置感(能力值)。數值變量。

vision視野(能力值)。數值變量。

penalties罰點球(能力值)。數值變量。

marking卡位(能力值)。數值變量。

standing_tackle斷球(能力值)。數值變量。

sliding_tackle剷球(能力值)。數值變量。

gk_diving門將撲救(能力值)。數值變量。

gk_handling門將控球(能力值)。數值變量。

gk_kicking門將開球(能力值)。數值變量。

gk_positioning門將位置感(能力值)。數值變量。

gk_reflexes門將反應(能力值)。數值變量。

rw球員在右邊鋒位置的能力值。數值變量。

rb球員在右後衛位置的能力值。數值變量。

st球員在射手位置的能力值。數值變量。

lw球員在左邊鋒位置的能力值。數值變量。

cf球員在鋒線位置的能力值。數值變量。

cam球員在前腰位置的能力值。數值變量。

cm球員在中場位置的能力值。數值變量。

cdm球員在後腰位置的能力值。數值變量。

cb球員在中後衛的能力值。數值變量。

lb球員在左後衛置的能力值。數值變量。

gk球員在守門員的能力值。數值變量。

y該球員的市場價值(單位為萬歐元)。這是要被預測的數值。

5,評估方法

  可以參考博文:機器學習筆記:常用評估方法

Python機器學習實例:數據競賽-足球運動員身價估計

6,完整代碼,請移步小編的github

  傳送門:https://github.com/LeBron-Jian/sofasofa-learn

數據預處理

  此處實戰一下數據預處理的理論知識點:請點擊知識點1,或者知識點2


1,本文數據預處理的主要步驟

  • (1) 刪除和估算缺失值(removing and imputing missing values)
  • (2)獲取分類數據(Getting categorical data into shape for machine learning)
  • (3)為模型構建選擇相關特徵(Selecting relevant features for the module construction)
  • (4)對原表的年份數據進行填充,比如格式是這樣的09/10/89 ,因為要計算年齡,所以補為完整的09/10/1989
  • (5)將體重和身高轉化為BMI指數
  • (6)將球員在各個位置上的能力值轉化為球員最擅長位置上的得分

2,分類數據處理

  對於定量特徵,其包含的有效信息為區間劃分,例如本文中work_rate_att 和 work_rate_def 他們分別代表了球員進攻的傾向和球員防守的傾向。用Low,Medium,High表示。所以我們可能會將其轉化為0 , 1,2 。

Python機器學習實例:數據競賽-足球運動員身價估計

  這裡使用標籤編碼來處理,首先舉例說明一下標籤編碼


from sklearn import preprocessing

labelEncoding = preprocessing.LabelEncoder()

labelEncoding.fit(['Low','Medium','High'])

res = labelEncoding.transform(['Low','Medium','High','High','Low','Low'])

print(res)

# [1 2 0 0 1 1]

  又或者自己編碼:


import pandas as pd

df = pd.DataFrame([

['green', 'M', 10.1, 'class1'],

['red', 'L', 13.5, 'class2'],

['blue', 'XL', 15.3, 'class1']])

print(df)

df.columns = ['color', 'size', 'prize', 'class label']

size_mapping = {

'XL':3,

'L':2,

'M':1

}

df['size'] = df['size'].map(size_mapping)

print(df)

class_mapping = {label:ind for ind,label in enumerate(set(df['class label']))}

df['class label'] = df['class label'].map(class_mapping)

print(df)

'''

0 1 2 3

0 green M 10.1 class1

1 red L 13.5 class2

2 blue XL 15.3 class1


color size prize class label

0 green 1 10.1 class1

1 red 2 13.5 class2

2 blue 3 15.3 class1


color size prize class label

0 green 1 10.1 0

1 red 2 13.5 1

2 blue 3 15.3 0

'''

  上面使用了兩種方法,一種是將分類數據轉換為數值型數據,一種是編碼分類標籤。

3,缺失值處理

3.1 統計空值情況


#using isnull() function to check NaN value

df.isnull().sum()

  本文中空值如下:

gk_positioning 0

gk_reflexes 0

rw 1126

rb 1126

st 1126

lw 1126

cf 1126

cam 1126

cm 1126

cdm 1126

cb 1126

lb 1126

gk 9315

y 0

Length: 65, dtype: int64

  

3.2 消除缺失值 dropna() 函數

  一個最簡單的處理缺失值的方法就是直接刪掉相關的特徵值(一列數據)或者相關的樣本(一行數據)。利用dropna()函數實現。


# 參數axis 表示軸選擇,axis = 0 代表行 axis = 1 代表列

df.dropna(axis = 1)

df.dropna(axis = 0)


# how參數選擇刪除行列數據(any / all)

dropna(how = all)


# 刪除值少於int個數的值

dropna(thresh = int)


# subset = [' '] 刪除指定列中有空值的一行數據(整個樣本)

  雖然直接刪除很簡單,但是直接刪除會帶來很多弊端。比如樣本值刪除太多導致不能進行可靠預測;或者特徵值刪除太多(列數據)可能會失去很多有價值的信息。

3.3 插值法 interpolation techniques

  比較常用的一種估值是平均值估計(mean imputation)。可以直接使用sklearn庫中的imputer類實現。

class sklearn.preprocessing .Imputer

(missing_values = 'NaN', strategy = 'mean', axis = 0, verbose = 0, copy = True)

miss_values :int 或者 NaN ,默認NaN(string類型)

strategy :默認mean。平均值填補

可選項:mean(平均值)

median(中位數)

most_frequent(眾數)

axis : 指定軸向。axis = 0 列向(默認) axis =1 行向

verbose :int 默認值為0

copy :默認True ,創建數據集的副本

False:在任何合適的地方都可能進行插值

  下面舉例說明:


# 創建CSV數據集

import pandas as pd

from io import StringIO

import warnings

warnings.filterwarnings('ignore')

# 數據不要打空格,IO流會讀入空格

csv_data = '''

A,B,C,D

1.0,2.0,3.0,4.0

5.0,6.0,,8.0

10.0,11.0,12.0,

'''

df = pd.read_csv(StringIO(csv_data))

print(df)

# 均值填充

from sklearn.preprocessing import Imputer

# axis = 0 表示列向 ,採用每一列的平均值填充空值

imr = Imputer(missing_values='NaN',strategy='mean',axis=0)

imr = imr.fit(df.values)

imputed_data = imr.transform(df.values)

print(imputed_data)

'''

A B C D

0 1.0 2.0 3.0 4.0

1 5.0 6.0 NaN 8.0

2 10.0 11.0 12.0 NaN

[[ 1. 2. 3. 4. ]

[ 5. 6. 7.5 8. ]

[10. 11. 12. 6. ]]

'''

  

4,將出生日期轉化為年齡

  如下,在這個比賽中,獲取的數據中出現了出生日期的Series,下面我們對其進行轉化。

Python機器學習實例:數據競賽-足球運動員身價估計

  下面我截取了一部分數據:

Python機器學習實例:數據競賽-足球運動員身價估計

  從數據來看,‘11/6/86’之類的數,最左邊的數表示月份,中間表示日,最後的數表示年。

  實際上我們在分析時候並不需要人的出生日期,而是需要年齡,不同的年齡階段會有不同的狀態,有可能age就是一個很好地特徵工程指示變量。

  那麼如何將birth轉化為age呢?這裡使用到datetime這個庫。

4.1 首先把birth_date轉化為標準時間格式

# 將出生年月日轉化為年齡

traindata['birth_date'] = pd.to_datetime(traindata['birth_date'])

print(traindata)

  結果如下:

Python機器學習實例:數據競賽-足球運動員身價估計

4.2 獲取當前時間的年份,並減去birth_date 的年份


# 獲取當前的年份

new_year = dt.datetime.today().year

traindata['birth_date'] = new_year - traindata.birth_date.dt.year

print(traindata)

  這裡使用了dt.datetime.today().year來獲取當前日期的年份,然後將birth數據中的年份數據提取出來(frame.birth.dt.year),兩者相減就得到需要的年齡數據,如下:

Python機器學習實例:數據競賽-足球運動員身價估計

5,身高體重轉化為BMI

  官方的標杆模型對身高體重的處理使用的是BMI指數。

  BMI指數是身體質量指數,是目前國際上常用的衡量人體胖瘦程度以及是否健康的一個標準。

Python機器學習實例:數據競賽-足球運動員身價估計

Python機器學習實例:數據競賽-足球運動員身價估計

  這裡直接計算,公式如下:


# 計算球員的身體質量指數(BMI)

train['BMI'] = 10000. * train['weight_kg'] / (train['height_cm'] ** 2)

test['BMI'] = 10000. * test['weight_kg'] / (test['height_cm'] ** 2)

  

6,將球員各個位置上的評分轉化為球員最擅長位置的評分

  目前打算將這11個特徵轉化為球員最擅長位置上的評分,這裡計算方式只會取這11個特徵中的最大值,當然這裡也省去了對缺失值的判斷,經過對數據的研究,我們發現有門將和射門球員的區分,如果使用最擅長位置的話就省去了這一步,但是如果這樣直接取最大值的話會隱藏一些特徵。

Python機器學習實例:數據競賽-足球運動員身價估計


# 獲得球員最擅長位置上的評分

positions = ['rw','rb','st','lw','cf','cam','cm','cdm','cb','lb','gk']

train['best_pos'] = train[positions].max(axis =1)

test['best_pos'] = test[positions].max(axis = 1)

  

重要特徵提取及其模型訓練

1,使用隨機森林提取重要特徵


# 提取重要性特徵

def RandomForestExtractFeature(traindata):

from sklearn.model_selection import train_test_split

from sklearn.ensemble import RandomForestClassifier

print(type(traindata)) # <class>

X,y = traindata[:,1:-1],traindata[:,-1]

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=0)

# n_estimators 森林中樹的數量

forest = RandomForestClassifier(n_estimators=100000,random_state=0,n_jobs=1)

forest.fit(X_train,y_train.astype('int'))

importances = forest.feature_importances_

return importances

  


模型訓練及其結果展示

1,自己的Xgboost,沒有做任何處理


#_*_ coding:utf-8_*_

import pandas as pd

import warnings

warnings.filterwarnings('ignore')

# 導入數據,並處理非數值型數據

def load_dataSet(filename):

'''

檢查數據,發現出生日期是00/00/00類型,

work_rate_att work_rate_def 是Medium High Low

下面對這三個數據進行處理

然後對gk

:return:

'''

# 讀取訓練集

traindata = pd.read_csv(filename,header=0)

# 處理非數值型數據

label_mapping = {

'Low':0,

'Medium':1,

'High':2

}

traindata['work_rate_att'] = traindata['work_rate_att'].map(label_mapping)

traindata['work_rate_def'] = traindata['work_rate_def'].map(label_mapping)

# 將出生年月日轉化為年齡

traindata['birth_date'] = pd.to_datetime(traindata['birth_date'])

import datetime as dt

# 獲取當前的年份

new_year = dt.datetime.today().year

traindata['birth_date'] = new_year - traindata.birth_date.dt.year

# 處理缺失值

res = traindata.isnull().sum()

from sklearn.preprocessing import Imputer

imr = Imputer(missing_values='NaN',strategy='mean',axis=0)

imr = imr.fit(traindata.values)

imputed_data = imr.transform(traindata.values)

return imputed_data


# 直接訓練迴歸模型

def xgboost_train(traindata,testdata):

from sklearn.model_selection import train_test_split

import xgboost as xgb

from xgboost import plot_importance

from matplotlib import pyplot as plt

from sklearn.externals import joblib

from sklearn.metrics import mean_squared_error

from sklearn.metrics import r2_score

X, y = traindata[:, 1:-1], traindata[:, -1]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234567)

# n_estimators 森林中樹的數量

model = xgb.XGBRegressor(max_depth=5,learning_rate=0.1,n_estimators=160,

silent=True,objective='reg:gamma')

model.fit(X_train,y_train)

# 對測試集進行預測

y_pred = model.predict(X_test)

# print(y_pred)

# 計算準確率,下面的是計算模型分類的正確率,

MSE = mean_squared_error(y_test, y_pred)

print("accuaracy is %s "%MSE)

R2 = r2_score(y_test,y_pred)

print('r2_socre is %s'%R2)

# test_pred = model.predict(testdata[:,1:])

# 顯示重要特徵

# plot_importance(model)

# plt.show()

# 保存模型

joblib.dump(model,'Xgboost.m')

# 使用模型預測數據

def predict_data(xgbmodel,testdata,submitfile):

import numpy as np

ModelPredict = np.load(xgbmodel)

test = testdata[:,1:]

predict_y = ModelPredict.predict(test)

submit_data = pd.read_csv(submitfile)

submit_data['y'] = predict_y

submit_data.to_csv('my_SVM_prediction.csv', index=False)

return predict_y


if __name__ == '__main__':

TrainFile= 'data/train.csv'

TestFile = 'data/test.csv'

SubmitFile = 'submit1.csv'

xgbmodel = 'Xgboost.m'

TrainData = load_dataSet(TrainFile)

TestData = load_dataSet(TestFile)

# RandomForestExtractFeature(TrainData)

xgboost_train(TrainData,TestData)

predict_data(xgbmodel,TestData,SubmitFile)

  

Python機器學習實例:數據競賽-足球運動員身價估計


2,隨機森林標杆模型

整理此隨機森林訓練模型的亮點

  • 1,將出生年月日轉化為球員的歲數(數據處理必須的)
  • 2,將球員在各個位置的能力使用球員最擅長的位置表示(!!此處有待商榷)
  • 3,利用球員的身體質量指數(BMI)來代替球員體重和身高這兩個特徵值
  • 4,根據數據判斷,發現各個位置的球員缺失值主要判斷是否為守門員,此處按照是否為守門員來分別訓練隨機森林
  • 5,最終使用height_cm(身高),weight_kg(體重),potential(潛力),BMI(球員身體指數),phy(身體對抗能力),international_reputation(國際知名度),age(年齡),best_pos(最佳位置)這9個特徵來預測結果


#_*_coding:utf-8_*_

import pandas as pd

import numpy as np

import datetime as dt

from sklearn.ensemble import RandomForestRegressor

# 讀取數據

train = pd.read_csv('data/train.csv')

test = pd.read_csv('data/test.csv')

submit = pd.read_csv('data/sample_submit.csv')

# 獲得球員年齡

today = dt.datetime.today().year

train['birth_date'] = pd.to_datetime(train['birth_date'])

train['age'] = today - train.birth_date.dt.year

test['birth_date'] = pd.to_datetime(test['birth_date'])

test['age'] = today - test.birth_date.dt.year

# 獲取球員最擅長位置的評分

positions = ['rw','rb','st','lw','cf','cam','cm','cdm','cb','lb','gk']

train['best_pos'] = train[positions].max(axis=1)

test['best_pos'] = test[positions].max(axis=1)

# 計算球員的身體質量指數(BMI)

train['BMI'] = 10000. * train['weight_kg'] / (train['height_cm'] ** 2)

test['BMI'] = 10000. * test['weight_kg'] / (test['height_cm'] ** 2)

# 判斷一個球員是否是守門員

train['is_gk'] = train['gk'] > 0

test['is_gk'] = test['gk'] > 0

# 用多個變量準備訓練隨機森林

test['pred'] = 0

cols = ['height_cm', 'weight_kg', 'potential', 'BMI', 'pac',

'phy', 'international_reputation', 'age', 'best_pos']


# 用非守門員的數據訓練隨機森林

reg_ngk = RandomForestRegressor(random_state=100)

reg_ngk.fit(train[train['is_gk'] == False][cols] , train[train['is_gk'] == False]['y'])

preds = reg_ngk.predict(test[test['is_gk'] == False][cols])

test.loc[test['is_gk'] == False , 'pred'] = preds

# 用守門員的數據訓練隨機森林

reg_gk = RandomForestRegressor(random_state=100)

reg_gk.fit(train[train['is_gk'] == True][cols] , train[train['is_gk'] == True]['y'])

preds = reg_gk.predict(test[test['is_gk'] == True][cols])

test.loc[test['is_gk'] == True , 'pred'] = preds

# 輸出預測值

submit['y'] = np.array(test['pred'])

submit.to_csv('my_RF_prediction.csv',index = False)

  

為什麼會有兩個結果?

  這裡解釋一下,因為這裡的標杆模型中的年齡的取值,是我以目前的時間為準,而不是以作者給的2018年為準,可能因為差了一歲導致球員的黃金年齡不同,價值也就不同。

Python機器學習實例:數據競賽-足球運動員身價估計

Python機器學習實例:數據競賽-足球運動員身價估計

隨機森林調參(網格搜索)


#_*_coding:utf-8_*_

import pandas as pd

import numpy as np

import datetime as dt

from sklearn.ensemble import RandomForestRegressor

# 讀取數據

train = pd.read_csv('data/train.csv')

test = pd.read_csv('data/test.csv')

submit = pd.read_csv('data/sample_submit.csv')

# 獲得球員年齡

today = dt.datetime.today().year

train['birth_date'] = pd.to_datetime(train['birth_date'])

train['age'] = today - train.birth_date.dt.year

test['birth_date'] = pd.to_datetime(test['birth_date'])

test['age'] = today - test.birth_date.dt.year

# 獲取球員最擅長位置的評分

positions = ['rw','rb','st','lw','cf','cam','cm','cdm','cb','lb','gk']

train['best_pos'] = train[positions].max(axis=1)

test['best_pos'] = test[positions].max(axis=1)

# 計算球員的身體質量指數(BMI)

train['BMI'] = 10000. * train['weight_kg'] / (train['height_cm'] ** 2)

test['BMI'] = 10000. * test['weight_kg'] / (test['height_cm'] ** 2)

# 判斷一個球員是否是守門員

train['is_gk'] = train['gk'] > 0

test['is_gk'] = test['gk'] > 0

# 用多個變量準備訓練隨機森林

test['pred'] = 0

cols = ['height_cm', 'weight_kg', 'potential', 'BMI', 'pac',

'phy', 'international_reputation', 'age', 'best_pos']

# 用非守門員的數據訓練隨機森林

# 使用網格搜索微調模型

from sklearn.model_selection import GridSearchCV

param_grid = [

{'n_estimators':[3,10,30], 'max_features':[2,4,6,8]},

{'bootstrap':[False], 'n_estimators':[3,10],'max_features':[2,3,4]}

]

forest_reg = RandomForestRegressor()

grid_search = GridSearchCV(forest_reg, param_grid ,cv=5,

scoring='neg_mean_squared_error')

grid_search.fit(train[train['is_gk'] == False][cols] , train[train['is_gk'] == False]['y'])

preds = grid_search.predict(test[test['is_gk'] == False][cols])

test.loc[test['is_gk'] == False , 'pred'] = preds

# 用守門員的數據訓練隨機森林

# 使用網格搜索微調模型

from sklearn.model_selection import GridSearchCV

'''

先對estimators進行網格搜索,[3,10,30]

接著對最大深度max_depth

內部節點再劃分所需要最小樣本數min_samples_split 進行網格搜索

最後對最大特徵數,ax_features進行調參

'''

param_grid1 = [

{'n_estimators':[3,10,30], 'max_features':[2,4,6,8]},

{'bootstrap':[False], 'n_estimators':[3,10],'max_features':[2,3,4]}

]

'''

parm_grid 告訴Scikit-learn 首先評估所有的列在第一個dict中的n_estimators 和

max_features的 3*4=12 種組合,然後嘗試第二個dict中的超參數2*3 = 6 種組合,

這次會將超參數bootstrap 設為False 而不是True(後者是該超參數的默認值)

總之,網格搜索會探索 12 + 6 = 18 種RandomForestRegressor的超參數組合,會訓練

每個模型五次,因為使用的是五折交叉驗證,換句話說,訓練總共有18 *5 = 90 輪,、

將花費大量的時間,完成後,就可以得到參數的最佳組合了

'''

forest_reg1 = RandomForestRegressor()

grid_search1 = GridSearchCV(forest_reg, param_grid1 ,cv=5,

scoring='neg_mean_squared_error')

grid_search1.fit(train[train['is_gk'] == True][cols] , train[train['is_gk'] == True]['y'])

preds = grid_search1.predict(test[test['is_gk'] == True][cols])

test.loc[test['is_gk'] == True , 'pred'] = preds

# 輸出預測值

submit['y'] = np.array(test['pred'])

submit.to_csv('my_RF_prediction1.csv',index = False)

# 打印參數的最佳組合

print(grid_search.best_params_)

  

Python機器學習實例:數據競賽-足球運動員身價估計

3,決策樹標杆模型(四個特徵)

整理此決策樹訓練模型的亮點

  • 1,將出生年月日轉化為球員的歲數(數據處理必須的)
  • 2,將球員在各個位置的能力使用球員最擅長的位置表示(!!此處有待商榷)
  • 3,直接使用潛力,國際知名度,年齡,最擅長位置評分這四個變量建立決策樹(我覺得有點草率)


#_*_coding:utf-8_*_

import pandas as pd

import datetime as dt

from sklearn.tree import DecisionTreeRegressor

# 讀取數據

train = pd.read_csv('data/train.csv')

test = pd.read_csv('data/test.csv')

submit = pd.read_csv('data/sample_submit.csv')


# 獲得球員年齡

today = dt.datetime.today().year

train['birth_date'] = pd.to_datetime(train['birth_date'])

train['age'] = today - train.birth_date.dt.year

test['birth_date'] = pd.to_datetime(test['birth_date'])

test['age'] = today - test.birth_date.dt.year

# 獲得球員最擅長位置上的評分

positions = ['rw','rb','st','lw','cf','cam','cm','cdm','cb','lb','gk']

train['best_pos'] = train[positions].max(axis =1)

test['best_pos'] = test[positions].max(axis = 1)

# 用潛力,國際知名度,年齡,最擅長位置評分 這四個變量來建立決策樹模型

col = ['potential','international_reputation','age','best_pos']

reg = DecisionTreeRegressor(random_state=100)

reg.fit(train[col],train['y'])

# 輸出預測值

submit['y'] = reg.predict(test[col])

submit.to_csv('my_DT_prediction.csv',index=False)

  結果如下:

Python機器學習實例:數據競賽-足球運動員身價估計

Python機器學習實例:數據競賽-足球運動員身價估計


分享到:


相關文章: