当前位置: 首页 > news >正文

sklearn-逻辑回归-制作评分卡

目录

数据集处理

分箱

分多少个箱子合适

分箱要达成什么样的效果

对一个特征进行分箱的步骤

分箱的实现

封装计算 WOE 值和 IV值函数

画IV曲线,判断最佳分箱数量

结论

pd.qcut 执行报错

功能函数封装

判断分箱个数


在银行借贷场景中,评分卡是一种以分数形式来衡量一个客户的信用风险大小的手段,它衡量向别人借钱的人(受信人,需要融资的公司)不能如期履行合同中的还本付息责任,并让借钱给别人的人(授信人,银行)造成经济损失的可能性。一般来说,评分卡打出的分值越高,客户的信用越好,风险越小。

数据集处理

分箱


要制作评分卡,是要给各个特征进行分档,以便业务人员能够根据新客户填写的信息,为这个新客户来打分。因此在评分卡制作过程中,一个重要的步骤就是分箱,本质就是对特征进行分档。

分箱是评分卡最难,也是最核心的部分。分箱的本质,就是离散化连续变量,好让拥有不同属性的人被分成不同的类别(打上不同的分数)。

分多少个箱子合适

既然是将连续型变量离散化,箱子的个数必然不能太多,最好控制在十个以下,用来制作评分卡,最好能在4~5个为最佳。离散化连续变量必然伴随着信息的损失,而且箱子越少,信息损失越大。
为了衡量特征上的信息量以及特征对预测函数的贡献,银行业定义了概念Information value(IV):
 

  •  N 是这个特征上箱子的个数
  •  i 代表每个箱子
  •  good% 是这个箱内的优质客户(标签为0)占整个特征中所有优质客户的比例
  •  bad% 是这个箱子里的坏客户(那些会违约的,标签为1)占整个特征中所有坏客户的比例
  •  WOE 是银行业中用来衡量违约概率的指标,中文叫做证据权重(weight of Evidence),本质就是优质客户比上坏客户的比例的对数,WOEi写作 

WOE是对一个箱子来说的,WOE越大,代表这个箱子里的优质客户越多,IV是对整个特征来说的,IV代表的意义由 表1 来控制

表1:

可见,IV 并非越大越好,我们想要找到 IV 的大小和箱子个数的平衡点,所以我们会对特征进行分箱,然后计算每个特征在每个箱子数目下的WOE值,利用IV值的曲线,找出合适的分箱个数。

分箱要达成什么样的效果

我们希望在同一个箱子里的人的属性是尽量相似的,而不同箱子里的人的属性是尽量不同的,就是常说的“组间差异大,组内差异小”。
对于评分卡来说,我们希望一个箱子内的人违约概率是类似的,而不同箱子的人违约概率差距很大,即 WOE 差距要大,并且每个箱子中坏客户所占的比重(bad%)也要不同。
我们可以使用卡方检验来对比两个箱子之间的相似性,如果两个箱子之间卡方检验的P值很大,说明他们非常相似,就可以将这两个箱子合并为一个箱子。

对一个特征进行分箱的步骤

  1. 首先把连续型变量分成一组数量较多的分类型变量,比如,将几万个样本分成100组或者50组
  2. 确保每一组中都要包含两种类别的样本,否则IV值会无法计算
  3. 对相邻的组进行卡方检验,卡方检验的P值很大的组进行合并,直到数据中的组数小于设定的N箱为止
  4. 我们让一个特征分别分成[2,3,4...20]箱,观察每个分箱个数下的IV值如何变化,找出最适合的分箱个数
  5. 分箱完毕后,我们计算每个箱的WOE值,bad%,观察分箱效果

这些步骤都完成后,我们可以对各个特征都进行分箱,然后观察每个特征的IV值,以此来挑选特征。

分箱的实现

封装计算 WOE 值和 IV值函数

# 计算 WOE 和 BAD RATE
# BAD RATE 是一个箱中,坏的样本所占的比例
# bad% 是一个箱中的坏样本占整个特征中的坏样本的比例def get_woe(num_bins):# 通过 num_bins 数据计算 woecolumns = ["min", "max", "count_0", "count_1"]dataf = pd.DataFrame(num_bins, columns=columns)# 一个箱子中所有的样本数dataf["total"] = dataf.count_0 + dataf.count_1# 一个箱子里的样本数,占所有样本数的比例dataf["percentage"] = dataf.total / dataf.total.sum()dataf["bad_rate"] = dataf.count_1 / dataf.totaldataf["good%"] = dataf.count_0 / dataf.count_0.sum()dataf["bad%"] = dataf.count_1/dataf.count_0.sum()dataf["woe"] = np.log(dataf["good%"] / dataf["bad%"])return dataf# 计算 IV 值
def get_iv(bins_df):rate = bins_df["good%"] - bins_df["bad%"]iv = np.sum(rate * bins_df.woe)return iv

画IV曲线,判断最佳分箱数量

# 导入数据
df = mlp.get_data_source("data8770d35cf63711ee80850242ac850002", return_type="dataframe")
# 删除不用于逻辑回归的列,只有数字类型的列才参与逻辑回归
# axis=1为删除列,第一个参数则输入表头的列表
# axis=0为删除行,第一个参数则输入索引的列表
df.drop(["apply_no", "pt", "query_no", "request_hash"], inplace=True, axis=1)
# 通过查看标签值的分布,发现1的个数只有80个,占比只有1%左右,属于严重不均衡的。如 图1 所示
print("标签值y分布情况:\n{}".format(y.value_counts()))
print("标签值y取1的占比情况:\n{}".format(y.value_counts()[1]/X.shape[0]))# 查看数据分布情况,图2 所示
df.describe([0.01,0.1,0.25,.5,.75,.9,.99]).TX = pd.DataFrame(X)
y = pd.DataFrame(y)# 分训练集和测试集,训练集用来建模,验证数据用来检测模型的效果的
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size = 0.3, random_state=420)
# 逻辑回归做评分卡时,建模数据需要将训练集的特征矩阵和标签要合并在一起
# 因为分箱的时候,需要的是特征矩阵+标签的结构
model_data = pd.concat([Ytrain, Xtrain], axis=1)
# 更新索引
model_data.index = range(model_data.shape[0])# 按照等频对需要分箱的列进行分箱,保证每个箱子中好的样本数量和坏的样本数据都 > 0,如果有0,需要减少分箱数量
# "sq_model_score_high" 为例
model_data["qcut"], updown = pd.qcut(model_data["sq_model_score_high"], retbins=True, q=10, duplicates='drop')"""
pd.qcut,基于分位数的分箱函数,本质是将连续型变量离散化,只能够处理一维数据
返回箱子的上限和下限
参数q: 要分箱的个数
参数 retbins=True来要求除了返回箱子的上 限和下限,同时返回一个为索引为样本的索引、元素为分到的箱子的Series的结构
现在返回两个值:每个样本属于哪个箱子(赋值给model_data["qcut"]列),以及所有箱子的上限和下限(赋值给updown变量)
"""
# 查看分箱情况,如 图3 所示
print("分箱结束后,每个箱子的元素个数:\n{}".format(model_data["qcut"].value_counts()))# 统计每个分段 0, 1 的数,结果如 图4 所示
# 这里使用了数据透视表的功能 groupby
count_y0 = model_data[model_data["y"] == 0].groupby(by="qcut").count()["y"]
count_y1 = model_data[model_data["y"] == 1].groupby(by="qcut").count()["y"]print("对列 sq_model_score_high 进行分箱后,每个箱子对应的0的数量为:\n{}".format(count_y0))
print("对列 sq_model_score_high 进行分箱后,每个箱子对应的1的数量为: \n{}".format(count_y1))# num_bins 值分别为每个区间的上界、下届,0 出现的次数,1 出现的次数
num_bins = [*zip(updown, updown[1:], count_y0, count_y1)]# 卡方检验num_bins_ = num_bins.copy()
import matplotlib.pyplot as plt
import scipy
IV = []
axisx = []
# 定义箱的数量,这里为2
N = 2while len(num_bins_) > N:pvs = []# 获取 num_bins_两两之间的卡方检验的置信度(卡方值)for i in range(len(num_bins_)-1):x1 = num_bins_[i][2:]x2 = num_bins_[i+1][2:]# scipy.stats.chi2_contingency()0索引返回 chi2 值,1索引返回 p 值pv = scipy.stats.chi2_contingency([x1, x2])[1]pvs.append(pv)# 通过 p 值进行处理,合并 p 值最大的两组# 找出 p 值最大的那一组所在的下标 i,num_bins_的[i]和[i+1]就是可以合并的组i = pvs.index(max(pvs))# 具体合并第i个箱子和i+1个箱子操作,把 num_bins_的[i:i+2]左闭右开换成新列表[()],新列表元素内容按照新方式计算# 1、第0个元素取第i个箱子的第0个元素,前一个箱子的下限# 2、第1个元素取第i+1个箱子的第1个元素,后一个箱子的上限# 3、第2个元素取第i个箱子的第2个元素和第i+1个箱子的第2个元素加和# 4、第3个元素取第i个箱子的第3个元素和第i+1个箱子的第3个元素加和num_bins_[i:i+2] = [(num_bins_[i][0],num_bins_[i+1][1],num_bins_[i][2]+num_bins_[i+1][2],num_bins_[i][3]+num_bins_[i+1][3])]bins_df = get_woe(num_bins_)# 记录箱子数axisx.append(len(num_bins_))# 记录对应的IV值IV.append(get_iv(bins_df))plt.figure()
plt.plot(axisx, IV)
plt.xticks(axisx)
# y 坐标为 IV值
plt.ylabel("IV")
# x 坐标为箱子数量
plt.xlabel("N")
# 画出 IV 值随着箱子数量的变化而变化图,如 图5 所示
plt.show()

图1:

图2:

图3:

图4:

图5:

结论

如 图5 所示,分箱数量越多,IV值越高,在这条线中,寻找随着箱子数量减少,IV值下降最快的那个点,这个转折点对应的箱子数量就是我们要找的相对合适的箱子数量。
这样,就这出了特征 sq_model_score_high 的最佳分箱个数

pd.qcut 执行报错

图6:

如 图6 所示,运行 pd.qcut()函数的时候,如果出现"ValueError: Bin edges must be unique"的错误,这通常意味着在尝试对数据进行分箱时,边界值出现了重复。这可能会导致qcut函数无法确定如何对数据进行分箱,因此需要进行调整以确保边界值唯一。
为了解决这个问题,可以在调用qcut()函数时,传入duplicates='drop'参数来指定处理重复边界值的方式,选择将重复的边界值删除,这可能会导致最终分箱后的箱的数量减少,以及数据条数减少;或者使用cut()函数,pd.cut()函数是根据值本身来确定分箱的边界,因此可以处理重复的边界值,并将它们归入相邻的箱中

model_data["cut"], updown = pd.cut(model_data["sq_model_score_high"], retbins=True, bins=20)

功能函数封装

判断分箱个数
 

def graph_for_best_bin(DF, X, Y, N=5, q=20, graph=True):"""自动优化分箱函数,基于卡方验证的分箱DF:需要输入的数据X:需要分箱的列名Y:分箱数据对应的标签 Y 列名N:保留分箱个数q: 初始分箱的个数graph:是否要画出IV图像区间为前开后闭(]"""import matplotlib.pyplot as pltimport scipymodel_data = DF[[X,Y]].copy()model_data["qcut"], updown = pd.qcut(model_data[X], retbins=True, q=q, duplicates='drop')count_y0 = model_data.loc[model_data[Y] == 0].groupby(by="qcut").count()[Y]count_y1 = model_data.loc[model_data[Y] == 1].groupby(by="qcut").count()[Y]num_bins_ = [*zip(updown, updown[1:], count_y0, count_y1)]for i in range(q):if 0 in num_bins_[0][2:]:num_bins_[0:2] = [(num_bins_[0][0],num_bins_[1][1],num_bins_[0][2]+num_bins_[1][2],num_bins_[0][3]+num_bins_[1][3])]for i in range(len(num_bins_)):if 0 in num_bins_[i][2:]:num_bins_[i-1:i+1] = [(num_bins_[i-1][0],num_bins_[i][1],num_bins_[i-1][2]+num_bins_[i][2],num_bins_[i-1][3]+num_bins_[i][3])]breakelse:breakdef get_woe(num_bins):columns = ["min", "max", "count_0", "count_1"]dataf = pd.DataFrame(num_bins, columns=columns)dataf["total"] = dataf.count_0 + dataf.count_1dataf["percentage"] = dataf.total / dataf.total.sum()dataf["bad_rate"] = dataf.count_1 / dataf.totaldataf["good%"] = dataf.count_0 / dataf.count_0.sum()dataf["bad%"] = dataf.count_1/dataf.count_1.sum()dataf["woe"] = np.log(dataf["good%"] / dataf["bad%"])return dataf# 计算 IV 值def get_iv(bins_df):rate = bins_df["good%"] - bins_df["bad%"]iv = np.sum(rate * bins_df.woe)return ivIV = []axisx = []while len(num_bins_) > N:pvs = []for i in range(len(num_bins_)-1):x1 = num_bins_[i][2:]x2 = num_bins_[i+1][2:]pv = scipy.stats.chi2_contingency([x1, x2])[1]pvs.append(pv)i = pvs.index(max(pvs))num_bins_[i:i+2] = [(num_bins_[i][0],num_bins_[i+1][1],num_bins_[i][2]+num_bins_[i+1][2],num_bins_[i][3]+num_bins_[i+1][3])]bins_df = get_woe(num_bins_)# 记录箱子数axisx.append(len(num_bins_))# 记录对应的IV值IV.append(get_iv(bins_df))if graph:plt.figure()plt.plot(axisx, IV)plt.xticks(axisx)plt.ylabel("IV")plt.xlabel("N")plt.show()return bins_df

相关文章:

sklearn-逻辑回归-制作评分卡

目录 数据集处理 分箱 分多少个箱子合适 分箱要达成什么样的效果 对一个特征进行分箱的步骤 分箱的实现 封装计算 WOE 值和 IV值函数 画IV曲线,判断最佳分箱数量 结论 pd.qcut 执行报错 功能函数封装 判断分箱个数 在银行借贷场景中,评分卡是…...

scrapy爬取图片

scrapy 爬取图片 环境准备 python3.10scrapy pillowpycharm 简要介绍scrapy Scrapy 是一个开源的 Python 爬虫框架,专为爬取网页数据和进行 Web 抓取而设计。它的主要特点包括: 高效的抓取性能:Scrapy 采用了异步机制,能够高效…...

在 Vue 项目中使用地区级联选

在 Vue 项目中使用地区级联选择的完整流程: 1.安装依赖包,这个包提供了中国省市区的完整数据。 npm install element-china-area-data --save 2.导入数据 import { regionData } from element-china-area-data 这个包提供了几种不同的数据格式&#…...

【简博士统计学习方法】第1章:1. 统计学习的定义与分类

自用笔记 1. 统计学习的定义与分类 1.1 统计学习的概念 统计学习(Statistical Machine Learning)是关于计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析的一门学科。 以计算机和网络为平台;以数据为研究对象;以…...

利用 Python 脚本批量创建空白 Markdown 笔记

文章目录 利用 Python 脚本批量创建空白 Markdown 笔记1 背景介绍2 需求描述3 明确思路4 具体实现4.1. 遍历 toc.md 文件,收集文件名和对应的文件内容4.2. 实现文件批量生成逻辑4.3. 补全缺失的工具函数4.4. 进一步补全工具函数中的工具函数 5 脚本运行6 注意事项 利…...

【Qt】C++11 Lambda表达式

1. 举例 connect(ui->pushButton, &QPushButton::clicked, [](bool checked){//具体代码qDebug() << "Hello" << checked;}); 2. 详情 //完整形式 [ capture ] ( params ) opt -> ret { body; }; capture 是捕获列表params 是参数表opt 是函数…...

怎样提高服务器中的数据传输速度?

服务器中的数据传输速度会影响着用户的体验感&#xff0c;当企业中的数据传输速度出现卡顿或者是过慢时&#xff0c;用户不能及时浏览到所需的内容&#xff0c;给用户造成不好的体验感&#xff0c;那么企业该怎样才能提高服务器中的数据传输速度呢&#xff1f; 服务器之间如何传…...

Vue 封装公告滚动

文章目录 需求分析1. 创建公告组件Notice.vue2. 注册全局组件3. 使用 需求 系统中需要有一个公告展示&#xff0c;且这个公告位于页面上方&#xff0c;每个页面都要看到 分析 1. 创建公告组件Notice.vue 第一种 在你的项目的合适组件目录下&#xff08;比如components目录&a…...

JVM实战—12.OOM的定位和解决

大纲 1.如何对系统的OOM异常进行监控和报警 2.如何在JVM内存溢出时自动dump内存快照 3.Metaspace区域内存溢出时应如何解决(OutOfMemoryError: Metaspace) 4.JVM栈内存溢出时应如何解决(StackOverflowError) 5.JVM堆内存溢出时应该如何解决(OutOfMemoryError: Java heap s…...

【python翻译软件V1.0】

如果不想使用密钥的形式&#xff0c;且需要一个直接可用的中英文翻译功能&#xff0c;可以使用一些免费的公共 API&#xff0c;如 opencc 或其他无需密钥的库&#xff0c;或直接用 requests 获取翻译结果。 其中&#xff0c;我可以给你一个简单的代码示例&#xff0c;使用 tra…...

Spring Boot中的依赖注入是如何工作

Spring Boot 中的依赖注入&#xff08;Dependency Injection&#xff0c;简称 DI&#xff09;是通过 Spring 框架的核心机制——控制反转&#xff08;Inversion of Control&#xff0c;IOC&#xff09;容器来实现的。Spring Boot 基于 Spring Framework&#xff0c;在应用中自动…...

ubuntu22.04 编译安装libvirt 10.x

环境安装 sudo apt-get update -y sudo apt-get install qemu-system-x86 bridge-utils libyajl-dev -y sudo apt-get install build-essential autoconf automake libtool -y sudo apt-get install libxml2-dev libxslt1-dev libgnutls28-dev libpciaccess-dev libnl-3-de…...

[fastadmin] 第三十四篇 FastAdmin 商城模块标签使用详解

FastAdmin 商城模块标签使用详解 一、标签基本语法 1.1 基础语法格式 {shop:goodslist flag"参数值" id"变量名" row"数量"}<!-- 循环内容 --> {/shop:goodslist}1.2 常用参数说明 flag: 商品标记筛选id: 循环变量名row: 显示数量 1.…...

(2024,LLaVA-Bench (Wilder),LLaVA-NeXT,LLaMA3,Qwen-1.5,语言模型扩展)

LLaVA-NeXT: Stronger LLMs Supercharge Multimodal Capabilities in the Wild 目录 1. 简介 2. 探索大规模语言模型的能力极限 3. LLaVA-Bench (Wilder)&#xff1a;日常生活视觉聊天基准 4. Benchmark 结果 1. 简介 我们通过引入近期更强大的开源大语言模型&#xff08;…...

IPEX-LLM开发项目过程中的技术总结和心得

IPEX-LLM开发项目过程中的技术总结和心得 在人工智能快速发展的时代&#xff0c;高效地开发和部署大语言模型&#xff08;LLM&#xff09;已成为技术人员的必备技能。在我们的项目中&#xff0c;我们采用了 Intel Extension for PyTorch&#xff08;简称 IPEX&#xff09;和 L…...

HTTP/HTTPS ②-Cookie || Session || HTTP报头

这里是Themberfue 上篇文章介绍了HTTP报头的首行信息 本篇我们将更进一步讲解HTTP报头键值对的含义~~~ ❤️❤️❤️❤️ 报头Header ✨再上一篇的学习中&#xff0c;我们了解了HTTP的报头主要是通过键值对的结构存储和表达信息的&#xff1b;我们已经了解了首行的HTTP方法和UR…...

【软考】软件设计师

「学习路线」&#xff08;推荐该顺序学习&#xff0c;按照先易后难排序&#xff09; 1、上午题—计算机系统&#xff08;5~6分&#xff09;[1.8; ] 2、上午题—程序设计语言&#xff08;固定6分&#xff09;[1.9; ] 3、下午题—试题一&#xff08;15分&#xff09; 4、上午题—…...

K8s Pod OOMKilled,监控却显示内存资源并未打满

1. 问题现象 pod一直重启&#xff0c;通过grafana查看&#xff0c;发现内存使用率并没有100%。 2. 排查过程 2.1 describe查看pod最新一次的状态 可以明显看到&#xff0c;最近一次的重启就是因为内存不足导致的。 2.2 describe 查看node节点状态 找到原因了&#xff0c;原来…...

C++ 原子变量

C 原子变量 文章目录 C 原子变量1. 原子变量是什么&#xff1f;2. 原子操作的特点3. 原子变量的作用1. 多线程安全的共享数据访问2. 替代锁机制3. 实现低级同步算法 4. 原子变量的常见操作5. 内存顺序&#xff08;Memory Ordering&#xff09;内存顺序控制在原子变量中的作用如…...

linux网络 | http结尾、理解长连接短链接与cookie

前言&#xff1a;本节是http章节的最后一部分&#xff0c;主要解释一些小概念。讲解到了HTTP的方法&#xff0c;表单&#xff0c; 重定向等等。 现在废话不多说&#xff0c; 开始我们的学习吧。 ps&#xff1a;本节内容都是概念&#xff0c; 知道就行&#xff0c; 友友们放心观…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...

欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!

多连接 BLE 怎么设计服务不会乱&#xff1f;分层思维来救场&#xff01; 作者按&#xff1a; 你是不是也遇到过 BLE 多连接时&#xff0c;调试现场像网吧“掉线风暴”&#xff1f; 温度传感器连上了&#xff0c;心率带丢了&#xff1b;一边 OTA 更新&#xff0c;一边通知卡壳。…...

【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项

一、条形码识别改名使用教程 打开软件并选择处理模式&#xff1a;打开软件后&#xff0c;根据要处理的文件类型&#xff0c;选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件&#xff0c;就选择 “PDF 识别模式”&#xff1b;若是处理图片文件&…...