首页 > 其他分享 >【金融风控】特征构造及代码详解

【金融风控】特征构造及代码详解

时间:2024-11-13 09:47:36浏览次数:3  
标签:ft df auto 代码 value 风控 详解 data columns

介绍

  • 知道未来信息的概念,及处理未来信息的方法

  • 掌握从原始数据构造出新特征的方法

  • 掌握特征变换的方法

  • 掌握缺失值处理的方法

【理解】数据准备

风控建模特征数据

  • 数据来源

  • 人行征信数据

查询原因包括:贷款审批、贷后管理、信用卡审批、担保资格审查、司法调查、公积金提、取复核查询、融资审批、额度审批、个人查询等。

获取渠道:

#1.终端app
招商银行,中国银行等

#2.商业银行柜台
自助服务终端

#3.中国人民银行分行
刷身份证即可

无论是哪一种获取方式,个人每年都有2次免费查询机会。

梳理数据的内在逻辑

关系种类

一对一:一个用户有一个注册手机号

一对多:一个用户多笔借款

多对多:一个用户可以登录多个设备,一个设备可以有多个用户登录

  • 举例

    下图中,蓝色框为二月当期账单,红色框为订单

  • 任务:分析厚数据常登陆首单用户的逾期情况

    • 可以将表结构展示到特征文档当中,说明取数逻辑

小结:

#1.数据库建模
用于业务系统,后端开发工程师。

#2.数仓建模
用于构建数仓,是数仓工程师来建模。
如果公司没有数仓工程师,就需要咋们自己来进行数据处理。pandas/SQL。

样本设计和特征框架

  • 定义观察期样本

    • 确定观察期(定X时间切面)和表现期(定Y的标签)

    • 确认样本数目是否合理

  • 数据EDA(探索性数据分析)

    • 看数据总体分布

      • data.shape

      • data.isnull()

      • data.info()

      • data.describe()

    • 看好坏样本分布差异

      • data[data[label]==0].describe() 好用户

      • data[data[label]==1].describe() 坏用户

    • 看单个数据

      • data.sample(n=10,random_state=1)

  • 梳理特征框架

    • RFM生成新特征

    举例 行为评分卡中的用户账单还款特征

    • 用户账单关键信息:时间,金额,还款,额度

小结:查看数据的分布,EDA(探索性数据分析)。

数据准备的逻辑:

  • 数据来源(征信数据)

  • 梳理数据内在逻辑(关联关系)

  • 查看数据分布(EDA)

【理解】特征构造

静态信息特征和时间截面特征

  • 用户静态信息特征

    • 用户的基本信息(半年内不会变化)

    • 姓名

    • 年龄

    • 性别

    • 籍贯

    • 政治面貌

    • 国籍

    • ....

  • 用户时间截面特征

    • 未来信息当前时间截面之后的数据

    • 时间截面数据在取数的时候要小心,避免使用未来信息

    • 产生未来信息最直接的原因:缺少快照表

      • 快照表,每天照个相片 23:00点 把当天的数据 备份一份

        • 快照表只会保存当天最终的状态

        日志表,每一次操作都记一次, 不会update,只有insert 操作一次记录一次

        • 每一次操作都会记录下来

    • 金融相关数据原则上都需要快照表记录所有痕迹(额度变化情况,多次申请的通过和拒绝情况...)

    • 缺少快照表的可能原因

      • 快照表消耗资源比较大,为了性能不做

      • 原有数据表设计人员疏忽,没做

      • 借用其他业务数据(如电商)做信贷

    • 举例

      首次借贷 二次借贷 爬虫授权 三次借贷

      ——————————————————————→

      用户借款授权爬虫逾期
      u1l11N0
      u1l12N0
      u1l13Y0
      u2l21N0
      u2l22N0
      u2l23Y1
      u3l11N0
      u3l12N0
      u3l13Y0

      实际存储

      用户授权
      u1Y
      u2Y
      u3Y
      用户借款逾期
      u1l110
      u1l120
      u1l130
      u2l210
      u2l220
      u2l231
      u3l110
      u3l120
      u3l130

      join 结果

      用户借款授权爬虫逾期
      u1l11Y0
      u1l12Y0
      u1l13Y0
      u2l21Y0
      u2l22Y0
      u2l23Y1
      u3l11Y0
      u3l12Y0
      u3l13Y0

      解决方案:加入快照的存储(快照表)

      用户授权时间
      u1Yt3
      u2Yt3
      u3Yt3

时间序列特征

用户时间序列特征
  • 从观察点往前回溯一段时间的数据

时间序列:一段时间内的数据。

时间序列特征衍生
  • 特征聚合:将单个特征的多个时间节点取值进行聚合。特征聚合是传统评分卡建模的主要特征构造方法。

    • 举例,计算每个用户的额度使用率,记为特征ft,按照时间轴以月份为切片展开

      • 申请前30天内的额度使用率ft1

      • 申请前30天至60天内的额度使用率ft2

      • 申请前60天至90天内的额度使用率ft3

      • 申请前330天至360天内的额度使用率ft12

      • 得到一个用户的12个特征

注意事项:

  在机器学习阶段的模型训练中,不提倡一上来就做特征。
  而是先跑baseline模型。
  当baseline模型达不到预期,可以做一些和特征相关的操作。
  
  10个特征。
  AUC 0.61 0.75
  
  #1.筛选特征
  0.7
  
  #2.衍生特征
  
  
  后端 CRUD boy | girl
  大数据:SQL boy | girl
  AI:调参 boy | girl

案例:

  import pandas as pd
  import numpy as np
  data = pd.read_excel('../data/textdata.xlsx')
  data.head()

显示结果

customer_idft1ft2ft3ft4ft5ft6ft7ft8ft9...gt3gt4gt5gt6gt7gt8gt9gt10gt11gt12
0111911.01213181012NaNNaN...100181012NaNNaNNaNNaNNaN
111211-11.01010131310NaNNaN...1010131310NaNNaNNaNNaNNaN
2113011.01012610025.010.0...1012610025.010.0NaNNaNNaN
3114-7-1.09870-1910.011.0...1010120-1910.011.0NaNNaNNaN
411511NaN6100171910.030.0...6100171910.030.015.0NaNNaN

5 rows × 26 columns

  • 可以根据这个时间序列进行基于经验的人工特征衍生,例如计算最近P个月特征大于0的月份数

  #最近p个月,ft>0的月份数
  def Num(ft,p):  #ft 特征名字 p特征大于0的月份数
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.where(df>0,1,0).sum(axis=1)
      return ft+'_num'+str(p),auto_value
  • 计算最近P个月特征ft等于0的月份数

  #最近p个月,ft=0的月份数
  def zero_cnt(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.where(df==0,1,0).sum(axis=1)
      return ft+'_zero_cnt'+str(p),auto_value
  • 计算近p个月特征ft大于0的月份数是否大于等于1

  #最近p个月,ft>0的月份数是否>=1     
  def Evr(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      arr=np.where(df>0,1,0).sum(axis=1)
      auto_value = np.where(arr,1,0)
      return ft+'_evr'+str(p),auto_value
  • 计算最近p个月特征ft的均值

  #最近p个月,ft均值
  def Avg(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.nanmean(df,axis = 1 )
      return ft+'_avg'+str(p),auto_value
  • 计算最近p个月特征ft的和,最大值,最小值

  #最近p个月,ft和
  def Tot(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.nansum(df,axis = 1)
      return ft+'_tot'+str(p),auto_value
  
  #最近(2,p+1)个月,ft和
  def Tot2T(ft,p):
      df=data.loc[:,ft+'2':ft+str(p+1)]
      auto_value=df.sum(1)
      return ft+'_tot2t'+str(p),auto_value
  
  #最近p个月,ft最大值
  def Max(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.nanmax(df,axis = 1)
      return ft+'_max'+str(p),auto_value
  
  #最近p个月,ft最小值
  def Min(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.nanmin(df,axis = 1)
      return ft+'_min'+str(p),auto_value
  • 其余衍生方法

  #最近p个月,最近一次ft>0到现在的月份数
  def Msg(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      df_value=np.where(df>0,1,0)
      auto_value=[]
      for i in range(len(df_value)):
          row_value=df_value[i,:]
          if row_value.max()<=0:
              indexs='0'
              auto_value.append(indexs)
          else:
              indexs=1
              for j in row_value:
                  if j>0:
                      break
                  indexs+=1
              auto_value.append(indexs)
      return ft+'_msg'+str(p),auto_value
   
  
  #最近p个月,最近一次ft=0到现在的月份数
  def Msz(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      df_value=np.where(df==0,1,0)
      auto_value=[]
      for i in range(len(df_value)):
          row_value=df_value[i,:]
          if row_value.max()<=0:
              indexs='0'
              auto_value.append(indexs)
          else:
              indexs=1
              for j in row_value:
                  if j>0:
                      break
                  indexs+=1
              auto_value.append(indexs)
      return ft+'_msz'+str(p),auto_value   
      
  #当月ft/(最近p个月ft的均值)
  def Cav(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = df[ft+'1']/np.nanmean(df,axis = 1 ) 
      return ft+'_cav'+str(p),auto_value 
  
  #当月ft/(最近p个月ft的最小值)
  def Cmn(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = df[ft+'1']/np.nanmin(df,axis = 1 ) 
      return ft+'_cmn'+str(p),auto_value 
  
  #最近p个月,每两个月间的ft的增长量的最大值
  def Mai(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])     
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          for k in range(len(df_value)-1):
              minus = df_value[k] - df_value[k+1]
              value_lst.append(minus)
          auto_value.append(np.nanmax(value_lst))     
      return ft+'_mai'+str(p),auto_value 
  
  #最近p个月,每两个月间的ft的减少量的最大值
  def Mad(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])      
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          for k in range(len(df_value)-1):
              minus = df_value[k+1] - df_value[k]
              value_lst.append(minus)
          auto_value.append(np.nanmax(value_lst))     
      return ft+'_mad'+str(p),auto_value 
  
  #最近p个月,ft的标准差
  def Std(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.nanvar(df,axis = 1)
      return ft+'_std'+str(p),auto_value 
      
  #最近p个月,ft的变异系数
  def Cva(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value=np.nanvar(df,axis = 1)/(np.nanmean(df,axis = 1 )+1e-10)
      return ft+'_cva'+str(p),auto_value 
  
  #(当月ft) - (最近p个月ft的均值)
  def Cmm(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = df[ft+'1'] - np.nanmean(df,axis = 1 ) 
      return ft+'_cmm'+str(p),auto_value 
  
  #(当月ft) - (最近p个月ft的最小值)
  def Cnm(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = df[ft+'1'] - np.nanmin(df,axis = 1 ) 
      return ft+'_cnm'+str(p),auto_value 
  
  #(当月ft) - (最近p个月ft的最大值)
  def Cxm(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = df[ft+'1'] - np.nanmax(df,axis = 1 ) 
      return ft+'_cxm'+str(p),auto_value 
  
  
  #( (当月ft) - (最近p个月ft的最大值) ) / (最近p个月ft的最大值) )
  def Cxp(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      temp = np.nanmax(df,axis = 1 )
      auto_value = (df[ft+'1'] - temp )/ temp
      return ft+'_cxp'+str(p),auto_value 
  
  #最近p个月,ft的极差
  def Ran(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = np.nanmax(df,axis = 1 )  -  np.nanmin(df,axis = 1 ) 
      return ft+'_ran'+str(p),auto_value 
  
  #最近p个月中,特征ft的值,后一个月相比于前一个月增长了的月份数
  def Nci(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])     
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          for k in range(len(df_value)-1):
              minus = df_value[k] - df_value[k+1]
              value_lst.append(minus)           
          value_ng = np.where(np.array(value_lst)>0,1,0).sum()
          auto_value.append(np.nanmax(value_ng))     
      return ft+'_nci'+str(p),auto_value 
  
  #最近p个月中,特征ft的值,后一个月相比于前一个月减少了的月份数
  def Ncd(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])     
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          for k in range(len(df_value)-1):
              minus = df_value[k] - df_value[k+1]
              value_lst.append(minus)           
          value_ng = np.where(np.array(value_lst)<0,1,0).sum()
          auto_value.append(np.nanmax(value_ng))     
      return ft+'_ncd'+str(p),auto_value    
  
  #最近p个月中,相邻月份ft 相等的月份数
  def Ncn(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])     
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          for k in range(len(df_value)-1):
              minus = df_value[k] - df_value[k+1]
              value_lst.append(minus)           
          value_ng = np.where(np.array(value_lst)==0,1,0).sum()
          auto_value.append(np.nanmax(value_ng))     
      return ft+'_ncn'+str(p),auto_value    
   
  #最近P个月中,特征ft的值是否按月份严格递增,是返回1,否返回0
  def Bup(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])     
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          index = 0
          for k in range(len(df_value)-1):
              if df_value[k] > df_value[k+1]:
                  break
              index =+ 1
          if index == p:            
              value= 1    
          else:
              value = 0
          auto_value.append(value)     
      return ft+'_bup'+str(p),auto_value   
  
  #最近P个月中,特征ft的值是否按月份严格递减,是返回1,否返回0
  def Pdn(ft,p):
      arr=np.array(data.loc[:,ft+'1':ft+str(p)])     
      auto_value = []
      for i in range(len(arr)):
          df_value = arr[i,:]
          value_lst = []
          index = 0
          for k in range(len(df_value)-1):
              if df_value[k+1] > df_value[k]:
                  break
              index =+ 1
          if index == p:            
              value= 1    
          else:
              value = 0
          auto_value.append(value)     
      return ft+'_pdn'+str(p),auto_value            
  
  #最近P个月中,ft的切尾均值,这里去掉了数据中的最大值和最小值
  def Trm(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = []
      for i in range(len(df)):
          trm_mean = list(df.loc[i,:])
          trm_mean.remove(np.nanmax(trm_mean))
          trm_mean.remove(np.nanmin(trm_mean))
          temp=np.nanmean(trm_mean) 
          auto_value.append(temp)
      return ft+'_trm'+str(p),auto_value 
  
  #当月ft / 最近p个月的ft中的最大值
  def Cmx(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = (df[ft+'1'] - np.nanmax(df,axis = 1 )) /np.nanmax(df,axis = 1 ) 
      return ft+'_cmx'+str(p),auto_value 
  
  #( 当月ft - 最近p个月的ft均值 ) / ft均值
  def Cmp(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = (df[ft+'1'] - np.nanmean(df,axis = 1 )) /np.nanmean(df,axis = 1 ) 
      return ft+'_cmp'+str(p),auto_value 
  
  #( 当月ft - 最近p个月的ft最小值 ) /ft最小值 
  def Cnp(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      auto_value = (df[ft+'1'] - np.nanmin(df,axis = 1 )) /np.nanmin(df,axis = 1 ) 
      return ft+'_cnp'+str(p),auto_value 
  
  #最近p个月取最大值的月份距现在的月份数
  def Msx(ft,p):
      df=data.loc[:,ft+'1':ft+str(p)]
      df['_max'] = np.nanmax(df,axis = 1)
      for i in range(1,p+1):
          df[ft+str(i)] = list(df[ft+str(i)] == df['_max'])
      del df['_max']
      df_value = np.where(df==True,1,0)
      auto_value=[]
      for i in range(len(df_value)):
          row_value=df_value[i,:]
          indexs=1
          for j in row_value:
              if j == 1:
                  break
              indexs+=1
          auto_value.append(indexs)
      return ft+'_msx'+str(p),auto_value
  
  #最近p个月的均值/((p,2p)个月的ft均值)
  def Rpp(ft,p):
      df1=data.loc[:,ft+'1':ft+str(p)]
      value1=np.nanmean(df1,axis = 1 )
      df2=data.loc[:,ft+str(p):ft+str(2*p)]
      value2=np.nanmean(df2,axis = 1 )   
      auto_value = value1/value2
      return ft+'_rpp'+str(p),auto_value    
  
  #最近p个月的均值 - ((p,2p)个月的ft均值)
  def Dpp(ft,p):
      df1=data.loc[:,ft+'1':ft+str(p)]
      value1=np.nanmean(df1,axis = 1 )
      df2=data.loc[:,ft+str(p):ft+str(2*p)]
      value2=np.nanmean(df2,axis = 1 )   
      auto_value = value1 - value2
      return ft+'_dpp'+str(p),auto_value   
  
  #(最近p个月的ft最大值)/ (最近(p,2p)个月的ft最大值)
  def Mpp(ft,p):
      df1=data.loc[:,ft+'1':ft+str(p)]
      value1=np.nanmax(df1,axis = 1 )
      df2=data.loc[:,ft+str(p):ft+str(2*p)]
      value2=np.nanmax(df2,axis = 1 )   
      auto_value = value1/value2
      return ft+'_mpp'+str(p),auto_value  
    
  #(最近p个月的ft最小值)/ (最近(p,2p)个月的ft最小值)
  def Npp(ft,p):
      df1=data.loc[:,ft+'1':ft+str(p)]
      value1=np.nanmin(df1,axis = 1 )
      df2=data.loc[:,ft+str(p):ft+str(2*p)]
      value2=np.nanmin(df2,axis = 1 )   
      auto_value = value1/value2
      return ft+'_npp'+str(p),auto_value  
  • 将上面衍生的方法封装成函数

  #定义批量调用双参数的函数        
  def auto_var2(feature,p):
      #global data_new
      try:
          columns_name,values=Num(feature,p)
          data_new[columns_name]=values
      except:
             print("Num PARSE ERROR",feature,p)
      try:
          columns_name,values=Evr(feature,p)
          data_new[columns_name]=values
      except:
             print("Evr PARSE ERROR",feature,p)
      try:
          columns_name,values=Avg(feature,p)
          data_new[columns_name]=values
      except:
             print("Avg PARSE ERROR",feature,p)
      try:
          columns_name,values=Tot(feature,p)
          data_new[columns_name]=values
      except:
          print("Tot PARSE ERROR",feature,p) 
      try:
          columns_name,values=Tot2T(feature,p)
          data_new[columns_name]=values
      except:
          print("Tot2T PARSE ERROR",feature,p)        
      try:
          columns_name,values=Max(feature,p)
          data_new[columns_name]=values
      except:
          print("Tot PARSE ERROR",feature,p)
      try:
          columns_name,values=Max(feature,p)
          data_new[columns_name]=values
      except:
          print("Max PARSE ERROR",feature,p)
      try:
          columns_name,values=Min(feature,p)
          data_new[columns_name]=values
      except:
          print("Min PARSE ERROR",feature,p)
      try:
          columns_name,values=Msg(feature,p)
          data_new[columns_name]=values
      except:
          print("Msg PARSE ERROR",feature,p)
      try:
          columns_name,values=Msz(feature,p)
          data_new[columns_name]=values
      except:
          print("Msz PARSE ERROR",feature,p)
      try:
          columns_name,values=Cav(feature,p)
          data_new[columns_name]=values
      except:
          print("Cav PARSE ERROR",feature,p)
      try:
          columns_name,values=Cmn(feature,p)
          data_new[columns_name]=values
      except:
          print("Cmn PARSE ERROR",feature,p)        
      try:
          columns_name,values=Std(feature,p)
          data_new[columns_name]=values
      except:
          print("Std PARSE ERROR",feature,p)   
      try:
          columns_name,values=Cva(feature,p)
          data_new[columns_name]=values
      except:
          print("Cva PARSE ERROR",feature,p)   
      try:
          columns_name,values=Cmm(feature,p)
          data_new[columns_name]=values
      except:
          print("Cmm PARSE ERROR",feature,p)  
      try:
          columns_name,values=Cnm(feature,p)
          data_new[columns_name]=values
      except:
          print("Cnm PARSE ERROR",feature,p)         
      try:
          columns_name,values=Cxm(feature,p)
          data_new[columns_name]=values
      except:
          print("Cxm PARSE ERROR",feature,p)          
      try:
          columns_name,values=Cxp(feature,p)
          data_new[columns_name]=values
      except:
          print("Cxp PARSE ERROR",feature,p)
      try:
          columns_name,values=Ran(feature,p)
          data_new[columns_name]=values
      except:
          print("Ran PARSE ERROR",feature,p)
      try:
          columns_name,values=Nci(feature,p)
          data_new[columns_name]=values
      except:
          print("Nci PARSE ERROR",feature,p)
      try:
          columns_name,values=Ncd(feature,p)
          data_new[columns_name]=values
      except:
          print("Ncd PARSE ERROR",feature,p)
      try:
          columns_name,values=Ncn(feature,p)
          data_new[columns_name]=values
      except:
          print("Ncn PARSE ERROR",feature,p)
      try:
          columns_name,values=Pdn(feature,p)
          data_new[columns_name]=values
      except:
          print("Pdn PARSE ERROR",feature,p) 
      try:
          columns_name,values=Cmx(feature,p)
          data_new[columns_name]=values
      except:
          print("Cmx PARSE ERROR",feature,p)         
      try:
          columns_name,values=Cmp(feature,p)
          data_new[columns_name]=values
      except:
          print("Cmp PARSE ERROR",feature,p)   
      try:
          columns_name,values=Cnp(feature,p)
          data_new[columns_name]=values
      except:
          print("Cnp PARSE ERROR",feature,p) 
      try:
          columns_name,values=Msx(feature,p)
          data_new[columns_name]=values
      except:
          print("Msx PARSE ERROR",feature,p)
      try:
          columns_name,values=Nci(feature,p)
          data_new[columns_name]=values
      except:
          print("Nci PARSE ERROR",feature,p)
      try:
          columns_name,values=Trm(feature,p)
          data_new[columns_name]=values
      except:
          print("Trm PARSE ERROR",feature,p)
      try:
          columns_name,values=Bup(feature,p)
          data_new[columns_name]=values
      except:
          print("Bup PARSE ERROR",feature,p)
      try:
          columns_name,values=Mai(feature,p)
          data_new[columns_name]=values
      except:
          print("Mai PARSE ERROR",feature,p)
      try:
          columns_name,values=Mad(feature,p)
          data_new[columns_name]=values
      except:
          print("Mad PARSE ERROR",feature,p)
      try:
          columns_name,values=Rpp(feature,p)
          data_new[columns_name]=values
      except:
          print("Rpp PARSE ERROR",feature,p)
      try:
          columns_name,values=Dpp(feature,p)
          data_new[columns_name]=values
      except:
          print("Dpp PARSE ERROR",feature,p)
      try:
          columns_name,values=Mpp(feature,p)
          data_new[columns_name]=values
      except:
          print("Mpp PARSE ERROR",feature,p)
      try:
          columns_name,values=Npp(feature,p)
          data_new[columns_name]=values
      except:
          print("Npp PARSE ERROR",feature,p)
      return data_new.columns.size
  • 对之前数据应用封装的函数

  data_new = pd.DataFrame()
  for p in range(1, 12):  
      for inv in ['ft', 'gt']:  
          auto_var2(inv, p)  
  data_new.columns.tolist()

显示结果

array(['ft_num1', 'ft_evr1', 'ft_avg1', 'ft_tot1', 'ft_tot2t1', 'ft_max1',
       'ft_min1', 'ft_msg1', 'ft_msz1', 'ft_cav1', 'ft_cmn1', 'ft_std1',
       'ft_cva1', 'ft_cmm1', 'ft_cnm1', 'ft_cxm1', 'ft_cxp1', 'ft_ran1',
       'ft_nci1', 'ft_ncd1', 'ft_ncn1', 'ft_pdn1', 'ft_cmx1', 'ft_cmp1',
       'ft_cnp1', 'ft_msx1', 'ft_bup1', 'ft_rpp1', 'ft_dpp1', 'ft_mpp1',
       'ft_npp1', 'gt_num1', 'gt_evr1', 'gt_avg1', 'gt_tot1', 'gt_tot2t1',
       'gt_max1', 'gt_min1', 'gt_msg1', 'gt_msz1', 'gt_cav1', 'gt_cmn1',
       'gt_std1', 'gt_cva1', 'gt_cmm1', 'gt_cnm1', 'gt_cxm1', 'gt_cxp1',
       ........])
  • 上面这种无差别聚合方法进行聚合得到的结果,通常具有较高的共线性,但信息量并无明显增加,影响模型的鲁棒性和稳定性。

  • 评分卡模型对模型的稳定性要求远高于其性能

    • 在时间窗口为1年的场景下,p值会通过先验知识,人为选择3、6、12等,而不是遍历全部取值1~12

    • 在后续特征筛选时,会根据变量的显著性、共线性等指标进行进一步筛选

  • 最近一次(current) 和历史 (history)做对比

    • current/history

    • current-history

特征衍生的方式小结:

#1.数值型数据
各种聚合

#2.类别型数据
统计

#3.当前值/历史值对比
当前值/历史值对比得到新的特征

用户时间序列缺失值处理
  • 用户时间序列缺失值处理

    • 优先考虑补零:大多数特征都是计数,缺失用0补充

    • 用户没有历史购物记录: max_gmv min_gmv 都可以用0补充

    • 用0填充缺失值带来的问题

      • cur/history_avg: 0/0 cur/history_avg:1/0

    • 根据风险趋势填补缺失值 (违约概率大小 无历史购物记录违约概率>有一单历史购物记录>有两单)

      用户没有历史购物记录 cur/history_avg : 0/0? 可以填充-2

      用户有一单历史购物记录 cur/history_avg : 1/0? 可以填充-1

      用户有两单历史购物记录 cur/history_avg : 1/1 可以计算出>0的值

  • 用户最后一次逾期距今天数,如果是白户如何填补缺失值?

  • 如果缺失值比较多的时候,考虑单独做成特征

    • 举例:用户授权GPS序列特征 gps_count_last_3month

    • 缺失意味着用户未授权GPS权限

  • 缺失有明显业务含义,可以填补业务默认值

    • 授信额度(用初始额度)

  • 缺失值处理小结

    缺失值处理
    一般计数类特征优先考虑用0填充
    有风险趋势按风险趋势填补
    缺失数值过多考虑新增是否缺失的特征列
    有业务含义填补业务默认值

时间序列数据的未来信息

  • 以借贷2发生的时间为观测点,下表中的未来信息会把大量退货行为的用户认为是坏客户,但上线后效果会变差

  • 特征构建时的补救方法

    • 对未来信息窗口外的订单计算有效单的特征 net order,NMV

      • NMV:Net Merchandise Value(商品净值)

    • 对未来信息窗口内订单只计算一般特征 order,GMV

      • GMV:Gross Merchandise Volume(商品交易总值)

  • 历史信贷特征也非常容易出现未来信息

    • 举例:

      信用卡 每月1日为账单日,每月10日为还款日,次月10日左右为M1

    • 在上图所示的截面时间(如3月5日)是看不到2月账单的逾期DPD30的情况的

    • 但如果数据库没有快照表会导致我们可以拿到2月账单的DPD30情况

    • 解决方案跟上面例子一样,分区间讨论,可以把账单分成3类

      • 当前未出账账单

      • 最后一个已出账账单

      • 其他已出账账单 (只有这个特征可以构建逾期类特征)

小结:

避免使用到未来信息的方式:加入快照表(添加时间字段)。

未来信息:在时间截面之后的数据。避免使用。

特征变换

分箱(离散化)
  • 概念

    • 特征构造的过程中,对特征做分箱处理时必不可少的过程

    • 分箱就是将连续变量离散化,合并成较少的状态

  • 分箱的作用

    • 离散特征的增加和减少都很容易,易于模型的快速迭代

    • 分箱(离散化)后的特征对异常数据有很强的鲁棒性

    • 单变量分箱(离散化)为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力

    • 分箱(离散化)后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能

    • 分箱(离散化)后,模型会更稳定,如对年龄离散化,20-30为一个区间,不会因为年龄+1就变成一个新的特征

    • 特征离散化以后,可以将缺失作为独立的一类带入模型

  • 怎么离散化(分箱)比较好

    • 等频?等距?还是其他

    • 分成几箱?10箱?100箱...

    • 分箱时缺失值怎么办

  • 常用分箱方法:卡方分箱、决策树分箱、等频分箱、聚类分箱

    • 等频分箱:

      • 按数据的分布,均匀切分,每个箱体里的样本数基本一样

      • 在样本少的时候泛化性较差

      • 在样本不均衡时可能无法分箱

    • 等距分箱:

      • 按数据的特征值的间距均匀切分,每个箱体的数值距离一样

      • 一定可以分箱

      • 无法保证箱体样本数均匀

  • 卡方分箱:使用卡方检验确定最优分箱阈值

    • 将数据按等频或等距分箱后,计算卡方值,将卡方值较小的两个相邻箱体合并

      使得不同箱体的好坏样本比例区别放大,容易获得高IV

    • 卡方分箱是利用独立性检验来挑选箱划分节点的阈值。卡方分箱的过程可以拆分为初始化和合并两步

      • 初始化:根据连续变量值大小进行排序,构建最初的离散化

      • 合并:遍历相邻两项合并的卡方值,将卡方值最小的两组合并,不断重复直到满足分箱数目要求

    [22-35](35-45](45-55](55-65]总计
    good32218
    bad12238
    p50%
    p(good+bad)2222-
    chi2(1-2)^2/2=1/2(2-2)^2/2=0(2-2)^2/2=0(3-2)^2/2=1/2-
    \rm{\overline{p}_{bad} = \frac{\sum_{k}n^k_{bad}}{\sum_{k}(n^k_{good}+n^k_{bad})}} \\ \rm{\chi_{k}^{2}=\frac{(n^k_{bad}-\overline{p}_{bad}(n^k_{good}+n^k_{bad}))^2}{\overline{p}_{bad}(n^k_{good}+n^k_{bad})}}

    合并坏人比例接近平均水平的箱体,留下比例差异大的箱体

案例:使用toad库进行分箱处理

数据集使用germancredit

  • Toad 是专为工业界模型开发设计的Python工具包,特别针对评分卡的开发

  • Toad 的功能覆盖了建模全流程,从EDA、特征工程、特征筛选到模型验证和评分卡转化

  • Toad 的主要功能极大简化了建模中最重要最费时的流程,即特征筛选和分箱。

第一次使用toad,需要安装:

pip install toad==0.1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple/

案例代码实现:

import pandas as pd
import numpy as np
import toad

data = pd.read_csv('../data/germancredit.csv')
data.replace({'good':0,'bad':1},inplace=True)

print(data.shape) # 1000 data and 20 features 
data.head()

显示结果

(1000, 21)
status.of.existing.checking.accountduration.in.monthcredit.historypurposecredit.amountsavings.account.and.bondspresent.employment.sinceinstallment.rate.in.percentage.of.disposable.incomepersonal.status.and.sexother.debtors.or.guarantors...propertyage.in.yearsother.installment.planshousingnumber.of.existing.credits.at.this.bankjobnumber.of.people.being.liable.to.provide.maintenance.fortelephoneforeign.workercreditability
0... < 0 DM6critical account/ other credits existing (not ...radio/television1169unknown/ no savings account... >= 7 years4male : singlenone...real estate67noneown2skilled employee / official1yes, registered under the customers nameyes0
10 <= ... < 200 DM48existing credits paid back duly till nowradio/television5951... < 100 DM1 <= ... < 4 years2female : divorced/separated/marriednone...real estate22noneown1skilled employee / official1noneyes1
2no checking account12critical account/ other credits existing (not ...education2096... < 100 DM4 <= ... < 7 years2male : singlenone...real estate49noneown1unskilled - resident2noneyes0
3... < 0 DM42existing credits paid back duly till nowfurniture/equipment7882... < 100 DM4 <= ... < 7 years2male : singleguarantor...building society savings agreement/ life insur...45nonefor free1skilled employee / official2noneyes0
4... < 0 DM24delay in paying off in the pastcar (new)4870... < 100 DM1 <= ... < 4 years3male : singlenone...unknown / no property53nonefor free2skilled employee / official2noneyes1

5 rows × 21 columns

  • 数据字段说明

    1. Status of existing checking account(现有支票帐户的存款状态)

    2. Duration in month(持续月数)

    3. Credit history(信用历史记录)

    4. Purpose(申请目的)

    5. Credit amount(信用保证金额)

    6. Savings account/bonds(储蓄账户/债券金额)

    7. Present employment since(当前就业年限)

    8. Installment rate in percentage of disposable income(可支配收入占比)

    9. Personal status and gender(个人婚姻状态及性别)

    10. Other debtors / guarantors(其他债务人或担保人)

    11. Present residence since(当前居民年限)

    12. Property(财产)

    13. Age in years(年龄)

    14. Other installment plans (其他分期付款计划)

    15. Housing(房屋状况)

    16. Number of existing credits at this bank(在该银行已有的信用卡数)

    17. Job(工作性质)

    18. Number of people being liable to provide maintenance for(可提供维护人数)

    19. Telephone(是否留存电话)

    20. foreign worker(是否外国工人)

    21. creditability 数据标签

  • toad 中的combiner类用来进行分箱处理

# 初始化一个combiner类
combiner = toad.transform.Combiner()

# 训练数据并指定分箱方法,其它参数可选  # min_samples: 每箱至少包含样本量,可以是数字或者占比
combiner.fit(data,y='creditability',method='chi',min_samples = 0.05)

# 以字典形式保存分箱结果
bins = combiner.export()
#查看分箱结果
print('duration.in.month:', bins['duration.in.month'])

显示结果

duration.in.month: [9, 12, 13, 16, 36, 45]
  • 通常使用双变量图(Bivar图 Bivariate graph)来评价分箱结果。注意,信贷风险分析中Bivar图,纵轴固定为负样本占比

  • 使用bin_plot()画图对分箱进行调整

import matplotlib.pyplot as plt
from toad.plot import bin_plot
#创建对象
c2 = toad.transform.Combiner()
#训练数据,并指定分箱方法
c2.fit(data[['duration.in.month','creditability']],y='creditability', method='chi',n_bins=7)  
#应用分箱结果,labels:是否将分箱结果转化成箱标签
transformed = c2.transform(data[['duration.in.month','creditability']],labels=True)
#传给bin_plot的数据必须是分箱转化之后的
bin_plot(transformed,x='duration.in.month',target='creditability')

显示结果

<AxesSubplot:xlabel='duration.in.month', ylabel='prop'>

  • 上图中柱形图表示每一箱的占比,折线图表示每一箱的坏样本率。一般折线图要呈现出单调的趋势。

  • 可以通过调整箱数实现单调趋势

c2 = toad.transform.Combiner()
c2.fit(data[['duration.in.month','creditability']],
        y='creditability', method='chi',n_bins=5)  # 改成5箱
transformed = c2.transform(data[['duration.in.month','creditability']],labels=True)
#传给bin_plot的数据必须是分箱转化之后的
bin_plot(transformed,x='duration.in.month',target='creditability')

显示结果

<AxesSubplot:xlabel='duration.in.month', ylabel='prop'>

  • 其它分箱方法:聚类分箱(k-means), 决策树分箱,等频分箱,等距分箱

  • 各种分箱方法对比

#分箱方式介绍
#chi:卡方分箱
#dt:决策树分箱
#quantile:等频分箱
#step:等距分箱
#kmeans:KMeans分箱

for method in ['chi', 'dt', 'quantile', 'step', 'kmeans']:  
    c2 = toad.transform.Combiner()  
    c2.fit(data[['duration.in.month','creditability']],
             y='creditability', method=method, n_bins=5)
    bin_plot(c2.transform(data[['duration.in.month','creditability']],labels=True),
                x='duration.in.month',target='creditability')
  • 从单调性和模型稳定性角度考虑一般使用卡方分箱

  • 多值无序类别特征需要做encoding处理,常见encoding方法:Onehot Encoding、Label Encoding、WOE Encoding

    • Onehot Encoding

    • Label Encoding

      婚姻状态 婚姻状态 统计出不同婚姻状态下的逾期率作为数值标签

      未婚 dpd rate

      已婚 dpd rate

      离异 dpd rate

      丧偶 dpd rate

      缺点:数据量少的情况下,某些数据可能有偏差

    • WOE Encoding

      WOE(Weight of Evidence) 反映单特征在好坏用户区分度的度量,WOE编码是一种用于二分类问题的编码方法,通过计算每个类别的证据权重来表示其与目标变量之间的关系。

      优势:WOE越大,bad rate越高,也就是说,通过WOE变换,特征值不仅仅代表一个分类,还代表了这个分类的权重。WOE可以把相对于bad rate显现非线性的特征转换为线性的,且对波动不敏感。遇到异常数据亦能平稳表现。

      应用场景:该方法通常用于分类建模中的特征工程,特别是在信用风险评估、营销模型和欺诈检测等领域。该方法的目标是将分类变量转换为数值变量,以便在统计建模中使用。

      计算公式:

      $$WOE_k=ln(p^k{good}/p^k{bad})$$ 好用户比例/坏用户比例

    婚姻状态GoodBadG-Bln(G/B)WOE
    未婚30%20%10%0.4050.405
    已婚40%10%30%1.3861.386
    离异10%40%-30%-1.386-1.386
    丧偶20%30%-10%-0.405-0.405
    总计100%100%
    • 使用toad计算woe

    from sklearn.model_selection import train_test_split
    X_train,X_test,Y_train,Y_test = train_test_split(data.drop('creditability',axis=1),data['creditability'],test_size=0.25,random_state=450)
    data_train = pd.concat([X_train,Y_train],axis=1)
    #增加一列区分训练/测试的特征
    data_train['type'] = 'train'
    data_test = pd.concat([X_test,Y_test],axis=1)
    data_test['type'] = 'test'
    #设置分箱边界,等价于fit
    adj_bin = {'duration.in.month': [9, 12, 18, 33]}
    
    c2 = toad.transform.Combiner()
    c2.set_rules(adj_bin)
    
    data_ = pd.concat([data_train,data_test],axis = 0)
    #分箱(可视化分箱功能,通过观察变量的单调性,帮助调整分箱节点)
    temp_data = c2.transform(data_[['duration.in.month','creditability','type']])
    
    #绘制badrate_plot图
    from toad.plot import badrate_plot, proportion_plot
    #by:需要观察的特征
    badrate_plot(temp_data, target = 'creditability', x = 'type', by = 'duration.in.month') 
    #绘制每一箱占比情况图
    proportion_plot(temp_data['duration.in.month'])

    显示结果

    <AxesSubplot:xlabel='value', ylabel='proportion'>

    • 上面第一张图中的第一箱和第二箱的bad_rate存在倒挂,说明bad_rate不单调,需要调整。可以将第一箱和第二箱进行合并

    # 假定将第一箱、第二箱合并
    adj_bin = {'duration.in.month': [9,18,33]}
    c2.set_rules(adj_bin)
    
    temp_data = c2.transform(data_[['duration.in.month','creditability','type']])
    badrate_plot(temp_data, target = 'creditability', x = 'type', by = 'duration.in.month')

    显示结果

    #将特征的值转化为分箱的箱号。
    binned_data = c2.transform(data_train)
    
    #计算WOE
    transfer = toad.transform.WOETransformer()
    
    #对WOE的值进行转化,映射到原数据集上。对训练集用fit_transform,测试集用transform.
    data_tr_woe = transfer.fit_transform(binned_data, binned_data['creditability'], exclude=['creditability','type'])
    data_tr_woe.head()

    显示结果

    status.of.existing.checking.accountduration.in.monthcredit.historypurposecredit.amountsavings.account.and.bondspresent.employment.sinceinstallment.rate.in.percentage.of.disposable.incomepersonal.status.and.sexother.debtors.or.guarantors...age.in.yearsother.installment.planshousingnumber.of.existing.credits.at.this.bankjobnumber.of.people.being.liable.to.provide.maintenance.fortelephoneforeign.workercreditabilitytype
    5690.7863130.7866220.069322-0.3841250.3331520.2448020.002898-0.0563410.3550580.0...0.085604-0.157497-0.1744410.0394850.0026480.0128220.0017220.0437421train
    5740.363027-0.2797290.069322-0.384125-0.1594080.244802-0.1733260.154169-0.2123560.0...0.085604-0.157497-0.174441-0.071350-0.2984670.012822-0.0011300.0437420train
    9930.7863130.7866220.0693220.1414840.3331520.2448020.5345270.154169-0.2123560.0...0.085604-0.157497-0.1744410.0394850.3113830.0128220.0017220.0437420train
    3550.3630270.0998120.0693220.272947-0.1594080.2448020.3993130.154169-0.2123560.0...0.5469490.605057-0.1744410.039485-0.2984670.012822-0.0011300.0437421train
    508-1.0729600.0998120.069322-0.384125-0.1594080.2448020.0028980.154169-0.3024470.0...0.085604-0.157497-0.1744410.0394850.0026480.012822-0.0011300.0437420train
    • WOE理解:当前组中好用户和坏用户的比值与所有样本中这个比值的差异。差异通过对这两个比值取对数来表示

      • WOE越大,差异越大,这个分组里的好用户的可能性就越大

      • WOE越小,差异越小,这个分组里的好用户的可能性也就越小。

    • 分箱结果对WOE结果有直接影响,分箱不同,WOE映射值也会有很大的不同

      • 箱的总数在5~10箱(可以适当调整,通常不超过10箱)

      • 并且将每一箱之间的负样本占比差值尽可能大作为箱合并的基本原则

      • 每一箱的样本量不能小于整体样本的5%,原则是每一箱的频数需要具有统计意义

    • 三种encoding的利弊

优势劣势
Onehot Encoding简单易处理、稳定、无需归一化、不依赖历史数据数据过于稀疏
Label Encoding区分效果好,维度小需统计历史数据、不稳定、需要归一化
WOE Encoding区分效果好,维度小,不需要归一化需统计历史数据、不稳定

多值有序类别型特征编码
  • 学历:本科,硕士,博士

  • 一定程度上学历高低能直接对应用户的信用风险,可以当做有序特征

  • 可以把多值有序特征转换为1,2,3...的数值

  • 本科 → 1,硕士→2,博士→3

特征组合

又叫特征交叉(Feature crossing),指不同特征之间基于常识、经验、数据挖掘技术进行分段组合实现特征构造,产生包含更多信息的新特征。

特征维度男程序猿女程序媛
青年青年男程序猿青年女程序媛
中年中年男程序猿中年女程序媛
  • 可以通过决策树模型,基于特定指标,贪心地搜索最优的特征组合形式。上一小结最后的案例为例

  • 基于上述规则可以得出以下特征

x['n1'] = x.apply(lambda x:1 if x.amount_tot>48077.5 \                          
                      and amount_cnt<=3.5 else 0)  
x['n2'] = x.apply(lambda x:1 if x.amount_tot>48077.5 \  
                      and amount_cnt>3.5 else 0)
  • 利用决策树实现特征的自动组合,可以有效降低建模人员的工作难度

用户关联特征

  • 如何评价一个没有内部数据的新客?

    • 使用外部第三方数据

    • 把新用户关联到内部用户,使用关联到的老客信息评估

  • 用户特征关联,可以考虑用倒排表做关联

    • 用户→[特征1,特征2,特征3...]

    • 特征→[用户1,用户2,用户3...]

  • 举例:用户所在地区的统计特征

    • 将用户申请时的GPS转化为geohash位置块

      • geohash:基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码

    • 对每个大小合适的位置块,统计申请时点GPS在该位置块的人的信用分

    • 当新申请的人,查询其所在的位置块的平均信用分作为GPS倒排表特征

    • 倒排表的组成:关键主键+统计指标

      • 关键主键:新用户通过什么数据和平台存量用户发生关联

      • 统计指标:使用存量用户的什么特征去评估这个新客户

    • 常见统计指标

    • 常见关联主键

  • 构造特征要从两个维度看数据:归纳+演绎

    • 归纳:从大量数据的结果总结出规律(相关关系)

    • 演绎:从假设推导出必然的结果(因果关系)

小结

  • 特征工程准备工作

    • ER图

    • 样本设计表

    • 特征框架表

  • 特征构建方法

    • 用户静态信息特征

    • 用户时间截面特征

    • 用户时间序列特征

    • 用户关联特征

  • 缺失值处理

    • 补零

    • 风险趋势

    • 增加缺失特征

    • 业务默认值

  • 未来信息处理

    • 快照表

    • 将数据区分成是否包含未来信息分别处理

  • 特征构造的标准

    • 简单

    • 归纳+演绎

【拓展】可解释性

  • 信贷业务的特征要求

    • 逻辑简单

    • 有强业务解释性

    • 容易构造

    • 容易排查错误

模型的可解释性没有准确的定义,凡是可以协助人理解模型决策过程和结果的方法,都可称之为模型的可解释性。

我们常说一个模型是“黑盒”模型,就是指该模型可解释性差。模型的构建者和使用者无法准确梳理模型的决策依据。

如果某个模型可解释性好,则是说我们能通过某些方法理解模型结果产生的逻辑。

一般来说,算法越复杂,一般精度越好,可解释性越差。

按照可解释性的强弱关系,将算法分类如下:

  • 第一梯度:线性回归、逻辑回归

  • 第二梯度:集成学习(结果是多个树共同决定的)

  • 第三梯度:支持向量机(把数据往高维空间映射,数据会失真)

标签:ft,df,auto,代码,value,风控,详解,data,columns
From: https://blog.csdn.net/2201_75415080/article/details/143651095

相关文章

  • HarmonyOS蓝牙串口协议(SPP)详解:实现设备间可靠数据交换
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在智能设备互联互通的世界里,蓝牙技术扮......
  • Vue自定义指令详解——以若依框架中封装指令为例分析
    自定义指令在Vue.js中,自定义指令提供了一种非常灵活的方式来扩展Vue的功能。以下是对Vue中自定义指令的详细解释:一、自定义指令的基本概念自定义指令允许开发者直接对DOM元素进行低层次操作,而无需编写大量的模板或者JavaScript代码。它们可以响应Vue的响应式系统,从而在数据......
  • [这可能是最好的Spring教程!]Maven的模块管理——如何拆分大项目并且用parent继承保证
    问题的提出在软件开发中,我们为了减少软件的复杂度,是不会把所有的功能都塞进一个模块之中的,塞在一个模块之中对于软件的管理无疑是极其困难且复杂的。所以把一个项目拆分为模块无疑是一个好方法┌──────┐┌─......
  • 代码随想录算法训练营第二十三天| leetcode39. 组合总和、leetcode40.组合总和II、lee
    1leetcode39.组合总和题目链接:39.组合总和-力扣(LeetCode)文章链接:代码随想录视频链接:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)|回溯法精讲!_哔哩哔哩_bilibili思路:跟之前差不多,就是将他的循环改一下,但是我发现有重复的数值了,不知道如何删除1.1自......
  • 使用Java填充Word模板的方法详解
    From: https://www.jb51.net/program/324679hhw.htmJava填充Word模板是一种将动态数据插入到Word文档模板中生成最终文档的过程,通常用于批量创建包含个人信息、报告结果或其他动态内容的文档,本文给大家介绍了使用Java填充Word模板的方法,需要的朋友可以参考下 +目录概......
  • Mysql大表处理方案:分区详解
    前言在我们日常处理海量数据的过程中,如何有效管理和优化数据库一直是一个既重要又具有挑战性的问题,较为通用的做法就是分区、分表、分库了。那我们应该如何选择这几种方式呢?下面本人记录以下几种方式的优劣,希望能够帮助到大家。首先分区:数据库中分区是将表或索引的数据划分成......
  • html网页详细代码 简便
    html网页代码大全1)贴图:<imgsrc="图片地址">2)加入连接:<ahref="所要连接的相关地址">写上你想写的字</a>3)在新窗口打开连接:<ahref="相关地址"target="_blank">写上要写的字</a>4)移动字体(走马灯):<marquee>写上你想写的字</marquee>......
  • Java代码实现行列转换
    本代码想要达到的效果 测试完整代码如下(copy直接运行):publicclassTestConvert{publicstaticvoidmain(String[]args){ArrayList<Attribute>sourceList=newArrayList<>();for(inti=0;i<3;i++){for(intj=0;j<3;j+......
  • 数学建模---t检验样例和matlab代码
    文章目录1.问题概述2.单个样本t检验2.1问题分析2.2matlab代码2.3结果分析3.配对样本t检验3.1问题分析3.2代码求解3.3结果分析4.独立样本t检验4.1问题分析4.2代码求解4.3结果分析4.3结果分析1.问题概述之前介绍了这个t检验的类型:1)单样本t检验:2)配对样本t检验:3)独......
  • 嵌入式硬件电子电路设计(五)MOS管详解(NMOS、PMOS、三极管跟mos管的区别)
    引言:在我们的日常使用中,MOS就是个纯粹的电子开关,虽然MOS管也有放大作用,但是几乎用不到,只用它的开关作用,一般的电机驱动,开关电源,逆变器等大功率设备,全部使用MOS管作为电子开关,使用起来比较方便,简单粗暴,经常用到的只有N沟道的MOS管,并且也针对PMOS讲解,并对比三极管跟mos管的区别。......