日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从0到1建立一张评分卡之数据预处理

發布時間:2025/3/21 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从0到1建立一张评分卡之数据预处理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

從 0 到 1 建立一張評分卡

? 之前看了很多評分卡建模方面的課程和文章,對評分卡的建立大致有一些了解。但是由于實際工作中沒有接觸過,也沒有看到過比較舒服的代碼,所以對評分卡這塊一直有點不踏實。所幸最近在 Gayhub 上發現一個特別好用的半自動化建模的庫,試了一遍發現堪稱神器,比之前看的 scorecardpy、toad 用上去還要方便。對于評分卡這塊,如果有大佬有相關的經驗,歡迎找我,很想學習一下工作中是如何將評分卡落地的。
? 下面以網上找的一個數據集為例,介紹一下如何從 0 到 1 建立一張評分卡。

數據讀取

? 首先,讀取數據,看下數據的大致情況。

folderOfData = './'allData = pd.read_csv(folderOfData + 'application.csv', header=0, encoding='latin1') allData['term'] = allData['term'].apply(lambda x: int(x.replace(' months', '')))# 處理標簽:Fully Paid是正常用戶;Charged Off是違約用戶 allData['y'] = allData['loan_status'].map(lambda x: int(x == 'Charged Off'))allData1 = allData.loc[allData.term == 36] trainData, testData = train_test_split(allData1, test_size=0.4)
  • term 應該表示期數,將其中的 months 替換掉
  • 生成 y 變量,charged off 表示違約。
  • 由于存在不同的貸款期限(term),申請評分卡模型評估的違約概率必須要在統一的期限中,且不宜太長,所以選取 term = 36months 的樣本

? 除去 ID 和 label 變量外一共有 24 個變量,看一下變量類型。

數據清洗

# 將帶%的百分比變為浮點數 trainData['int_rate_clean'] = trainData['int_rate'].map(lambda x: float(x.replace('%', '')) / 100) def CareerYear(x):# 對工作年限進行轉換x = str(x)if x.find('nan') > -1:return -1elif x.find("10+") > -1: # 將"10+years"轉換成 11return 11elif x.find('< 1') > -1: # 將"< 1 year"轉換成 0return 0else:return int(re.sub("\D", "", x)) # 其余數據,去掉"years"并轉換成整數# 將工作年限進行轉化,否則影響排序 trainData['emp_length_clean'] = trainData['emp_length'].map(CareerYear) def DescExisting(x):# 將desc變量轉換成有記錄和無記錄兩種if type(x).__name__ == 'float':return 'no desc'else:return 'desc'# 將desc的缺失作為一種狀態,非缺失作為另一種狀態 trainData['desc_clean'] = trainData['desc'].map(DescExisting)

? 這里用到了__name__這個 python 的魔法方法。

def ConvertDateStr(x):mth_dict = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10,'Nov': 11, 'Dec': 12}if str(x) == 'nan':return datetime.datetime.fromtimestamp(time.mktime(time.strptime('9900-1', '%Y-%m')))# time.mktime 不能讀取1970年之前的日期else:yr = int(x[4:6])if yr <= 17:yr = 2000 + yrelse:yr = 1900 + yrmth = mth_dict[x[:3]]return datetime.datetime(yr, mth, 1)# 處理日期。earliest_cr_line的格式不統一,需要統一格式且轉換成python的日期 trainData['app_date_clean'] = trainData['issue_d'].map(lambda x: ConvertDateStr(x)) trainData['earliest_cr_line_clean'] = trainData['earliest_cr_line'].map(lambda x: ConvertDateStr(x))

? 這里的時間轉換操作比較復雜。

def MakeupMissing(x):if np.isnan(x):return -1else:return x# 處理mths_since_last_delinq。原始值中有0,用-1代替缺失 trainData['mths_since_last_delinq_clean'] = trainData['mths_since_last_delinq'].map(lambda x: MakeupMissing(x))trainData['mths_since_last_record_clean'] = trainData['mths_since_last_record'].map(lambda x: MakeupMissing(x))trainData['pub_rec_bankruptcies_clean'] = trainData['pub_rec_bankruptcies'].map(lambda x: MakeupMissing(x))

? 下面衍生兩個變量。

# 考慮申請額度與收入的占比 trainData['limit_income'] = trainData.apply(lambda x: x.loan_amnt / x.annual_inc, axis=1)# 考慮earliest_cr_line到申請日期的跨度,以月份記 trainData['earliest_cr_to_app'] = trainData.apply(lambda x: MonthGap(x.earliest_cr_line_clean, x.app_date_clean),axis=1)

? 下面利用之前提到的半自動化庫進行數據的數據處理。

? 首先看一下處理完之后的數據的缺失情況。先看變量的缺失情況。

# 每個變量缺失率的計算 def missing_cal(df):"""df :數據集return:每個變量的缺失率"""missing_series = df.isnull().sum()/df.shape[0]missing_df = pd.DataFrame(missing_series).reset_index()missing_df = missing_df.rename(columns={'index':'col', 0:'missing_pct'})missing_df = missing_df.sort_values('missing_pct',ascending=False).reset_index(drop=True)return missing_df missing_cal(trainData)

? 可以看到 mths_since_last_record、mths_since_last_delinq、desc 缺失值較多。對缺失率超過一定閾值的變量進行剔除,這里取 0.3,將上述三個變量剔除。

# 缺失值剔除(單個變量) def missing_delete_var(df,threshold=None):"""df:數據集threshold:缺失率刪除的閾值return :刪除缺失后的數據集"""df2 = df.copy()missing_df = missing_cal(df)missing_col_num = missing_df[missing_df.missing_pct>=threshold].shape[0]missing_col = list(missing_df[missing_df.missing_pct>=threshold].col)df2 = df2.drop(missing_col,axis=1)print('缺失率超過{}的變量個數為{}'.format(threshold,missing_col_num))return df2 trainData=missing_delete_var(trainData,threshold=0.3)

? 然后看下樣本的缺失情況。

# 單個樣本的缺失分布 def plot_missing_user(df,plt_size=None):"""df: 數據集plt_size: 圖紙的尺寸return :缺失分布圖(折線圖形式)"""missing_series = df.isnull().sum(axis=1)list_missing_num = sorted(list(missing_series.values))plt.figure(figsize=plt_size)plt.rcParams['font.sans-serif']=['Microsoft YaHei']plt.rcParams['axes.unicode_minus'] = Falseplt.plot(range(df.shape[0]),list_missing_num)plt.ylabel('缺失變量個數')plt.xlabel('samples')return plt.show() plot_missing_user(trainData,plt_size=None)

? 上圖是那 3 個缺失變量剔除之前每個樣本的缺失情況圖。可以看到中間一大段樣本缺失變量個數為 2 個,缺失變量最多的個數為 3 個,僅一小部分樣本沒有缺失變量。將 3 個缺失率較高的變量進行剔除之后的樣本缺失情況如下:

? 可以看到僅有少數樣本有 1 個缺失變量。這里為了展示代碼,對有 1 個缺失變量的樣本進行剔除。

# 缺失值剔除(單個樣本) def missing_delete_user(df,threshold=None):"""df:數據集threshold:缺失個數刪除的閾值return :刪除缺失后的數據集"""df2 = df.copy()missing_series = df.isnull().sum(axis=1)missing_list = list(missing_series)missing_index_list = []for i,j in enumerate(missing_list):if j>=threshold:missing_index_list.append(i)df2 = df2[~(df2.index.isin(missing_index_list))]print('缺失變量個數在{}以上的用戶數有{}個'.format(threshold,len(missing_index_list)))return df2

? 這里有個坑,就是這個函數的 df 的 index 必須是從 0 開始的,因為 trainData 是經過切分之后的,index 被打亂了,導致用這個函數時剔除的樣本一直不對,需要用 trainData.reset_index(drop=True),被這個坑絆住了大半天。
后面還有對缺失變量進行填充,由于這里已經剔除完,樣本集不存在缺失值,所以無需進行缺失值的填充。但是還是介紹一下缺失值填充的方法。
類別型變量

  • 用眾數進行填充
  • 單獨當作一個類別
def fillna_cate_var(df,col_list,fill_type=None):"""df:數據集col_list:變量list集合fill_type: 填充方式:眾數/當做一個類別return :填充后的數據集"""df2 = df.copy()for col in col_list:if fill_type=='class':df2[col] = df2[col].fillna('unknown')if fill_type=='mode':df2[col] = df2[col].fillna(df2[col].mode()[0])return df2

數值型變量

  • 缺失率 5%以下:中位數
  • 缺失率 5%-15%:隨機森林填充
  • 缺失率 15%以上:當作一個類別
def fillna_num_var(df,col_list,fill_type=None,filled_df=None):"""df:數據集col_list:變量list集合fill_type:填充方式:中位數/隨機森林/當做一個類別filled_df :已填充好的數據集,當填充方式為隨機森林時 使用return:已填充好的數據集"""df2 = df.copy()for col in col_list:if fill_type=='median':df2[col] = df2[col].fillna(df2[col].median())if fill_type=='class':df2[col] = df2[col].fillna(-999)if fill_type=='rf':rf_df = pd.concat([df2[col],filled_df],axis=1)known = rf_df[rf_df[col].notnull()]unknown = rf_df[rf_df[col].isnull()]x_train = known.drop([col],axis=1)y_train = known[col]x_pre = unknown.drop([col],axis=1)rf = RandomForestRegressor(random_state=0)rf.fit(x_train,y_train)y_pre = rf.predict(x_pre)df2.loc[df2[col].isnull(),col] = y_prereturn df2

? 此外還有常變量處理和分類變量降基處理。常變量處理就是處理方差過小的變量,而分類變量降基是將一些占比較小的類別進行合并。附上代碼。

# 常變量/同值化處理 def const_delete(df,col_list,threshold=None):"""df:數據集col_list:變量list集合threshold:同值化處理的閾值return :處理后的數據集"""df2 = df.copy()const_col = []for col in col_list:const_pct = df2[col].value_counts().iloc[0]/df2[df2[col].notnull()].shape[0]if const_pct>=threshold:const_col.append(col)df2 = df2.drop(const_col,axis=1)print('常變量/同值化處理的變量個數為{}'.format(len(const_col)))return df2 # 分類型變量的降基處理 def descending_cate(df,col_list,threshold=None):"""df: 數據集col_list:變量list集合threshold:降基處理的閾值return :處理后的數據集"""df2 = df.copy()for col in col_list:value_series = df[col].value_counts()/df[df[col].notnull()].shape[0]small_value = []for value_name,value_pct in zip(value_series.index,value_series.values):if value_pct<=threshold:small_value.append(value_name)df2.loc[df2[col].isin(small_value),col]='other'return df2

【作者】:Labryant
【原創公眾號】:風控獵人
【簡介】:某創業公司策略分析師,積極上進,努力提升。乾坤未定,你我都是黑馬。
【轉載說明】:轉載請說明出處,謝謝合作!~

總結

以上是生活随笔為你收集整理的从0到1建立一张评分卡之数据预处理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。