精华内容
下载资源
问答
  • C语言评分小系统

    2019-03-20 01:48:41
    NULL 博文链接:https://sunting-bcwl.iteye.com/blog/764366
  • ES+java操作ES

    千次阅读 2019-12-23 22:22:54
    原则上,使用DSL查询 做模糊查询 或其他需要进行相关性评分的场景,其它全用DSL过滤。 用法 { “query”: { “bool”: { 与(must) 或(should) 非(must not) “must”: [ {“match”: {“description”: ...

    1.ES文档操作

    1.1 什么是ES中的文档

    document --存放数据
    在ES里面 通过index索引库 type 类型(表) 行({id:name:}) 列 id /name
    ES是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
    ES使用Javascript对象符号(JavaScript Object Notation),也就是JSON,作为文档序列化格式。JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。

    一个文档不只有数据。它还包含元数据(metadata)—关于文档的信息。三个必须的元数据节点是:
    在这里插入图片描述
    _index:
    索引库,类似于关系型数据库里的“数据库”—它是我们存储和索引关联数据的地方。

    _type:
    在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。可以是大写或小写,不能包含下划线或逗号。我们将使用 employee 做为类型名。

    _id:
    与 _index 和 _type 组合时,就可以在ELasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成。

    另外还包括:_uid 文档唯一标识(_type#_id)
    _source: 文档原始数据
    _all: 所有字段的连接字符串

    1.2 文档的增删改

    在ES中存储数据的行为就叫做索引(indexing),文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以简单的对比传统数据库和ES的对应关系:
    关系数据库(MYSQL) -> 数据库DB-> 表TABLE-> 行ROW-> 列Column
    Elasticsearch -> 索引库Indices -> 类型Types -> 文档Documents -> 字段Fields
    ES集群可以包含多个索引(indices)(数据库),每一个索引库中可以包含多个类型(types)(表),每一个类型包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。

    1.3 创建索引文档

    ①使用自己的ID创建:

    PUT {index}/{type}/{id}
    {
      "field": "value",
      ...
    }
    

    ②ES内置ID创建:

    POST {index}/{type}/
    {
     "field": "value",
     ...
    }
    

    ①②ES响应内容:

    {
    "_index": "itsource",
    "_type": "employee",
    "_id": xxxxxx,
    "_version": 1, //文档版本号
    "created": true //是否新增
    }
    

    ③ 获取指定ID的文档
    GET crud/employee/123?pretty
    ** ④ 返回的内容:**

    {
    "_index" : "crud",
    "_type" : "employee",
    "_id" : "123",
    "_version" : 1,
    "found" : true,
    "_source" : {
       "email": "xxx@qq.com",
       "fullName": "xxxx",
       ...
    }
    }
    

    返回文档的部分字段:
    GET默认返回整个文档,通过GET /crud/employee/123?_source=fullName,email
    只返回文档内容,不要元数据:
    GET crud/employee/123/_source
    ④ 修改文档
    更新整个文档
    post {index}/{type}/{id}

    Id:1,
    Name:xx

    在响应中,我们可以看到Elasticsearch把 _version 增加了。
    {

    “_version” : 2,
    “created”: false
    }
    created 标识为 false 因为同索引、同类型下已经存在同ID的文档。
    在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。
    局部更新文档
    接受一个局部文档参数 doc,它会合并到现有文档中,对象合并在一起,存在的标量字段被覆盖,新字段被添加。

    POST itsource/employee/123/_update
    {
    “doc”:{
    "email" : "nixianhua@itsource.cn", 
    "salary": 1000
    }
    }
    

    email会被更新覆盖,salary会新增。
    这个API 似乎 允许你修改文档的局部,但事实上Elasticsearch
    遵循与之前所说完全相同的过程,这个过程如下:

    1. 从旧文档中检索JSON
    2. 修改它
    3. 删除旧文档
    4. 索引新文档

    脚本更新文档
    也可以通过使用简单的脚本来进行。这个例子使用一个脚本将age加5:
    POST crud/emploee/123/_update
    {
    “script” : “ctx._source.age += 5”
    }
    在上面的例子中, ctx._source指向当前被更新的文档。
    注意,目前的更新操作只能一次应用在一个文档上。

    删除文档
    DELETE {index}/{type}/{id}
    存在文档的返回:
    {
    “found” : true,
    “_index” : “website”,
    “_type” : “blog”,
    “_id” : “123”,
    “_version” : 3
    }
    不存在的返回:
    {
    “found” : false,
    “_index” : “website”,
    “_type” : “blog”,
    “_id” : “123”,
    “_version” : 4
    }
    注意:尽管文档不存在,但_version依旧增加了。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。
    批量操作bulk API – 后台java程序api
    使用单一请求来实现多个文档的create、index、update 或 delete。
    Bulk请求体格式:
    { action: { metadata }}\n
    { request body }\n
    { action: { metadata }}\n
    { request body }\n
    每行必须以 “\n” 符号结尾,包括最后一行。这些都是作为每行有效的分离而做的标记。
    create当文档不存在时创建之。
    index创建新文档或替换已有文档。
    update局部更新文档。
    delete删除一个文档。
    例如:
    POST _bulk
    { “delete”: { “_index”: “crud”, “_type”: “employee”, “_id”: “123” }}
    { “create”: { “_index”: “crud”, “_type”: “blog”, “_id”: “123” }}
    { “title”: “我发布的博客” }
    { “index”: { “_index”: “crud”, “_type”: “blog” }}
    { “title”: “我的第二博客” }
    注意:delete后不需要请求体,最后一行要有回车

    1.4 文档的简单查询

    1.4.1 通过文档ID获取

    GET crud/employee/1

    1.4.2 批量获取

    mget API参数是一个 docs数组,数组的每个节点定义一个文档的 _index 、 _type 、 _id 元数据。如果你只想检索一个或几个确定的字段,也可以定义一个 _source 参数:
    方式1:GET _mget
    {
    “docs” : [
    {
    “_index” : “itsource”,
    “_type” : “blog”,
    “_id” : 2
    },
    {
    “_index” : “itsource”,
    “_type” : “employee”,
    “_id” : 1,
    “_source”: “email,age”
    }
    ]
    }
    方式2:同一个索引库的同一个类型下
    GET itsource/blog/_mget
    {
    “ids” : [ “2”, “1” ]
    }

    1.4.3 空搜索

    没有指定任何的查询条件,只返回集群索引中的所有文档:
    GET _search

    1.4.4 分页搜索

    和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数:
    size : 每页条数,默认 10 pageSize
    from : 跳过开始的结果数,默认 0 beginIndex limit beginIndex,pageSie
    如果你想每页显示5个结果,页码从1到3,那请求如下:
    GET _search?size=5
    GET _search?size=5&from=5
    GET _search?size=5&from=10

    1.4.5查询字符串搜索

    一个搜索可以用纯粹的uri来执行查询。在这种模式下使用搜索,并不是所有的选项都是暴露的。它可以方便快速进行 curl 测试。
    查询年龄为25岁的员工
    GET itsource/employee/_search?q=age:25
    age[20 TO 30]

    2.DSL查询与过滤

    2.1 DSL查询

    由ES提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。
    DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。我们可以这样表示之前关于“老郑”的查询:
    查询字符串模式(查询条件很少):GET crud/employee/_search?q=fullName:xx
    DSL模式(查询条件很多):
    GET crud/employee/_search
    {
    “query” : {
    “match” : {
    “fullName” : “xxx”
    }
    }
    }

    模拟匹配
    GET crud/employee/_search
    {
    “query”: {
    “wildcard” : { “name” : “cc” }
    }
    }

    对于简单查询,使用查询字符串比较好,但是对于复杂查询,由于条件多,逻辑嵌套复杂,查询字符串不易组织与表达,且容易出错,因此推荐复杂查询通过DSL使用JSON内容格式的请求体代替。

    2.2 DSL查询和DSL过滤

    DSL查询

    使用DSL查询,必须要传递query参数给ES。
    GET _search
    {“query”: YOUR_QUERY_HERE}

    一个常用的相对完整的DSL查询:
    GET crud/employee/_search
    {
    “query”: {
    “match_all”: {}
    },
    “from”: 20,
    “size”: 10,
    “_source”: [“fullName”, “age”, “email”],
    “sort”: [{“join_date”: “desc”},{“age”: “asc”}]
    }

    上面查询 表示 查询所有数据,查询fullName,age和email,按照加入日期和年龄进行排序

    DSL过滤

    DSL过滤语句和DSL查询语句非常相似,但是它们的使用目的却不同 :
    DSL过滤 查询文档的方式更像是对于我的条件“有”或者“没有”,–精确查询
    而DSL查询语句则像是“有多像”。–类似于模糊查询

    DSL过滤和DSL查询在性能上的区别 :
    过滤结果可以缓存并应用到后续请求。
    查询语句同时 匹配文档,计算相关性,所以更耗时,且不缓存。
    过滤语句 可有效地配合查询语句完成文档过滤。

    原则上,使用DSL查询 做模糊查询 或其他需要进行相关性评分的场景,其它全用DSL过滤。

    用法

    {
    “query”: {
    “bool”: { 与(must) 或(should) 非(must not)
    “must”: [
    {“match”: {“description”: “search” }}
    ],
    “filter”: {
    “term”: {“tags”: “lucene”}
    }
    }
    },
    “from”: 20,
    “size”: 10,
    “_source”: [“fullName”, “age”, “email”],
    “sort”: [{“join_date”: “desc”},{“age”: “asc”}]
    }

    2.3 使用DSL查询与过滤

    ① 全匹配(match_all

    普通搜索(匹配所有文档):
    {
    “query” : {
    “match_all” : {}
    }
    }

    如果需要使用过滤条件(在所有文档中过滤,红色部分默认可不写):
    GET _search
    {
    “query”: {
    “bool”: {
    “must”: [
    {
    “match_all”: {}
    }
    ],
    “filter”: {
    “term”: {
    “name”: “zs1”
    }
    }
    }
    }
    }

    ② 标准查询(match和multi_match)

    match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
    如果你使用match查询一个全文本字段,它会在真正查询之前用分析器先分析查询字符:
    {
    “query”: {
    “match”: {
    “fullName”: “Steven King”
    }
    }
    }

    上面的搜索会对Steven King分词,并找到包含Steven或King的文档,然后给出排序分值。
    如果用 match 下指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed的字符串时,它将为你搜索你给定的值,如:
    { “match”: { “age”: 26 }}
    { “match”: { “public”: true }}
    { “match”: { “tag”: “full_text” }}

    multi_match 查询允许你做 match查询的基础上同时搜索多个字段:
    {
    “query”:{
    “multi_match”: {
    “query”: “Steven King”,
    “fields”: [ “fullName”, “title” ]
    }
    }
    }
    selec
    t * from employee where fullName = ‘steven King’ or tile = ‘steven King’
    上面的搜索同时在fullName和title字段中匹配。
    提示:match一般只用于全文字段的匹配与查询,一般不用于过滤。

    ③单词搜索与过滤(Term和Terms)

    {
    “query”: {
    “bool”: {
    “must”: {
    “match_all”: {}
    },
    “filter”: {
    “term”: {
    “tags”: “elasticsearch”
    }
    }
    }
    }
    }
    Terms搜索与过滤
    {
    “query”: {
    “terms”: {
    “tags”: [“jvm”, “hadoop”, “lucene”],
    “minimum_match”: 2
    }
    }
    }
    minimum_match:至少匹配个数,默认为1

    ④ 组合条件搜索与过滤(Bool)

    组合搜索bool可以组合多个查询条件为一个查询对象,查询条件包括must、should和must_not。
    例如:查询爱好有美女,同时也有喜欢游戏或运动,且出生于1990-06-30及之后的人。
    {
    “query”: {
    “bool”: {
    “must”: [{“term”: {“hobby”: “美女”}}],
    “should”: [{“term”: {“hobby”: “游戏”}},
    {“term”: {“hobby”: “运动”}}
    ],
    “must_not”: [
    {“range” :{“birth_date”:{“lt”: “xxxxxxx”}}}
    ],

    查询你的爱好 必须 美女 或者 你爱好 游戏或者运动 ,你必须是90后
    "filter": […],
    “minimum_should_match”: 1
    }
    }
    }

    Hobby=美女 and (hobby=游戏 or hobby=运动) and birth_date >= 1990-06-30
    提示: 如果 bool 查询下没有must子句,那至少应该有一个should子句。但是 如果有 must子句,那么没有 should子句也可以进行查询。

    ⑤ 范围查询与过滤(range)

    range过滤允许我们按照指定范围查找一批数据:
    {
    “query”:{
    “range”: {
    “age”: {
    “gte”: 20,
    “lt”: 30
    }
    }
    }
    }

    上例中查询年龄大于等于20并且小于30。
    gt:> gte:>= lt:< lte:<=

    ⑥ 存在和缺失过滤器(exists和missing)

    {
    “query”: {
    “bool”: {
    “must”: [{
    “match_all”: {}
    }],
    “filter”: {
    “exists”: { “field”: “gps” }
    }
    }
    }
    }

    提示:exists和missing只能用于过滤结果。

    ⑦ 前匹配搜索与过滤(prefix)

    和term查询相似,前匹配搜索不是精确匹配,而是类似于SQL中的like ‘key%’
    {
    “query”: {
    “prefix”: {
    “fullName”: “倪”
    }
    }
    }

    select * from xxx where fullName like ‘倪%’
    上例即查询姓倪的所有人。

    ⑧ 通配符搜索(wildcard)

    使用代表0~N个,使用?代表1个。
    **{
    “query”: {
    “wildcard”: {
    “fullName”: "倪
    华"
    }
    }
    }**
    select * from xxx where fullName like ‘倪%华’

    3. 分词与映射

    3.1 分词与映射

    在全文检索理论中,文档的查询是通过关键字查询文档索引来进行匹配,因此将文本拆分为有意义的单词,对于搜索结果的准确性至关重要,因此,在建立索引的过程中和分析搜索语句的过程中都需要对文本串分词。
    ES中分词需要对具体字段指定分词器等细节,因此需要在文档的映射中明确指出。

    3.2 IK分词器

    ES默认对英文文本的分词器支持较好,但和lucene一样,如果需要对中文进行全文检索,那么需要使用中文分词器,同lucene一样,在使用中文全文检索前,需要集成IK分词器。
    下载
    ES的IK分词器插件源码地址:https://github.com/medcl/elasticsearch-analysis-ik

    将IK放置于ES根目录/plugins/ik
    分词器(可默认)
    词典配置:config/IKAnalyzer.cfg.xml
    重启ES
    测试分词器
    POST _analyze
    {
    “analyzer”:“ik_smart”,
    “text”:“中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首”
    }
    注意:IK分词器有两种类型,分别是ik_smart分词器ik_max_word分词器。
    ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。
    ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;

    4. 文档映射Mapper

    ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型。

    4.1 ES字段类型

    ① 基本字段类型

    字符串:text(分词),keyword(不分词) StringField(不分词文本),TextFiled(要分词文本)
    text默认为全文文本,keyword(关键字)默认为非全文文本
    数字:long,integer,short,double,float
    日期:date
    逻辑:boolean
    {user:{“key”:value}}
    {hobbys:[xxx,xx]}

    ② 复杂数据类型

    对象类型:object
    数组类型:array
    地理位置:geo_point,geo_shape

    4.2 默认映射

    查看索引类型的映射配置:GET {indexName}/_mapping/{typeName}
    ES在没有配置Mapping的情况下新增文档,ES会尝试对字段类型进行猜测,并动态生成字段和类型的映射关系。
    在这里插入图片描述

    4.3 简单类型映射

    字段映射的常用属性配置列表
    在这里插入图片描述
    "city": {
    “type”: “text”,
    “analyzer”: “ik_smart”,
    “fields”: {
    “raw”: {
    “type”: “keyword”
    }
    }
    }

    有些类型 有时候需要分词 有时候不需要分词
    city分词
    city.raw 不分词
    那么以后搜索过滤和排序就可以使用city.raw字段名

    ① 针对单个类型的映射配置方式

    查询映射类型:
    GET shop/goods/_mapping
    修改映射类型
    (1)Delete shop;
    (2)PUT shop;
    (3)POST shop/goods/_mapping
    {
    “goods”: {
    “properties”: {
    “price”: {
    “type”: “integer”
    },
    “name”: {
    “type”: “text”,
    “analyzer”: “ik_smart”,
    “search_analyzer”: “ik_smart”
    }
    }
    }
    }
    (4)加入数据
    put shop/goods/1
    {
    “price”:88,
    “name”: “iphone8”
    }

    注意:你可以在第一次创建索引的时候指定映射的类型。此外,你也可以晚些时候为新类型添加映射(或者为已有的类型更新映射)。

    你可以向已有映射中增加字段,但你不能修改它。如果一个字段在映射中已经存在,这可能意味着那个字段的数据已经被索引。如果你改变了字段映射,那已经被索引的数据将错误并且不能被正确的搜索到。
    我们可以更新一个映射来增加一个新字段,但是不能把已有字段的类型那个从 analyzed 改到 not_analyzed。

    ② 同时对多个类型的映射配置方式

    PUT {indexName}
    {
    “mappings”: {
    “user”: {
    “properties”: {
    “id”: {
    “type”: “integer”
    },
    “info”: {
    “type”: “text”,
    “analyzer”: “ik_smart”,
    “search_analyzer”
    }
    }
    },
    “dept”: {
    “properties”: {
    “id”: {
    “type”: “integer”
    },
    …更多字段映射配置
    }
    }
    }
    }

    4.4 对象及数组类型映射–了解

    ① 对象的映射与索引

    {
    “id” : 1,
    “girl” : {
    “name” : “王小花”,
    “age” : 22
    }
    }
    对应的mapping配置:
    {
    “properties”: {
    “id”: {“type”: “long”},
    “girl”: {
    “properties”:{
    “name”: {“type”: “keyword”},
    “age”: {“type”: “integer”}
    }
    }
    }
    }

    ② 数组与对象数组

    注意:数组中元素的类型必须一致。
    {
    “id” : 1,
    “hobby” : [“王小花”,“林志玲”]
    }
    对应的mapping配置是:
    {
    “properties”: {
    “id”: {“type”: “long”},
    “hobby”: {“type”: “keyword”}
    }
    }

    对象数组的映射–用不上
    {
    “id” : 1,
    “girl”:[{“name”:“dlrb”,“age”:32},{“name”:“赵丽颖”,“age”:22}]
    }
    对应的映射配置为:
    “properties”: {
    “id”: {
    “type”: “long”
    },
    “girl”: {
    “properties”: {
    “age”: { “type”: “integer” },
    “name”: { “type”: “text” }
    }
    }
    }
    -----------put
    [{
    Age:31
    Name:林志玲
    },{
    Age:22
    Name:赵丽颖
    }]

    注意:同内联对象一样,对象数组也会被扁平化索引
    {
    “user1.girl.age”: [32, 22],
    “user2.girl.name”: [“林志玲”, “赵丽颖”]
    }

    4.5全局映射

    全局映射可以通过动态模板和默认设置两种方式实现。
    默认方式:default
    索引下所有的类型映射配置会继承_default_的配置,如:
    PUT {indexName}
    {
    “mappings”: {
    default”: {
    “_all”: {
    “enabled”: false 关闭默认映射配置
    }
    },
    “user”: {
    //指定自己的自定义配置
    },
    “dept”: {
    “_all”: {
    “enabled”: true //启动默认配置
    }

    },

    }
    }

    动态模板:dynamic_templates
    注意:ES会默认把string类型的字段映射为text类型(默认使用标准分词器)和对应的keyword类型,如:
    "name": {
    “type”: “text”,
    “fields”: {
    “keyword”: {
    “type”: “keyword”,
    “ignore_above”: 256
    }
    }
    }

    在实际应用场景中,一个对象的属性中,需要全文检索的字段较少,大部分字符串不需要分词,因此,需要利用全局模板覆盖自带的默认模板:
    PUT _template/global_template //创建名为global_template的模板
    {
    “template”: “", //匹配所有索引库
    “settings”: { “number_of_shards”: 1 }, //匹配到的索引库只创建1个主分片
    “mappings”: {
    default”: {
    “_all”: {
    “enabled”: false //关闭所有类型的_all字段
    },
    “dynamic_templates”: [
    {
    “string_as_text”: {
    “match_mapping_type”: “string”,//匹配类型string
    “match”: "
    _text”, //匹配字段名字以_text结尾
    “mapping”: {
    “type”: “text”,//将类型为string的字段映射为text类型
    “analyzer”: “ik_max_word”,
    “search_analyzer”: “ik_max_word”
    }
    }
    },
    {
    “string_as_keyword”: { a
    “match_mapping_type”: “string”,//匹配类型string
    “mapping”: {
    “type”: “keyword”//将类型为string的字段映射为keyword类型
    }
    }
    }
    ]
    }
    }}

    上面的意思:就是如果索引库里面字段 以_text结尾 就需要进行分词,如果不是,就不分词
    测试:
    (1)拷贝上面代码执行
    (2)删除库 delete shop
    (3)创建库 put shop
    (4)加入数据测试

    POST shop/goods/5
    {
    “id”:12,
    “name_text”:”iphone x”,
    “local“:”cnsssss”
    }

    说明:上例中定义了两种动态映射模板string_as_text和string_as_keyword.
    在实际的类型字段映射时,会依次匹配:
    ①字段自定义配置
    ②全局配置
    ③默认配置

    4.6 最佳实践

    (1)有历史数据不做映射
    (2)根据优先级来
    步骤
    (1)如果有库 先删除库 ,如果没有库 ,就创建库
    (2)建库
    (3)做全局映射 --动态模板
    (4)做自定义映射
    (5)操作crud

    5 ES集群

    5.1为什么需要集群

    1)单点故障
    2)高并发
    3)海量数据

    5.2 ES集群相关概念

    ES 的1个集群中放多个节点,放多个shard(shard又分成主的shard和从shard)

    ES cluster(1)—Node()-Shard()

    ES节点类型Node有三种节点:
    master Node:主节点,维护集群信息 索引库操作
    data node:数据节点, 文档crud
    client node:只负责处理用户请求

    通过两个属性进行设置: node.master node.data

    1、默认情况下,每个节点都有成为主节点的资格,也会存储数据,还会处理客户端的请求。- 在生产环境下,如果不修改ElasticSearch节点的角色信息,在高数据量,高并发的场景下集群容易出现脑裂等问题
    2、在一个生产集群中我们可以对这些节点的职责进行划分。建议集群中设置3台以上的节点作为master节点【node.master: true node.data: false】,这些节点只负责成为主节点,维护整个集群的状态。
    3、再根据数据量设置一批data节点【node.master: false node.data: true】,这些节点只负责存储数据,后期提供建立索引和查询索引的服务,这样的话如果用户请求比较频繁,这些节点的压力也会比较大。
    4、在集群中建议再设置一批client节点【node.master: false node.data: false】,这些节点只负责处理用户请求,实现请求转发,负载均衡等功能。

    5.3 集群理解

    5.3.1 shard&replica机制再次梳理以及单node环境中创建index图解

    ①图解单node环境下创建index是什么样子的
    1)单node环境下,创建一个index,有3个primary shard,3个replica shard
    (2)集群status是yellow
    (3)这个时候,只会将3个primary shard分配到仅有的一个node上去,另外3个replica shard是无法分配的
    (4)集群可以正常工作,但是一旦出现节点宕机,数据全部丢失,而且集群不可用,无法承接任何请求
    在这里插入图片描述

    5.4 node环境下replica shard分配

    (1)replica shard分配:3个primary shard,3个replica shard,2 node
    (2)primary —> replica同步
    (3)读请求:primary/replica
    在这里插入图片描述

    5.5 图解横向扩容过程,如何超出扩容极限,以及如何提升容错性

    在这里插入图片描述
    (1)primary&replica自动负载均衡,6个shard,3 primary,3 replica
    (2)每个node有更少的shard,IO/CPU/Memory资源给每个shard分配更多,每个shard性能更好
    (3)扩容的极限,6个shard(3 primary,3 replica),最多扩容到6台机器,每个shard可以占用单台服务器的所有资源,性能最好
    (4)超出扩容极限,动态修改replica数量,9个shard(3primary,6 replica),扩容到9台机器,比3台机器时,拥有3倍的读吞吐量
    (5)3台机器下,9个shard(3 primary,6 replica),资源更少,但是容错性更好,最多容纳2台机器宕机;

    (6)这里的这些知识点,你综合起来看,就是说,一方面告诉你扩容的原理,怎么扩容,怎么提升系统整体吞吐量;另一方面要考虑到系统的容错性,怎么保证提高容错性,让尽可能多的服务器宕机,保证数据不丢失

    5.6 图解ES容错机制:master选举,replica容错,数据恢复

    在这里插入图片描述
    (1)6 shard,3 node
    (2)master node宕机,自动master选举,red
    (3)replica容错:新master将replica提升为primary shard,yellow
    (4)重启宕机node,master copy replica到该node,使用原有的shard并同步宕机后的修改,green

    6.Java API(ES)

    6.1 导包

    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>5.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.7</version>
    </dependency>
    

    6.2 创建文档索引

     //连接es服务方法 嗅探方式 
        public TransportClient getClient() throws Exception{
            Settings settings = Settings.builder()
                    .put("client.transport.sniff", true).build();
            TransportClient client = new PreBuiltTransportClient(settings)
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
            return client;
        }
    

    6.2 获取文档

     @Test
        public void testGet() throws Exception{
            //得到client
            TransportClient client = getClient();
            System.out.println(client.prepareGet("aisell", "user", "1").get().getSource());
            client.close();
        }
    

    6.3 新增文档

    @Test
        public void testES() throws Exception{
            //得到client
            TransportClient client = getClient();
            IndexRequestBuilder builder = client.prepareIndex("aisell", "user", "1");
            Map mp = new HashMap();
            mp.put("name","xxxyy");
            mp.put("age",18);
    
            builder.setSource(mp);
            IndexResponse indexResponse = builder.get();
            System.out.println(indexResponse);
            client.close();
    
        }
    

    6.3 修改文档

     @Test
        public void testUpdate() throws Exception{
            TransportClient client = getClient();
            IndexRequest indexRequest = new IndexRequest("aisell", "user", "1");
            Map mp = new HashMap();
            mp.put("name","test123");
            mp.put("age",28);
            //不存在 就新增 存在 就更新
            UpdateRequest updateRequest = new UpdateRequest("aisell", "user", "1").doc(mp).upsert(indexRequest);
            //执行
            client.update(updateRequest).get();
            client.close();
    
        }
    

    6.4 删除文档

    @Test
        public void testDel() throws Exception{
            TransportClient client = getClient();
            client.prepareDelete("aisell","user","1").get();
            client.close();
            
        }
    

    6.5 批量操作文档

     @Test
        public void testBulk() throws Exception{
            TransportClient client = getClient();
            BulkRequestBuilder req = client.prepareBulk();
            for(int i=1;i<50;i++){
                Map mp = new HashMap();
                mp.put("name","tt"+i);
                mp.put("age",18+i);      req.add(client.prepareIndex("shoppings","goods",""+i).setSource(mp));
            }
            BulkResponse responses = req.get();
            if(responses.hasFailures()){
                System.out.println("插错了");
            }
        }
    

    6.6 分页 排序 过滤

     @Test
        public void testDSL() throws Exception{
            //得到client对象
            TransportClient client = getClient();
            //得到builder
            SearchRequestBuilder builder = client.prepareSearch("shoppings").setTypes("goods");
            //得到bool对象
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            //得到must
            List<QueryBuilder> must = boolQuery.must();
            must.add(QueryBuilders.matchAllQuery());
            //添加filter过滤器      boolQuery.filter(QueryBuilders.rangeQuery("age").gte("18").lte("58"));
            builder.setQuery(boolQuery);
            //添加分页
            builder.setFrom(0);
            builder.setSize(10);
            //设置排序
            builder.addSort("age", SortOrder.DESC);
            //设置查询字段
            builder.setFetchSource(new String[]{"name","age"},null);
            //取值
            SearchResponse response = builder.get();
            //得到查询内容
            SearchHits hits =  response.getHits();
            //得到命中数据 返回数组
            SearchHit[] hitsHits = hits.getHits();
            //循环数组 打印获取值
            for (SearchHit hitsHit : hitsHits) {
                System.out.println("得到结果"+hitsHit.getSource());
            }
        }
    
    展开全文
  • 排序方法在实际的应用场景中非常常见,Scala里面有三种排序方法,分别是: sorted,sortBy ,sortWith 分别介绍下他们的功能: (1)sorted 对一个集合进行自然排序,通过传递隐式的Ordering (2)sortBy 对一...

    原文在这里:https://blog.csdn.net/u010454030/article/details/79016996

    排序方法在实际的应用场景中非常常见,Scala里面有三种排序方法,分别是: sorted,sortBy ,sortWith

    分别介绍下他们的功能:

    (1)sorted

    对一个集合进行自然排序,通过传递隐式的Ordering

    (2)sortBy

    对一个属性或多个属性进行排序,通过它的类型。

    (3)sortWith

    基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。

    例子一:基于单集合单字段的排序

    val xs=Seq(1,5,3,4,6,2)
        println("==============sorted排序=================")
       println(xs.sorted) //升序
       println(xs.sorted.reverse) //降序
        println("==============sortBy排序=================")
       println( xs.sortBy(d=>d) ) //升序
       println( xs.sortBy(d=>d).reverse ) //降序
        println("==============sortWith排序=================")
       println( xs.sortWith(_<_) )//升序
       println( xs.sortWith(_>_) )//降序
    结果:

    ==============sorted排序=================
    List(1, 2, 3, 4, 5, 6)
    List(6, 5, 4, 3, 2, 1)
    ==============sortBy排序=================
    List(1, 2, 3, 4, 5, 6)
    List(6, 5, 4, 3, 2, 1)
    ==============sortWith排序=================
    List(1, 2, 3, 4, 5, 6)
    List(6, 5, 4, 3, 2, 1)
    例子二:基于元组多字段的排序

    注意多字段的排序,使用sorted比较麻烦,这里给出使用sortBy和sortWith的例子

    先看基于sortBy的实现:

    val pairs = Array(
                          ("a", 5, 1),
                          ("c", 3, 1),
                          ("b", 1, 3)
                           )
     
       //按第三个字段升序,第一个字段降序,注意,排序的字段必须和后面的tuple对应
       val bx= pairs.
       sortBy(r => (r._3, r._1))( Ordering.Tuple2(Ordering.Int, Ordering.String.reverse) )
        //打印结果        
        bx.map( println )
    结果:

    (c,3,1)
    (a,5,1)
    (b,1,3)
    再看基于sortWith的实现:

    val pairs = Array(
                          ("a", 5, 1),
                          ("c", 3, 1),
                          ("b", 1, 3)
                           )
           val b= pairs.sortWith{
          case (a,b)=>{
            if(a._3==b._3) {//如果第三个字段相等,就按第一个字段降序
             a._1>b._1
            }else{
            a._3<b._3 //否则第三个字段降序
            }
          }
        }
        //打印结果
        b.map(println)
    从上面可以看出,基于sortBy的第二种实现比较优雅,语义比较清晰,第三种灵活性更强,但代码稍加繁琐

    例子三:基于类的排序

    先看sortBy的实现方法 排序规则:先按年龄排序,如果一样,就按照名称降序排

    case class Person(val name:String,val age:Int)
     
        val p1=Person("cat",23)
        val p2=Person("dog",23)
        val p3=Person("andy",25)
        
        val pairs = Array(p1,p2,p3)
     
       //先按年龄排序,如果一样,就按照名称降序排
       val bx= pairs.sortBy(person => (person.age, person.name))( Ordering.Tuple2(Ordering.Int, Ordering.String.reverse) )
     
        bx.map(
          println
        )
    结果:

    Person(dog,23)
    Person(cat,23)
    Person(andy,25)
    再看sortWith的实现方法:

    case class Person(val name:String,val age:Int)
     
        val p1=Person("cat",23)
        val p2=Person("dog",23)
        val p3=Person("andy",25)
     
        val pairs = Array(p1,p2,p3)
     
        val b=pairs.sortWith{
          case (person1,person2)=>{
            person1.age==person2.age match {
              case true=> person1.name>person2.name //年龄一样,按名字降序排
              case false=>person1.age<person2.age //否则按年龄升序排
            }
          }
        }
        b.map(
          println
        )
    结果:

    Person(dog,23)
    Person(cat,23)
    Person(andy,25)
    总结:

    本篇介绍了scala里面的三种排序函数,都有其各自的应用场景:

    sorted:适合单集合的升降序

    sortBy:适合对单个或多个属性的排序,代码量比较少,推荐使用这种

    sortWith:适合定制化场景比较高的排序规则,比较灵活,也能支持单个或多个属性的排序,但代码量稍多,内部实际是通过java里面的Comparator接口来完成排序的。

    实际应用中,可以根据具体的场景来选择合适的排序策略。

    展开全文
  • 全面的信贷评分模型开发流程介绍

    在这里插入图片描述
    本课程,将从信用评分模型的发展、应用、类型及开发流程等多个方面展开介绍,旨在让初学者全面了解其在信贷金融领域里的可靠性及重要性,并且掌握一定的开发能力。

    文章目录

    1.评分卡简介

    1.1 简介

    定义: 信用评分模型是运用数据挖掘技术和统计分析方法,通过对消费者的人口特征、信用历史记录、行为记录、交易记录等大量数据进行系统的分析,挖掘数据中蕴含的行为模式、信用特征,捕捉历史信息和未来信用表现之间的关系,发展出预测性的模型,以一个信用评分来综合评估消费者未来的某种信用表现。

    应用:信用评分模型是消费者信贷中先进额技术手段,是银行、信用机构、个人消费信贷公司、电信公司、保险公司等涉及消费信用的企业实体最核心的管理技术之一。被广泛应用于信用卡生命周期管理、骑车贷款管理、住房贷款管理、个人贷款管理、消费信贷管理等多领域,在市场营销、信贷审批、风险管理、账户管理、客户关系管理等多方面发挥着十分重要的作用。

    起源:信用评分模型的应用和发展,首先是应消费信贷金融机构风险管理的需要而产生的,然后才逐步应用到市场营销管理、收益管理、客户关系管理等领域。

    发展:从数理分析技术发展的层次上讲,信用评分魔性的发展经历了3个历史阶段:

    • 以客户分类为核心:使用一些描述性统计方法,如均值、方差、频率、概率分布等和探索性统计方法,如聚类分析、因子分析、相关性分析等,对客户资质信息进行简单的分析,结合经验进行初步评估,以此为基础对客户进行分门别类。
    • 以预测模型为核心:是信用分析技术的重大突破,它通过对消费信贷机构的外部信息和内部信息等进行深度挖掘,提炼出大量的反映消费者行为特征和资信能力的衍生变量,并运用先进的数理统计技术把各种变量的信息进行综合,系统性地对客户未来某方面的信用表现作出预测。
    • 以决策模型为核心:比纯粹的预测模型又进了一步,它不仅对消费者未来信用表现进行预测,还会将其以函数的形式加入决策,综合其他多个参考因子,将其对决策的影响数量化。如风险定价、额度管理,不同消费者对不同的利率、额度等的敏感度不同,也就意味着,需要对不同类型的消费者进行区分决策。

    现状:如今,消费金融信贷领域中,评分模型主要应用于:

    • 风险管理
      • 审批
      • 授信
      • 评级
      • 定价
      • 贷后管理
      • 催收
    • 市场营销
      • 获客
      • 精准营销
      • 交叉销售

    效能:它可以为管理人员提供大量具有高度预测力的信息,帮助管理人员制定行之有效的管理策略,以较高的精度有效地开拓市场、控制风险、挖掘收益,实现消费信贷业务的高效益。

    技术:数据库技术、数理统计技术、计算机技术的发展,使信用评分模型的发展获得科技基础。消费信贷活动的过程产生大量发展信用评分模型所需要的相关数据,这些数据反映了消费者信用历史、资信状况、信用行为等方面的信息。

    • 数据库技术:用于大规模收集、整理、保存、提取数据;
    • 数理统计技术:用于从大量的、纷繁复杂的数据中挖掘有用的信息、提炼行为特征、分析行为模式;
    • 计算机技术:使数据分析、模型发展、模型自动化实施等的数据处理规模、速度不断扩大,加速了整体技术的发展。

    1.2 种类

    1.2.1 依预测目的分

    • 风险评分模型
      • 对违约拖欠的风险概率进行预测
    • 收益评分模型
      • 对消费者给信贷机构带来收益的潜力大小进行预测
    • 流失倾向评分模型
      • 对现有客户在未来一定时期内流失的概率进行预测
    • 市场反应评分模型
      • 对目标客户接受信贷机构影响的概率进行预测
    • 转账倾向评分模型
      • 对目标客户把贷款余额从别的银行转账过来的概率进行预测
    • 循环信贷倾向
      • 对目标客户或现有客户利用信用卡账户进行循环信贷的概率进行预测
    • 欺诈评分模型
      • 对信用卡申请或信用卡交易为欺诈行为的概率进行预测

    1.2.2 依发展主体分

    • 信用局评分模型(通用化模型)
    • 行业共享模型(通用化模型)
    • 以银行内部自有数据为评分基础的客户化模型(客制化模型)

    1.2.3 依实证化程度分

    • 专家风险评分模型
    • 半客户化评分模型
    • 完全客户化评分模型

    1.2.4 依模型对象分

    • 账户层次评分模型
    • 客户层次评分模型
    • 消费者层次评分模型

    1.2.5 依生命周期分

    • 拓展客户
      • 管理决策:目标客户判断、产品/激励、利率/年费/其他收费
      • 评分模型:信用局风险评分、信用局收益评分、信用局破产评分、市场反应评分、转载倾向评分
    • 审批客户
      • 管理决策:是否批准、定价、初始信用额度、交叉销售
      • 评分模型:申请风险评分、信用局风险评分、信用局收益评分
    • 管理客户
      • 管理决策:提高/降低信用额度、交易授权、超额透支授信、反欺诈、重新定价、激活/挽留、坏账催收、续发信用卡
      • **评分模型:**行为风险评分、行为收益评分、流失倾向评分、坏账催收评分

    1.2.6 依使用时机分

    • A_score

      • **定义:**进件评分(application score)对每一笔新申请的贷款进行评分,主要用在信贷准入门槛的设计和授信额度的确定方面
      • **数据:**申请前的客户基本信息、登录、行为信息、外部征信数据
      • **目的:**预估到期后发生严重拖欠的概率(逾期天数>60天)
      • 范围:
        • 不经过评分、特列类型的案件处理
        • 信息缺失的案件处理
        • 评分截点的设定策略
        • 例外推翻的控制
        • 应用评分决定贷款额度的策略和定价
      • 项目:
        A_score.rar
    • B_score

      • 定义:行为评分(behavior score)按月对未逾期的贷款进行评分,主要用来在贷后管理中确定客户风险的高低,进而根据风险的不同采取不同的贷后管理手段。
      • 作用:行为评分可用于信贷客户多方面管理,包括额度管理、市场活动、提前预警等。B_score、历史信贷行为、外部征信源、恶意刷额识别评分
      • 策略:按照不同评分,给予不同管理手段策略:
        • 特差:早期预警
        • :限额
        • :不予调额
        • 流失:流失挽回
        • :提升额度
      • 数据:

    图片

    • 项目:
      B_score.rar

    • C_score

      • 定义:催收评分(collection score),对当前月逾期的贷款进行评分。主要是用来在催收管理中确定风险比较高的客户,进而根据风险的不同采取不同的催收手段
      • 数据:逾期信息、行为信息、征信信息、风险分类标注、催收日志挖掘、催收语音转文本
      • 模型
        • 催收难度及力度
        • 失联修复
        • 催收亲密度模型
      • 作用:应用评分决定贷款催收管理策略:
        • **入催客户:**忘还款、手头紧、老赖
        • **计算催收容易度:**设备特征、行为特征、信贷表现、前几期还款情况
        • **决策结果:**易、中、难
      • 项目
        C_score.rar
    • F_score

      • 定义:F_score,利用多种定量方法,评估欺诈概率,拒绝疑似恶意骗贷客户的进件及申请
      • 模型:
        • 多种算法集成学习
          • 业务规则
          • 逻辑回归
          • 随机森林
          • 神经网络
        • 关联风险模型
          • 图数据库
          • 关联图谱
            • 定义:利用图数据库、聚类分析和复杂网络,实时识别群体欺诈风险,给出团案预警
            • 过程:实时数据清洗——计算连通图——团体分割——团伙特性分析
          • 复杂网络
        • **反欺诈预警模型 **
      • 数据
        • 设备信息:异常设备、网络、地址
        • 行为信息:异常注册、登录、点击
        • 关联信息:关联人风险
        • 授信信息:征信数据、机构黑名单
      • 项目
        F_score.rar

    1.3 作用及优势

    特性:客观性、一致性、准确性、全面性、效率性

    作用

    • 风险模式数据化
    • 提供客观风险量尺:减少主观判断
    • 提高风险管理效率:节省人力成本
    • 风险管理
    • 市场营销
    • 财务、资本预算及考核绩效
    • 资产证券化及组合模式的创新

    优势:

    • 更稳:大数定律,坏账率更可控
    • 更快:自动化审批,提升效率
    • 更省:降低人力成本(审批、反欺诈、催收)
    • 更充分地使用弱变量
    • 反欺诈:预防 > 亡羊补牢

    2. 评分卡项目规划

    在设置评分卡前,风险管理单位必须先就其对信用评分的期望、应用计划及策略提出项目规划,主要包括以下六项:

    2.1 项目目标

    陈述目前作业现况以及想通过评分卡项目解决的问题。信用评分卡的建置需投入大量时间、精力、金钱、人力,风险管理各单位人员需思考当前工作问题及对评分卡的期望,目标设定必须清楚明确,最后产出结果有真正的帮助。
    目标确认后,若需与外部信用评分卡厂商合作,依需求内容、开发时间、开发成本、开发经验与能力等构面设计选商条件并定订各项条件的评分权重。

    2.2 项目范围

    依急迫程度排列优先处理程序,避免一次性处理太多问题,做到聚焦。
    针对选定目标设定项目范围,包括项目主要内容、涉及业务、相关部门、项目组织架构、项目成员等,逐项确认并列于计划书内。

    2.3 时程规划

    时程规划可分为:内部前置规划、流程与系统修改、评分模型建置、效力测试、上线导入等几个重要阶段。项目长度依复杂度及数据质量而定,一般来说大约界于6~9个月。

    2.4 成本效益分析

    成本评估包括相关设备扩充、系统购置或修改、模型开发等费用。效益则分为质化与量化两方面,质化效益包括风险管理技术与观念的提升、授信质量稳定等,量化效益须估算可节省的人力、作业时间及作业成本等。

    2.5 配套措施

    与信用评分有关的相关事项如授信规范、申请书格式修改、进件及征审流程设计、数据质量确认、教育训练等皆须事先规划,另外与之搭配的系统如评分运算引擎、决策系统及征审系统等也须在评分模型建置完成前准备就绪,否则将出现空有评分模型却无使用平台或业务流程运转不顺等窘境。

    2.6 运营计划

    信用模型上线之后的实际应用于管理,诸如信用评分的应用、模型效能监控、相关系统维护、紧急备援计划、运营作业成本、MIS分析等作业皆须详细规划,以确保信用评分与风险管理业务整合之后能够顺利运行。

    3.评分卡开发流程

    图片

    3.1 业务理解

    3.1.1 开发目的

    此步骤非常关键,却易被忽略。开发模型前,必须先决定评分目的及要预测的事件,并要有明确的定义。模型应用目的不同,对变量选择或好坏客户的定义也会有所不同。
    风险管理单位按照项目规划设定的目标与模型建置人员讨论,确认建置模型的目的。另外双方对项目进行方式、建置时程、成本、交付文件项目及格式、模型测试指针、项目验收标准、教育训练,以及其他特殊要求或条件限制达成共识。

    3.1.2 模型要求

    如:可解释、复杂度、样本量

    3.1.3 客户类型

    如:自有渠道、外部渠道、新客、老客

    3.1.4 产品类型

    如:大额、小额、单期、分期

    3.2 基本定义

    评分目的确认之后,紧接着要对建模所需的重要指标的基本定义进行讨论。

    3.2.1 窗口期

    • 观察期
      • 定义:变量计算的历史期间
      • 区间:不宜太短,稳定性不高;也不宜过长,无法反映近期情况,一般为6~24个月
      • 特点:观察期时间窗口越长,建模样本数越少
    • 表现期
      • 定义:准备预测的时间长度,如:预测客户未来12个月内出现违约的几率
      • 区间:一般为12~24,根据产品不同会有变化
      • 特点:表现期越长,违约率越高

    3.2.2 违约定义

    对于预测建模,定义目标变量是最重要、对建模结果影响最大的一步。坏客户定义越严格,意味着坏账率越低,同样意味着通过率越低。
    违约定义并不限定为逾期,只要认定为非目标客户,如未来一年内出现M2以上逾期、催收、呆账、强停、拒住、协商等,皆可当成评分模型中的违约条件。
    银行业信用评分解决方案默认的目标事件定义选择:

    • 不良或逾期
      • (观察窗口内)90天逾期
      • (观察窗口内)230天或260天或1*90天
    • 良好:
      • 从未逾期
      • 从未在观察期内逾期

    也可根据坏账转移矩阵定义,不同账期客户转移到更坏的概率不同,选取显著变化的节点
    图片

    定义好的目标变量(GOOD/BAD)基于以上良好/不良定义创建并与ABT表并接后预测建模。

    3.2.3 适用范围

    • 灰色区间

    某些条件下的客户,风险处于较为模糊的灰色地带,很难将其归类为好客户或坏客户,此类客户无鲜明的风险特征,很难判断好坏。为强化模型的区隔能力,灰色地带的客户不适合纳入建模样本中。不过在模型完成后,看加入测试,观察分数落点,理论上中等分数居多。
    业务中,可利用**转移分析(roll rate analysis)**观察各条件下的客户经过一段时间后的表现,观察区隔力和稳定度,作为灰色区间客户的好坏判断条件。
    也可加入人工干预,直接进行评估。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sCERUsrq-1569656926265)(https://uploader.shimo.im/f/Ms3vUKfOFrIyOgvl.png!thumbnail)]

    • 无评分意义

    并非所有状况都须依赖评分来判断风险,有些类型客户评分意义不大。例:

    • 目前处于逾期中的客户,本身已出现违约事实,无须加以评分

    • 数据缺漏严重

    • 数据期间过短

    • 近来无信用往来记录

    • 非常规对象

    用于建模的客户或申请者必须是日常审批过程中接触到的,需排除几类人群:

    • 异常行为:如销户、按条例拒绝的、特殊账户
    • 特殊账户:出国、失窃、死亡、未成年人、员工账户、VIP
    • 其他建模对象:欺诈、主动销户者(流失)

    3.2.4 样本区隔

    为了达到最佳效果,通常依客群或产品特性做样本区隔,分别开发数张字评分卡。
    **例:**信用卡的行为评分卡,预借现金、长期循环及全额缴清等几类客户行为各有特殊,其风险变量及变量权重可能有所差异,可考虑分别就这几类客户开发专属的子评分卡。
    若受预算、开发时间、数据积累等限制,暂时无法细切区隔而共享同一评分卡也可以,权宜做法为调整切分点或外加条件以做调整,不过效果可能较差。
    样本区隔不宜过度,适度有助于提高模型预测效果,过度则不但不利于后期子评分卡维护,且建模样本不足,反而影响模型的预测能力及稳定度。

    3.3 数据准备

    整个建模项目,最耗费时间的通常是数据整理阶段,对于入模数据,有如下多方面的准备条件。

    3.3.1 数据来源

    图片

    3.3.2 数据有效性

    • 正确性:数据真实准确
    • 完整性:广度、深度、长度,数据保留完整,缺失率低
    • 实时性:可以反映客户近期情况,实时更新客户动态信息变化
    • 合法性:数据使用必须合法,使用前需确认当地法令对授信准驳依据是否有特殊限制。某些国家不允许以性别、年龄、种族、地域等因子作为准驳依据条件。
    • 可用性
      • 不合理数据。如年龄过小或多大,予以剔除
      • 缺失数据。可利用平均数、众数、邻近值进行填充,如空值有特殊意义,可保留。
      • 数据加密格式。了解数据格式,厘清数据使用限制。

    3.3.3 原始变量选择

    获取到数据后,根据业务提取重要特征,建立对应数据字典
    图片

    3.3.4 样本选择

    • 样本数量:

    不同模型对数据量的要求不同,基本要求在5000+,最好可以达到3~10W,数据量越多越好。

    • 样本质量:

    样本内的数据需满足如下要求:

    • 代表性
    • 充分性:样本分布均匀
    • 时效性:窗口期内
    • 排除性:唯一标识
    • 表现推测性:正负样本分布明显

    3.3.5 抽样方式

    • 随机抽样
    • 分类抽样

    3.3.6 数据集准备

    按照要求准备原始变量及样本集,并且完成数据合并

    3.3.7 数据采样

    • 采样目的
      • 均衡样本
    • 采样方式
      • 过采样
      • 欠采样
      • 变量权重调整
    • 采样结果
      • 好坏样本比率为:3:1~5:1

    3.3.8 样本切分

    通常将样本集拆分为训练集测试集,通过评估模型在两个数据集上的表现,评估准确性及稳定性。可增加验证集,迭代模型区隔力和稳定度。

    • 训练集
      • 即发展组样本,一般占比70%
      • 用于建模
    • 测试集
      • 即对照组样本,一般占比30%
      • 建模完成后做验证之用

    3.4 数据探索

    3.4.1 数据质量检查

    • 正态性检验
    • 准确性诊断

    3.4.2 描述性统计分析

    • 样本总体
      • 样本量
      • 特征量
    • 变量类型
      • 连续型变量
      • 离散型变量
        • 数值型
        • 类别型
    • 变量分布
      • 位置度量
        • 中位数、众数
      • 集中程度
        • 均值、方差
      • 离散程度
        • 偏度、峰度
    • 缺失程度
      • 变量缺失
      • 样本缺失

    3.4.2 EDA探索性分析

    探索性分析和数据描述是检查数据并理解其特征的一系列过程的名称。在评分卡开发过程中,需要进行下列指标计算及变量分析:

    • WOE转换(证据权重)

    WOE值是分箱i的坏客户分布与好客户分布的比值的对数,调整为分箱i的坏好比与总体样本的坏好比 的比值的对数,衡量了分箱i对整体坏好比的影响程度。
    图片

    图片

    • IV(信息值)

    图片
    图片

    • 单变量分析(Single Factor Analysis):候选变量单变量统计特征的评价,及其取值在变量范围内的分布。完成变量分箱、WOE编码与IV计算后,我们需要做单变量分析,一般从两个角度进行分析:

      • **单变量X分布稳定性:**各箱占比相对均匀,每项占比不宜过高或过低,一般不低于5%
      • 单变量X与目标变量Y关联:验证单变量重要性
        • 从IV出发,筛选IV较高值
        • 单变量与目标变量做回归检验P值
    • 多变量分析(Multi Factors Analysis) :通过列联表、关联性和相关性指标确定不同变量之间的检验关系。多变量分析从两个角度分析变量的特性并完成筛选工作:

      • 变量间的两两线性相关性,通过相关系数矩阵
        • 选择IV较高的
        • 选择分箱均衡的
      • 变量间的多重共线性与目标变量关系,变量筛选
        • 特征重要性:随机森林、GBDT、XGBOOST
        • 模型拟合优度和复杂度:基于AIC的逐步回归
        • 带约束:LASSO
        • 多重共线性检验:VIF
    • 违约要素分析:计算每个候选预测变量分类或分段条件下的违约率分布。

    3.5 数据清洗

    3.5.1 格式处理

    3.5.2 缺失值处理

    3.5.3 异常值处理

    3.5.4 数据规约

    3.5.5 变量粗筛

    • 无意义变量
      • 如唯一标识、手机号等无效字段
    • 常量
      • 单类别比例超过95%,默认为常量,可剔除
    • 缺失率
      • 缺失率不高于设定值
    • 单类别比例
      • 单类别比例不低于5%

    3.6 特征工程

    3.6.1 特征衍生

    • 衍生目的

    基于业务,衍生适合入模的好特征,好的特征具有以下优势:

    • 稳定性高:市场因素,人群产品稳定的情况下,特征的分布稳定
    • 区分性高:违约与未违约客群的分布显著不同
    • 差异性大:不能在全部或大多数客群上取单一值
    • 可解释性强:特征与信用风险的关系符合风控逻辑
    • 衍生方式
      • 基于不同类别型变量,衍生不同时间切片某种操作频率、频次、登录方式个数等。
      • 基于时间变量,衍生账龄、在账月份数
      • 波动率指标,某种操作频次的频率、标准差
      • 决策树创建新特征

    图片

    • 衍生手段
      • 手动创建
      • 暴力生成

    3.6.2 特征分箱

    • 分箱定义:
      • 连续变量离散化
      • 状态多的离散变量合并少状态
    • 分箱优势:
      • 稳定性:避免特征中无意义的波动对评分带来波动
      • 缺失值处理:可将缺失作为独立的分箱带入模型
      • 异常值处理:可于其他值合并作为一个分箱
      • 无需归一化:从数值型变为类别型,没有尺度差异
    • 分箱限制:
      • 有一定的信息丢失,数值型变量分箱后变为取值有限的几个值
      • 需要编码。分箱后的变量是类别型,不能直接带入Logistic模型中,需要进行一次数值编码
    • 分箱方法:

    分箱原理为相似度高的归为一组,有监督考虑业务含义相似度,无监督考虑样本分布相似度。

    • 有监督
      • Best KS
      • ChiMerge卡方分箱
      • 决策树最优分箱

    图片

    • 无监督
      • 等频
      • 等距
      • 聚类

    3.6.3 特征编码

    • 编码方式
      • 数值编码
      • one-hot编码
        • 针对类别型大于2的分类变量,将其哑变量化
      • WOE编码

    3.6.4 特征选择

    为了提高信息值,需调整合并WOE相近的组别,最后得到的分组结果为粗分类。待所有长清单的变量信息值皆计算完成后,即可从中挑选变量,优先排除高度相关、趋势异常、解释不易及容易偏移者。
    经过筛选后的变量集合成为短清单,这个清单即模型的候选变量。在建模时可利用顺向进入
    法(forward selection)、反向排除法(backward elimination)、逐步回归法(stepwise)等方法选出效果最佳的变量组合。

    • 选择方式
      • 重要性
      • 共线性
      • 信息值
        • IV值高于0.2
        • IV高于10的剔除
      • 模型筛选
        • LASSO
        • stepwise

    3.7 模型训练 model training

    3.7.1 算法选择

    • Logistic
    • 优点:
      • 成熟、成功
      • 评分结果稳定
      • 评分构成透明
      • 实施部署简单
      • 不容易过拟合

    图片
    图片

    图片
    图片
    图片

    • GBDT

    图片
    图片

    • XGBOOST

    图片
    图片

    • 神经网络

    图片

    • 组合模型

    图片
    图片

    3.7.2 训练 train

    3.7.3 测试 test

    3.7.4 验证 oot

    • 准确性、稳健性、有意义
    • 样本外测试
    • 时间外测试

    3.8 拒绝推断

    通常,我们用以建立评分卡的建模对象人群(以前的Accepts)在结构上可能与实际申请人群存在着结构上的差异。因为只有接受了的申请者才能够对其定义目标变量。但我们将在生产系统中部署的模型需要对未来所有申请者评分。因此需要对拒绝过的用户做违约推断,即拒绝演绎、婉拒推论。
    只有当建模的接受人口(Accepts)与实际申请总体差别才需要拒绝推断:

    • 很大的时候评分卡批准率相对较低(低于70%)
    • 或以前的拒绝标准根据完全的人为判断决定的时候

    是否两总体间存在较大差异,可以通过一系列特征比较的图表来进行判断
    基本思路:

    • 利用在接受人群基础上训练的模型对拒绝人群评分
    • 将拒绝人群分为“推断好/推断不良”
    • 将此作为拒绝人群的目标变量累加(Append)到接受人群建模数据里
    • 对新数据集重复模型训练过程

    过程图
    图片

    具体方法

    • K近邻法

    当K=5时,对于新样本的预测选取最邻近的5个观测的分类的众数。

    3.9 标准评分转换

    基于预先设定的PDO及Base Point 对每个入模特征规则配置相应分值
    图片

    图片

    图片

    3.10 效力验证

    评分卡模型构建完成后需要验证魔性的性能,保证模型既稳定、有效,部署后需持续监测模型的表现,做到及时更新与迭代

    3.10.1 基本要求

    一般一个好的模型应该达到以下几个基本要求:

    • 精确性。达到可接受水平,避免过拟合。
    • 稳健性。要求最终模型的变量应该能够确保包含稳健一直的数据,能够在后续实施阶段准确获取,能够适用于更广范围的数据集。
    • 有意义。即业务变量及其预测值是可解释的,例如:信用卡的额度利用率越高,违约率相应也越高。
    • 模型中变量不宜过多。通常,包含的变量不超过1020个(最优1012个),变量太多可能导致过拟合,变量太少往往区分度不够。

    图片

    3.10.2 模型准确性

    • KS

    KS(柯尔莫哥洛夫-斯米尔诺夫kolmogorovsmirnow)图纵轴为坏客户累计百分比,横轴为 总体样本累计百分比。perf_eva函数绘制KS 曲线过程:
    ◦ 先将样本随机排列,随机种子seed默认为 186
    ◦ 按照预测违约概率倒序排列(坏客户累计百 分比曲线位于上方)
    ◦ 分为groupnum(默认20)等份 ◦ 计算每一等份中违约与正常客户的累计百 分比
    ◦ 绘制出两者之间差值即为KS曲线
    • KS曲线中的最大值即为KS值,其取值范围 0~1。KS值越大模型的区分能力越好。
    • 通常申请评分卡要求KS 0.3。而且测试集 与训练集的KS值相差小于0.01。
    图片

    • ROC与AUC

    • ROC(受试者工作特征曲线Receiver Operating Charactersitic)曲线纵轴为 真正例率(True Positive Rate, TPR),横 轴为假正例率(False Positive Rate, FPR):
    ◦ 先将样本随机排列,随机种子seed默认为 186
    ◦ 按照预测违约概率降序排列
    ◦ 分概率值计算好坏客户数量,然后计算 TPR=TP/(TP+FN)与FPR=FP/(TN+FP) ◦ 以TPR为纵轴FPR为横轴绘制散点图即为 ROC曲线
    • AUC(Area Under ROC Curve)为ROC曲线 下面积之和,其取值范围0~1。AUC值越大模 型效果越好。
    • 行为评分卡通常要求AUC 0.75,申请评分 卡的AUC相对低一些也能够接受。
    图片

    • GINI系数

    3.10.3 模型排序性

    • Lift

    3.10.4 变量有效性

    • IV

    3.10.5 模型稳定性

    • 评分稳定性指标(PSI)

    图片
    图片

    • 特征分布指标(VSI)

    3.10.6 评分排序性

    信用评分与违约概率成反比,分数越高停贷率越低;客群的评分分布区间一般符合正态性,有一定的离散度。
    图片

    3.11 模型报告

    整理完整的模型开发过程报告

    4.模型部署

    4.1 模型文件

    4.1.1 pkl

    4.1.2 pmml

    4.2 部署方式

    4.2.1 脚本

    4.2.2 决策引擎

    决策引擎配置

    • 位置:DW
    • 内容:信用评分——风险策略——数据集市CRM
    • 作用:决策额度、计算、定价

    5.评分卡的切分与使用

    5.1 评估指标

    利润贡献者所带来的利润,在弥补着利润消耗者带去的损失,信贷风险管理,讲究一个平衡和最优。综合评估模型不同决策点下的通过率和逾期率,制定最优效益的评分决策

    • 逾期率
    • 通过率
    • 综合盈利
    • 准入线

    5.2 设定策略

    • 准入线的设定策略
    • 风险切分点的设置
    • 不经过评分、特别类型的申请件的处理
    • 评分卡信息缺失的申请件处理
    • 例外推翻的控制

    图片

    图片

    5.3 基于A_score的额度定价

    预先设定好基础额度base limit(B),盖帽额度hat limit(H),托底额度floor limit(F)。评分最高的区间杜颖的预期违约率是Pmin,评分最低的区间对应的预期违约率是Pmax,占比最高的区间对应的预期违约率是P0,某一条进件对应的预期违约率是P1,则该进件对应的授信额度是:
    图片

    5.4 基于A_score的利率定价

    增添多种其他利率因子,同样与模型评分相关

    6.模型监控

    模型实施后,要建立多个报表对魔性的有效性、稳定性进行监控

    6.1 监控报表

    6.1.1 稳定性监控

    比较评分卡上线后建模训练样本客户的分值分布,监控模型的有效性。
    图片
    图片
    图片

    6.1.2 特征监控

    比较评分卡上线后和建模训练期间的每个特征的分布,监控特征的变化趋势,从而评估模型的有效性和稳定性
    图片
    图片

    6.1.3 未过评分账户监测

    6.1.4 不良贷款分析

    评估不同分数段的不良贷款,并与建模训练期间的预测进行比较,监控客户信贷质量。
    通过对不同分数段的不良信贷资产进行账龄分析、迁徙率分析,监控信贷资产质量是否发生显著性的变化。
    图片

    6.1.5 拒绝原因分析

    分析被评分卡拒绝的原因分布
    图片
    图片

    6.2 风险跟踪

    • 风险趋势分析
    • 异常行为分析
    • 欺诈网络分析
    • 风险警告

    7.模型调优

    网贷市场环境变化快,评分卡生命周期短,相比传统信贷模型迭代频率高。评分模型需保持稳定,当产品、客群、宏观经济、监管政策等没发生重大变化时,不同时间上的评分结果应保持稳定,便于策略应用。
    图片

    7.1 调优原因

    7.1.1 市场环境变化

    • 市场转移
    • 行业变化
    • 产品变化

    7.1.2 模型监控

    • 入模变量发生重大偏移
    • 当前评分与建模评分分布发生变化
    • 模型区分能力变差
    • 变量区分度变差

    7.1.3 新变量探索引入

    • 新数据源引入
    • 新的预测变量探索

    7.2 调优方法

    收紧或放松

    7.2.1 A类调优

    • 在通过的客群中寻找差客户拒绝
    • 将会降低通过率,且降低逾期指标
    • 离线即可完成量化分析

    7.2.2 D类调优

    • 在拒绝的客群中寻找好客户通过
    • 将会提高通过率,逾期指标可能增加
    • 需要决策引擎标记豁免部分样本分析

    7.3 调优步骤

    • 确认调整贷前策略还是贷中策略
    • 是D类调优还是A类调优
    • 量化分析调优阈值
    • 预测按照方案调整后的效果
    • 调整后验证结果与预计效果是否
    • 重复修正

    7.4 调额步骤

    • 筛选可调额客户
    • 分为调额组合对照组
    • 调额后调额组对照组资产趋势分析
    • 根据结果回调最初筛选可调额客户的规则

    8.常见问题

    8.1 数据问题

    • 历史数据量过少
    • 历史数据无切片

    8.2 建模问题

    • 建模目标不明确
    • 模型过拟合
    • 模型选型不合理
    • 错误使用后验变量
    • 评分的评估指标不合理
    • 离线数据与在线数据不一致

    8.3 业务问题

    • 沟通问题
    展开全文
  • 项目中遇到一个需求,需要把sql中查出的数据,进行复杂计算后,进行多条件排序(只用sql无法做到)。因此用到了java8 Stream中的sort来解决,这篇文章的用途,是为了... // 评分 private Integer score; // 入职时长.

    项目中遇到一个需求,需要把sql中查出的数据,进行复杂计算后,进行多条件排序(只用sql无法做到)。因此用到了java8 Stream中的sort来解决,这篇文章的用途,是为了让更多需要用到多条件排序的人可以迅速上手。

    一、案例

    pojo

    @Data
    public class TestInfo {
        private Long id;
        // 姓名
        private String name;
        // 评分
        private Integer score;
        // 入职时长(月)
        private Integer onJobMonth;
        // 上月评分
        private Integer lastScore;
    }
    

    排序需求

    对员工评分进行排序。评分高者在前,评分相同者,在司工龄长的在前,评分与工龄都相同的,上月评分高的在前。

    排序数据及写法

    姓名评分工龄(月)上月评分
    张三1010
    李四20310
    王五1002290
    赵六10032100
    腾七10031100
    加斯滕601230
    马佳佳601240
    巴神60320
    List<TestInfo> testInfos = buildTestInfo();
    
            List<TestInfo> sortList = testInfos.stream().sorted(
                    // 以上月评分排序,评分高的在前
                    Comparator.comparing(TestInfo::getLastScore, Comparator.reverseOrder())
                            // 以在司时长排序,时间长的在前
                            .thenComparing(TestInfo::getOnJobMonth, Comparator.reverseOrder())
                            // 以评分排序,评分高的在前
                            .thenComparing(TestInfo::getScore, Comparator.reverseOrder())).collect(Collectors.toList());
            sortList.forEach(System.out::println);
    
    // 排序结果
    TestInfo(id=4, name=赵六, score=100, onJobMonth=32, lastScore=100)
    TestInfo(id=5, name=腾七, score=100, onJobMonth=31, lastScore=100)
    TestInfo(id=3, name=王五, score=100, onJobMonth=22, lastScore=90)
    TestInfo(id=8, name=马佳佳, score=60, onJobMonth=12, lastScore=40)
    TestInfo(id=6, name=加斯滕, score=60, onJobMonth=12, lastScore=30)
    TestInfo(id=7, name=巴神, score=60, onJobMonth=3, lastScore=20)
    TestInfo(id=2, name=李四, score=20, onJobMonth=3, lastScore=10)
    TestInfo(id=1, name=张三, score=10, onJobMonth=1, lastScore=0)
    

    由排序结果可知,在sort中,代码逻辑中,将需求的排序顺序翻转即可达到效果。至于原因,看到这里的朋友,脑补下每次的排序结果,就能理解了。

    二、关于Comparator

    Comparator的thenComparing用在comparing及相关方法之后, 是以上次排序为结果进行再排序。
    thenComparing和comparing的参数相同,都为(Function keyExtractor)和(Function keyExtractor,Comparator keyComparator),其中java.util.function.Function为java8引入的函数式接口(这里不讨论自定义比较器),而第二个参数为排序方式,java8的Comparator有提供多种排序方式。

    方法名含义
    reverseOrder自然顺序倒叙排序
    naturalOrder自然顺序正序排序
    nullsFirst自然顺序排序,如果有null,则null在最前(如果上面案例用name排序,且有人name为null,则上面的自然排序会报错)
    nullsLast自然顺序排序,如果有null,则null在最后(与nullsFirst类似)

    对于reverseOrder方法,有另一种写法,如:Comparator.comparing(TestInfo::getLastScore, Comparator.reverseOrder())还可以写为Comparator.comparing(TestInfo::getLastScore).reversed()。

    展开全文
  • 目录1 数据格式转换2 数据的排序2.1 单个表格列数据的排序需要用到函数:2.2 多排序问题:3 基本统计数据分析3.1 描述性统计3.2 列数据统计(最值/方差等)4数据透视操作和函数 1 数据格式转换 查看与转换表格某一列...
  • 成绩排序(牛客网): 链接:https://www.nowcoder.com/questionTerminal/0383714a1bb749499050d2e0610418b1 题目: 语法:python3 思路: 1、用结构体构造一个User类 2、写2个比较器:升序、降序 3、读取...
  • 字节跳动01全球员工总数字节的员工数量目前超过5万人图片来源:字节范02岗位职级字节跳动的职级研发序列一共10级:字节跳动创始人一鸣是5-1级,应届生一般是1-1级,中级工程师是1-2级,2-2属于资深研发人才,3-1属于...
  • 定义一个Teacher结构体包括:姓名,性别,年龄,员工编号,评分 2.定义一个对结构体数组排序的函数,包括一个返回值为BOOl类型的回调函数,实现按照不同的条件对结构体成员排序 3.定义一个打印所有老师信息的函数 ...
  • 针对评价非常不满意的员工,为什么上次评分的中值为0.81,而此次评分为非常不满意? 以下就评价为非常不满的员工的数据进行分析。 选择上次评分为满意和非常满意,而此次评分为非常不满意的数据。 相关数据显示 / ...
  • 我们的搜索排序机器学习模型的前三个阶段 主要的结论是,基于机器学习的搜索排序在每个阶段都有效,因为我们选择的模型和基础设施的复杂度水平与可用的数据量和需要排序的库存大小相匹配。当使用少量数据进行训练时...
  • python信用评分卡建模(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_campaign=commission&utm_source=cp-400000000398149&utm_medium=share ...
  • CDA数据分析师 出品【导读】今天教大家如何用Python写一个员工流失预测模型。获取数据代码:扫描下方公众号CDA数据分析师回复关键字“离职”说道离职的原因,可谓多种多样。人们...
  • 企业进行人力资源管理时,往往付出了大量的资源与成本后仍然呈现员工高流失率,尤其对于中小企业影响更大,员工信用管理困难依旧是企业难以解决的问题。征信模型的缺失与不确定性是导致技术手段的筛选在人力管理中受...
  • 比如,有A、B、C三个员工竞选某要职,HR组织吃瓜群众位对其三位进行评分,出于不清楚的原因,HR在拿到群众们的评分结果后,会先对某侯选人的得分结果记录集进行排序,再按比例去掉记录两头(最高、最低)的N%评分...
  • GBDT模型用于评分卡模型 本文主要总结以下内容: GBDT模型基本理论介绍 GBDT模型如何调参数 GBDT模型对样本违约概率进行估计(GBDT模型用于评分卡python代码实现请看下一篇博客) GBDT模型挑选变量重要性 GBDT...
  • 4、绩效考核 考核内容:业绩和价值观各占50% 考核频次:季度考核为主 部门排序:2-7-1排序 个人排序:3-6-1排序 考核工具:KPI 主导 淘汰标准:连续两个季度,成为末尾10% 晋升条件:上年度 KPI 达3.75 评价形式:...
  • 评分值 12 private boolean ascDesc; // true升序/false降序 13 private String whichProperty; // 需要对哪个分值排序 14 15 16 17 18 19 public String ...
  • 在Teacher.h中,定义一个Teacher结构体,成员变量包括:姓名、性别、年龄、员工编号、评分。2分 声明一个打印Teacher的函数,void printTeacher(Teacher * teacher)。1分 实现部分 4分 定义一个函数指针类型,...
  • 绩效评分标准,分为六档,分别是: 5 晋升条件 晋升资格:上年度KPI达3.75 主管提名:一般KPI不达3.75主管不会提名 晋升委员会面试:晋升委员会组成一般是合作方业务部门大佬、HRG、该业务线大佬等 晋升委员会投票 ...
  • 评分卡打分公式by Hayley D’Auria 通过海莉·德奥里亚(Hayley D'Auria) 如何为您真正喜欢的设计工作打分 (How To Score A Design Job You’ll Actually Love) 我希望有人告诉我关于面试的8件事 (8 Things I Wish...
  • 今天教大家如何用Python写一个员工流失预测模型。公众号后台,回复关键字“离职”获取完整数据。Show me data,用数据说话。 今天我们聊一聊 员工离职,说到离职的原因,可谓多种多样。人们归总了两点: 1. 钱没给...
  • Show me data,用数据说话 今天我们聊一聊 员工离职 ... 说道离职的原因,可谓多种多样。...这不仅仅是公司评估员工流动率的过程,通过找到导致员工流失的主要因素,预测未来的员工离职状况,从而进..
  • 遍历vector容器,取出来每一个选手,执行for循环,可以把10个评分打分存到deque容器中 sort算法对deque容器中分数排序,去除最高和最低分 deque容器遍历一遍,累加总分 获取平均分 #include <...
  • C-教师比较排序。。

    2015-05-23 14:55:20
    //比较员工评分 BOOL compareScoreByAscending(Teacher teacher1,Teacher teacher2); BOOL compareScoreByDescending(Teacher teacher1,Teacher teacher2); //输出全部男老师 void printMaleTeacher(Teacher *...
  • 最近公司有这样一个需求:如果有10万奖金,要分摊给员工,按每个人的实际能力分摊,要求不能有剩余。看到网上并没有相关的帖子,于是借此记录下我的解题思路。 一开始我的做法是用10w*(每个人的工资/全部人工资之...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,954
精华内容 781
关键字:

员工评分排序