GitHub 标星 7.4k!Python 魔法库之 FuzzyWuzzy

来源:Be_melting

blog.csdn.net/lys_828/article/details/106489371

【导语】:还在为日常工作中不同的数据集的字段进行匹配烦恼?今天跟大家分享FuzzyWuzzy一个简单易用的模糊字符串匹配工具包。让你多快好省的解决烦恼的匹配问题!

1. 前言

在处理数据的过程中,难免会遇到下面类似的场景,自己手里头获得的是简化版的数据字段,但是要比对的或者要合并的却是完整版的数据(有时候也会反过来)

最常见的一个例子就是:在进行地理可视化中,自己收集的数据只保留的缩写,比如北京,广西,新疆,西藏等,但是待匹配的字段数据却是北京市,广西壮族自治区,新疆维吾尔自治区,西藏自治区等,如下。因此就需要有没有一种方式可以很快速便捷的直接进行对应字段的匹配并将结果单独生成一列,就可以用到FuzzyWuzzy库。

GitHub 标星 7.4k!Python 魔法库之 FuzzyWuzzy

2. FuzzyWuzzy库介绍

FuzzyWuzzy 是一个简单易用的模糊字符串匹配工具包。它依据 Levenshtein Distance 算法,计算两个序列之间的差异。

Levenshtein Distance算法,又叫 Edit Distance算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。一般来说,编辑距离越小,两个串的相似度越大。

这里使用的是Anaconda下的jupyter notebook编程环境,因此在Anaconda的命令行中输入一下指令进行第三方库安装。

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple FuzzyWuzzy

2.1 fuzz模块

该模块下主要介绍四个函数(方法),分别为:简单匹配(Ratio)、非完全匹配(Partial Ratio)、忽略顺序匹配(Token Sort Ratio)和去重子集匹配(Token Set Ratio)

注意: 如果直接导入这个模块的话,系统会提示warning,当然这不代表报错,程序依旧可以运行(使用的默认算法,执行速度较慢),可以按照系统的提示安装python-Levenshtein库进行辅助,这有利于提高计算的速度。

GitHub 标星 7.4k!Python 魔法库之 FuzzyWuzzy

2.1.1 简单匹配(Ratio)

简单的了解一下就行,这个不怎么精确,也不常用

fuzz.ratio("河南省", "河南省")>>> 100>fuzz.ratio("河南", "河南省")>>> 80

2.1.2 非完全匹配(Partial Ratio)

尽量使用非完全匹配,精度较高

fuzz.partial_ratio("河南省", "河南省")>>> 100
fuzz.partial_ratio("河南", "河南省")>>> 100

2.1.3 忽略顺序匹配(Token Sort Ratio)

原理在于:以 空格 为分隔符,小写 化所有字母,无视空格外的其它标点符号

fuzz.ratio("西藏 自治区", "自治区 西藏")>>> 50fuzz.ratio('I love YOU','YOU LOVE I')>>> 30
fuzz.token_sort_ratio("西藏 自治区", "自治区 西藏") >>> 100fuzz.token_sort_ratio('I love YOU','YOU LOVE I') >>> 100

2.1.4 去重子集匹配(Token Set Ratio)

相当于比对之前有一个集合去重的过程,注意最后两个,可理解为该方法是在token_sort_ratio方法的基础上添加了集合去重的功能,下面三个匹配的都是倒序

fuzz.ratio("西藏 西藏 自治区", "自治区 西藏")>>> 40
fuzz.token_sort_ratio("西藏 西藏 自治区", "自治区 西藏")>>> 80
fuzz.token_set_ratio("西藏 西藏 自治区", "自治区 西藏")>>100

fuzz这几个ratio()函数(方法)最后得到的结果都是数字,如果需要获得匹配度最高的字符串结果,还需要依旧自己的数据类型选择不同的函数,然后再进行结果提取,如果但看文本数据的匹配程度使用这种方式是可以量化的,但是对于我们要提取匹配的结果来说就不是很方便了,因此就有了process模块。

2.2 process模块

用于处理备选答案有限的情况,返回模糊匹配的字符串和相似度。

2.2.1 extract提取多条数据

类似于爬虫中select,返回的是列表,其中会包含很多匹配的数据

choices = ["河南省", "郑州市", "湖北省", "武汉市"]process.extract("郑州", choices, limit=2)>>> [('郑州市', 90), ('河南省', 0)]# extract之后的数据类型是列表,即使limit=1,最后还是列表,注意和下面extractOne的区别

2.2.2 extractOne提取一条数据

如果要提取匹配度最大的结果,可以使用extractOne,注意这里返回的是 元组 类型, 还有就是匹配度最大的结果不一定是我们想要的数据,可以通过下面的示例和两个实战应用体会一下

process.extractOne("郑州", choices)>>> ('郑州市', 90)
process.extractOne("北京", choices)>>> ('湖北省'45)

3. 实战应用

这里举两个实战应用的小例子,第一个是公司名称字段的模糊匹配,第二个是省市字段的模糊匹配

3.1 公司名称字段模糊匹配

数据及待匹配的数据样式如下:自己获取到的数据字段的名称很简洁,并不是公司的全称,因此需要进行两个字段的合并GitHub 标星 7.4k!Python 魔法库之 FuzzyWuzzy

直接将代码封装为函数,主要是为了方便日后的调用,这里参数设置的比较详细,执行结果如下:GitHub 标星 7.4k!Python 魔法库之 FuzzyWuzzy

3.1.1 参数讲解:

① 第一个参数df_1是自己获取的欲合并的左侧数据(这里是data变量);

② 第二个参数df_2是待匹配的欲合并的右侧数据(这里是company变量);

③ 第三个参数key1是df_1中要处理的字段名称(这里是data变量里的‘公司名称’字段)

④ 第四个参数key2是df_2中要匹配的字段名称(这里是company变量里的‘公司名称’字段)

⑤ 第五个参数threshold是设定提取结果匹配度的标准。注意这里就是对extractOne方法的完善,提取到的最大匹配度的结果并不一定是我们需要的,所以需要设定一个阈值来评判,这个值就为90,只有是大于等于90,这个匹配结果我们才可以接受

⑥ 第六个参数,默认参数就是只返回两个匹配成功的结果

⑦ 返回值:为df_1添加‘matches’字段后的新的DataFrame数据

3.1.2 核心代码讲解

第一部分代码如下,可以参考上面讲解process.extract方法,这里就是直接使用,所以返回的结果m就是列表中嵌套元祖的数据格式,样式为: [(‘郑州市’, 90), (‘河南省’, 0)],因此第一次写入到’matches’字段中的数据也就是这种格式

注意,注意: 元祖中的第一个是匹配成功的字符串,第二个就是设置的threshold参数比对的数字对象

s = df_2[key2].tolist()m = df_1[key1].apply(lambda x: process.extract(x, s, limit=limit))    df_1['matches'] = m

第二部分的核心代码如下,有了上面的梳理,明确了‘matches’字段中的数据类型,然后就是进行数据的提取了,需要处理的部分有两点需要注意的:

① 提取匹配成功的字符串,并对阈值小于90的数据填充空值

② 最后把数据添加到‘matches’字段

m2 = df_1['matches'].apply(lambda x: [i[0] for i in x if i[1] >= threshold][0] if len([i[0] for i in x if i[1] >= threshold]) > 0 else '')#要理解第一个‘matches’字段返回的数据类型是什么样子的,就不难理解这行代码了#参考一下这个格式:[('郑州市', 90), ('河南省', 0)]df_1['matches'] = m2
return df_1

3.2 省份字段模糊匹配

自己的数据和待匹配的数据背景介绍中已经有图片显示了,上面也已经封装了模糊匹配的函数,这里直接调用上面的函数,输入相应的参数即可,代码以及执行结果如下:

GitHub 标星 7.4k!Python 魔法库之 FuzzyWuzzy

数据处理完成,经过封装后的函数可以直接放在自己自定义的模块名文件下面,以后可以方便直接导入函数名即可,可以参考将自定义常用的一些函数封装成可以直接调用的模块方法。

4. 全部函数代码

#模糊匹配
def fuzzy_merge(df_1, df_2, key1, key2, threshold=90, limit=2):    """    :param df_1: the left table to join    :param df_2: the right table to join    :param key1: key column of the left table    :param key2: key column of the right table    :param threshold: how close the matches should be to return a match, based on Levenshtein distance    :param limit: the amount of matches that will get returned, these are sorted high to low    :return: dataframe with boths keys and matches    """    s = df_2[key2].tolist()
    m = df_1[key1].apply(lambda x: process.extract(x, s, limit=limit))        df_1['matches'] = m
    m2 = df_1['matches'].apply(lambda x: [i[0] for i in x if i[1] >= threshold][0] if len([i[0] for i in x if i[1] >= threshold]) > 0 else '')    df_1['matches'] = m2
    return df_1    from fuzzywuzzy import fuzzfrom fuzzywuzzy import process
df = fuzzy_merge(data, company, '公司名称', '公司名称', threshold=90)df

转自:https://mp.weixin.qq.com/s/NU7cHq0nMDzcRHkjI2eEZg

节省一半键入量,实测Python代码补全插件aiXcoder

代码补全在开发过程中是非常必要的能力,我们最常用的是 IDE 自带的代码补全功能,但问题在于,IDE 并不能理解我们的代码,推荐总是不那么准确。

今天笔者介绍一个国产代码补全神器 aiXcoder,它现在的离线版已经支持 Java 和 Python 了。对比 PyCharm 原生代码补全体验,它的效果实在是太好了。

节省一半键入量,实测Python代码补全插件aiXcoder

图注:aiXcoder 代码补全实测,整行代码预测用起来特别炫酷,整体上比 IDE 的预测准很多

PyCharm IDE 对比 aiXcoder

如果装上 aiXcoder,PyCharm 上的补全效果提升还是挺明显的,但是为了更直观地展示出来,我们可以统计编写同一段代码,敲键数到底有什么不同。

就下面这一段代码,笔者用 PyCharm IDE 的原生推荐,一共敲击了 96 个字符,其中字符串还没计算在内。

节省一半键入量,实测Python代码补全插件aiXcoder

如果安装上了 aiXcoder,效果就像文章前面的动图,总共只需要敲击 36个字符(字符串不计入),效果提升特别明显。

至于为什么能节省这么多功夫,看看aiXcoder下面两个预测就知道了。

一次性将变量名、API、参数传入都帮我们写好了,直接预测整行代码:

节省一半键入量,实测Python代码补全插件aiXcoder

整个列表推导式的内部逻辑,一次性都预测好了,只要填上参数:

节省一半键入量,实测Python代码补全插件aiXcoder

本地补全引擎:确保代码安全

之前笔者也用过代码补全插件,但是它们大部分都要上传代码到服务器,这就比较劝退了。不过 aiXcoder最新发布的 2.5 本地版,完全在本地计算机上运行,不会上传任何代码。

为了验证是不是全部计算都在本地完成,我特意关闭了网络连接,看看它对同一段代码的预测是不是正常,结果是,关不关网络连接,预测都是一样的。

另外,在笔记本上用,我还是比较在意性能的,在测试插件的时候,特别关注了它消耗的计算资源。打开进程管理器,一边写一边观察 CPU 占比。基本键入代码的过程中 CPU 占比在10%吧,停止键入很快就降到0了。

 

节省一半键入量,实测Python代码补全插件aiXcoder

虽然代码预测都放到本地了,但是 aiXcoder提供了下面这种滚动条,来平衡「补全能力」和「计算性能」之间的关系。试了一下,越靠近「Faster」,表示会尽快返回预测结果;越靠近「Longer」,表示插件将反馈更好的预测结果。

节省一半键入量,实测Python代码补全插件aiXcoder

插件市场,安装便捷

aiXcoder 2.5 目前支持 Java 和 Python,它们都可以在 JetBrains 市场上直接下载与安装。即在 IntelliJ IDEA 的市场上,可直接下载 aiXcoder用于 Java 代码补全。同理在 PyCharm 市场上,也可以直接下载 aiXcoder用于 Python 代码补全。

在 PyCharm 上,下面三步即可完成安装:

节省一半键入量,实测Python代码补全插件aiXcoder

与之前一样,Python 插件安装后,它会自动下载服务端,只需要等十几秒,就能快速用上。反正笔者已经愉快地用上了,小伙伴们也来试试呀。

 

最后,更多语言与 IDE 支持可查阅下载页面:

https://www.aixcoder.com/#/Download

转自:https://mp.weixin.qq.com/s/MNK0I6xKJ837S6w2CrZ0gA

早安!被房价和沪牌吊打的打工人

沪漂小许最近的心情真的是坐过山车一样。

 

住在宝山的他,有一辆挂着外地车牌的小轿车,每天从家里开车去大宁上班。有时候领导让他去市中心办点事,高架、内环内随便开,只要下午三点前开回普通地面道路就行。

 

早安!被房价和沪牌吊打的打工人

 

结果上个月末,上海交警的新规,打他了个措手不及:从2020年11月2日起,外地车牌的限行时间调整为工作日的7时至20时。

 

早安!被房价和沪牌吊打的打工人

 

也就是说,小许一整个白天都不能开车上市区的主要高架、隧道、大桥了,对他来说最重要的南北高架,呼玛路至鲁班立交段都不能上,那可怎么上班?

 

那辆外牌车,于小许而言,突然就变成了一堆可移动的废铁。

 

昼伏夜出?你当我是夜猫子啊。

  

1

不仅如此,从明年五一小长假后开始,工作日的7-10点、16-19点,内环内再也不许外地车开了。

 

小许一周最起码需要去2-3次内环拜访客户,这下明年五一后,早于早上十点、晚于下午四点,内环也不能开了,难道内环内这么多大公司都得搬出来?

 

岂有此理!

 

上海有高达167万辆外地车,占全市保有量的三分之一,新政大大的影响了着一大批车主的出行。

 

早安!被房价和沪牌吊打的打工人

 

于是上周,小许就拉着我给他出出主意。我想了想,无非就是拍牌买新能源车

 

作为典型的沪漂,小许的社保交了两年不到,但拍牌需要连续3年的社保记录,等待太久了。而且新规一出,代拍的黄牛乐开了花:想要拍牌的人突然多了好几倍!

 

又要9万多的价格,又只有10%左右的中签率,小许只能灰心放弃。

 

早安!被房价和沪牌吊打的打工人

 

那第二条路就是去买新能源车了。

 

开惯了汽油车的小许对于纯电动车的续航里程还是有点焦虑,所以就拉我跟他一起去商场看了可以加油的插混车,送沪牌,不限行,6个座位,30万出头落地,看上去很香。

 

早安!被房价和沪牌吊打的打工人

 

但销售小姐姐的一番话让他打了退堂鼓。

 

社保有12个月没有?有了。

 

充电桩有吗?这…

 

新能源车上牌,需要固定车位的充电桩,而小许所在的楼盘,只有业主才有资格拥有固定车位,小许才刚来上海两年,根本不满足五年社保家庭才能买房的条件。

 

没交满五年社保就不能买房 → 没买房就没有固定车位 → 没固定车位就没有充电桩 → 没充电桩就不能买新能源车 → 没有新能源车就不能开车上班。

 

咱们沪漂人,这也太惨了吧。

 

早安!被房价和沪牌吊打的打工人

 

2

实在没辙的小许,接下来要尽量避开高架和内环,开始每天堵在地面上的生活,要么,只能去租一辆不限行的车,这又是一笔不小的开销。

 

小许的遭遇只是众多外地人来上海拼搏的一个缩影,沪漂们的九九八十一难,真是说也说不过来。

 

最明显的,就是户口问题。

 

目前,上海落户一般有6大类:

 

1.应届毕业生打分落户
2.海归留学生落户
3.居转户
4.人才引进落户
5.投资创业、企业高管落户
6.亲属投靠等其他途径

 

每年,上海落户人数在11-13万人左右,其中,像小许这种通过就业、社保的居转户和人才引进,每年只有4.5万人左右。

 

其中,每年人才引进基本占三分之一,也就是1.5万人左右,而且看看落户的名单,都是些什么“神仙公司”:

 

早安!被房价和沪牌吊打的打工人

 

人才引进要看公司甚至所在的区有没有名额,像浦东一个区(含临港)基本会占掉40%的名额,剩下的15个区瓜分其余的60%,真是难上加难。

 

而居转户的名额,每个月也就2000人左右,更是一关接着一关,居住证年限要满足7年,学历、职称要有一定标准,工资、社保和个税也要达标,一切都搞定后,由于落户总量有限制,接下来就是排队与等待。

 

3

户口的重要性,相信沪漂们都懂。

 

办理港澳通行证、台湾通行证、护照更换身份证、办理临时身份证……外地户籍虽然也能在上海办理,但办理时间会慢一些,若是上海户口,办证会方便很多。

 

上海户口的个人可以办理社保,非沪籍只能公司办理社保,自由职业的沪漂们很烦心。

 

上海户口的小孩可以办理少儿医保,非沪籍小孩需父母一方有上海居住证且积分120分才可以办理。

 

而上海户口的优势,在买房上展现得淋漓尽致。

 

小孩上学,就算你买了学区房,要是没户口,还是排在人家人户一致的家庭后面。

 

外地户口,单身是吧?不好意思,无论交多少年社保都没有购房资格,只有落户才行。而租房子的麻烦,我们都懂。

 

早安!被房价和沪牌吊打的打工人

 

外地户口,结婚了是吧?不好意思,整个家庭也只能买一套。如果你想一套自住一套学区,想都不用想;如果你要置换,抱歉,只能先把手头上的房子卖了,才能腾出名额买下一套。

 

先卖后买,在目前的上海楼市是非常危险的。你看上的房子,地段、品质、学区更好,往往是领涨的龙头,你在卖掉原来房子之前,没法提前锁定目标房源的价格,而且你的目标房源往往涨的比你现在持有的房子要多,这过程非常痛苦。

 

胆子大一些的,先把定金交了,一旦原来的房子短时间内不能以合适的价格出售,就面临违约风险,只能打骨折售出,损失颇大。

 

缺乏了买卖房子的灵活性,这在置换过程中会非常被动。笋盘,就是这么来的~

 

4

沪漂们落户上海的艰难,我们都懂。但我一直相信,只要足够努力,变得足够优秀,对于我们艰苦奋斗的打工人来说,那都不是事儿!

 

早安!被房价和沪牌吊打的打工人

 

举个例子,对于沪牌,就算是上海本地人,其实也拿他没办法,10%左右的中签率,差不多一辆车的拍卖价格,使得不少上海本地人也开着赣E、浙D、苏F甚至黑C拍照的车,在高架下“望路兴叹”。

 

实在因为运气问题拍不到,还有15万一块的公司牌,价格者得,童叟无欺。

 

早安!被房价和沪牌吊打的打工人

 

你看,其实能不能拿到沪牌,和出身关系不大,更看重的是努力程度。

 

买房也是如此,进大单位走人才落户,双一流毕业走应届生落户,甚至创业成立公司,投资到一定数额,无论是以公司名义买还是以落户高管身份买,都不是问题。

 

问题是,我们足够优秀吗?

 

反观上海本地人,至今还有大量的“老上海”蜗居在市中心的棚户区和老破小中,上海户口除了给他们带来了一口流利的上海话外,好像并没有什么优势。有房票,买不起,能拍车牌,也拍不起。

 

早安!被房价和沪牌吊打的打工人

 

而足够努力、足够优秀的新上海人,则已经在上海新房市场里大杀四方,9万+的静安映,10万+的鑫耀中城,310打头的上海人占比已经不足三分之一,另外三分之二都被外地人“后来居上”。

 

早安!被房价和沪牌吊打的打工人

 

而更厉害的外地人——李佳琦们,不仅在上海买了房,还通过自己的努力,把成千上万的上海人变成了“尾款人”。

 

早安!被房价和沪牌吊打的打工人

 

沪漂们,你愿意做哪一种呢?

 

以上是正文,来自平层86。
如果你觉得有用,请把这篇文章转发给有需要的朋友。

 

【END】

转自:https://mp.weixin.qq.com/s/AcK95V7b17ukTCUlTHJo0A

Python 实用技:将 Office 文件转为 PDF

文 | 潮汐

来源:Python 技术「ID: pythonall」

Python 实用技:将 Office 文件转为 PDF

在平时的工作中,难免需要一些 小Tip 来解决工作中遇到的问题,今天的文章给大家安利一个方便快捷的小技巧,将 Office(doc/docx/ppt/pptx/xls/xlsx)文件批量或者单一文件转换为 PDF 文件。不过在做具体操作之前需要在 PC 安装好 Office,再利用 Python 的 win32com 包来实现 Office 文件的转换操作。

安装 win32com

在实战之前,需要安装 Python 的 win32com,详细安装步骤如下:

使用 pip 命令安装


pip install pywin32

如果我们遇到安装错误,可以通过python -m pip install –upgrade pip更新云端的方式再进行安装即可:


python -m pip install --upgrade pip 

下载离线安装包安装

如果 pip 命令未安装成功的话还可以下载离线包安装,方法步骤如下:首先在官网选择对应的 Python 版本下载离线包:https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/ 下载好后傻瓜式安装好即可。

文件转换逻辑

详细代码如下:

class PDFConverter:
    def __init__(self, pathname, export='.'):
        self._handle_postfix = ['doc''docx''ppt''pptx''xls''xlsx'# 支持转换的文件类型
        self._filename_list = list()  #列出文件
        self._export_folder = os.path.join(os.path.abspath('.'), 'file_server/pdfconver')
        if not os.path.exists(self._export_folder):
            os.mkdir(self._export_folder)
        self._enumerate_filename(pathname)

    def _enumerate_filename(self, pathname):
        '''
        读取所有文件名
        '''

        full_pathname = os.path.abspath(pathname)
        if os.path.isfile(full_pathname):
            if self._is_legal_postfix(full_pathname):
                self._filename_list.append(full_pathname)
            else:
                raise TypeError('文件 {} 后缀名不合法!仅支持如下文件类型:{}。'.format(pathname, '、'.join(self._handle_postfix)))
        elif os.path.isdir(full_pathname):
            for relpath, _, files in os.walk(full_pathname):
                for name in files:
                    filename = os.path.join(full_pathname, relpath, name)
                    if self._is_legal_postfix(filename):
                        self._filename_list.append(os.path.join(filename))
        else:
            raise TypeError('文件/文件夹 {} 不存在或不合法!'.format(pathname))

    def _is_legal_postfix(self, filename):
        return filename.split('.')[-1].lower() in self._handle_postfix and not os.path.basename(filename).startswith(
            '~')

    def run_conver(self):
        print('需要转换的文件数是:', len(self._filename_list))
        for filename in self._filename_list:
            postfix = filename.split('.')[-1].lower()
            funcCall = getattr(self, postfix)
            print('原文件:', filename)
            funcCall(filename)
        print('转换完成!')

doc/docx 转换为 PDF

doc/docx 转换为 PDF 部分代码如下所示:


    def doc(self, filename):
        name = os.path.basename(filename).split('.')[0] + '.pdf'
        exportfile = os.path.join(self._export_folder, name)
        print('保存 PDF 文件:', exportfile)
        gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}'084)
        pythoncom.CoInitialize()
        w = Dispatch("Word.Application")
        pythoncom.CoInitialize()  # 加上防止 CoInitialize 未加载
        doc = w.Documents.Open(filename)
        doc.ExportAsFixedFormat(exportfile, constants.wdExportFormatPDF,
                                Item=constants.wdExportDocumentWithMarkup,
                                CreateBookmarks=constants.wdExportCreateHeadingBookmarks)
        w.Quit(constants.wdDoNotSaveChanges)
 def docx(self, filename):
        self.doc(filename)

ppt/pptx 转换为 PDF

ppt/pptx 转换为 PDF 部分代码如下:


 def ppt(self, filename):
        name = os.path.basename(filename).split('.')[0] + '.pdf'
        exportfile = os.path.join(self._export_folder, name)
        gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}'084)
        pythoncom.CoInitialize()
        p = Dispatch("PowerPoint.Application")
        pythoncom.CoInitialize()
        ppt = p.Presentations.Open(filename, FalseFalseFalse)
        ppt.ExportAsFixedFormat(exportfile, 2, PrintRange=None)
        print('保存 PDF 文件:', exportfile)
        p.Quit()

    def pptx(self, filename):
        self.ppt(filename)

xls/xlsx 转换为 PDF

    def xls(self, filename):
        name = os.path.basename(filename).split('.')[0] + '.pdf'
        exportfile = os.path.join(self._export_folder, name)
        pythoncom.CoInitialize()
        xlApp = DispatchEx("Excel.Application")
        pythoncom.CoInitialize()
        xlApp.Visible = False
        xlApp.DisplayAlerts = 0
        books = xlApp.Workbooks.Open(filename, False)
        books.ExportAsFixedFormat(0, exportfile)
        books.Close(False)
        print('保存 PDF 文件:', exportfile)
        xlApp.Quit()

    def xlsx(self, filename):
        self.xls(filename) 

执行转换逻辑

if __name__ == "__main__":
    # 支持文件夹批量导入
    #folder = 'tmp'
    #pathname = os.path.join(os.path.abspath('.'), folder)
    # 也支持单个文件的转换
    pathname = "G:/python_study/test.doc"
    pdfConverter = PDFConverter(pathname)
    pdfConverter.run_conver()

总结

今天的文章主要是 Python 实战之小工具的运用,希望对大家有所帮助。

转自:https://mp.weixin.qq.com/s/ffqHzv08n84cIfCG5o_P8A

各区高中的性价比

 

 

 

整个K12的教育规划,以高中选择作为基石。高中向后对接高考,向前对接中考。

 

而作为新政下实际上的第一次考核,中考又是整个义务教育段教育规划的终点。

 

所以用中考和高考的对比数据来衡量各区各高中的性价比,是对中考以下各个年级的家长都有意义的一项分析。

 

各区高中的性价比

 

这个话题,在坊间有各种“管窥全豹”式的分析。有一些讲的挺对,而大部分略偏面。G大自己做完这项覆盖面更大的数据分析以后,也改变了一部分自己之前的观点。今天我们就详细讲一讲。

 

开始之前先重复一下我对教育数据的一贯态度:数据只代表过去。用数据预测未来肯定有偏失。在这个大前提下,我们在数据中搜寻有用的信息。

 

先看一下高中的高招表现排位。为了能做对比,G大把高中2019年的高招数据单独拿了出来做了排名,并加入了艺体生,因此以下排位和之前文章的排位会略有不同。(排位依据从一套综合指数计算而来,下图备注里有简介。)

 

各区高中的性价比

 

各位从各区高中出现的数量,排位,各高中之间的间隔/扎堆,跨区相近水平高中的比较,应该已然能够得出一些结论。G大这里先不展开,你们可以自行思考一下。

 

顺便再放一下G大版本的上海高招学校分档表。各档学校都按照不同权重计入上述排位所依据的指数中。

 

各区高中的性价比

 

我们把上述各高中的高招结果指数,去交叉他们2016年的中考本区收分线,可以得到下面这张图:

各区高中的性价比

 

肉眼可见的正相关。这个也是G大之前反复讲的中考决定高考那句话的由来。

 

简单跑一下数据拟合,会发现这是一个2阶多项式回归。说人话:这就是我们小时候学的一元二次函数图形的右半。

 

这个拟合结果给我们几个启示:

 

  1. 高分段非常陡,中考每差一分,相应高考结果就要差非常多

  2. 低分段相对缓和,中考分数差别导致进入不同高中所对应的高考结果差距没有那么大

  3. 围绕着拟合线,高中自然落在了高性价比和低性价比两边,如下图

各区高中的性价比

在高性价比这边最突出的三所学校:交附闵行、西位、久隆,都有故事。

 

交附闵行2016年第二年招生,中考分数位居全市第16,明显有红利。而随着该校毕业生参加高考,口碑大涨,该校的中考分数线也逐年提升。2020年为全市第六。

 

西位高考出成绩靠直升班,久隆作为扶助贫困生学校,出成绩靠各区零志愿,这两个也都是特例。

 

低性价比这边同样也有一些著名的例子:松一、北郊、川沙、上师、建平。这些我们下文讲各个区的时候再展开。

 

闵行

各区高中的性价比

闵行的教育呈高度两极割裂。

 

头部的七宝、交附闵行、华二紫竹三校站在全市的最头部,拥有不错的性价比,和相当亮眼的高招表现。闵行教育竞争激烈不假,然而在这个部位,闵行的教育竞争反而有一些红利。

 

然而在头部三校以下,闵行出现了严重的断档。接下去的上师闵、闵中和上闵外三校,其高招表现和头部三校差距甚远。

 

这三校中上师闵是闵行性价比低之最(该校2020年高招结果有比较大进步),而闵中则一直是这个区间中庸的存在。上闵外倒是相对亮眼:高考第一届已经是全市区重点第二的水平。

 

后面的四所高中也都没什么亮点,各自吸纳各档考生,性价比整体低于全市水准。

 

所以“头部娃的天堂,普娃的炼狱”是当下对闵行的合适评价。

 

徐汇

各区高中的性价比

上中是妖怪,高招出路拉其他三校和七宝一大截。

 

徐汇区其他的市重点就有些遗憾了。南模、位育、市二,南洋全部在性价比线以下。教育大区竞争的确激烈。

 

区重点这档徐汇区反而表现不错。西位前面讲过了。徐汇中学非常出色得站在高性价比一侧。其后的中国和市四也基本在线上。

 

另外这张图没有放出来的紫竹园中学,是全市唯二两家高招出路相当不错的美术高中。各位感兴趣可以到上面的Top100排位表自行查看。

 

整体而言徐汇区高中分布合理,档次丰富,区重点和美术高中实力不俗,但市重点性价比略低。

 

杨浦

各区高中的性价比

G大常说杨浦盛名之下其实难副,是有数据支撑的:该区就是上面徐汇那张图整体往下移动一段距离,也因此整体性价比更低。

 

交附复附这两所头部学校分列性价比线两侧,也算头部学校里面的基准值。控江略为在性价比线以下一点点,整体实力仍然有正牌八大的水准。

 

杨浦控江以下就是惨惨惨。杨高和同一基本上就是区重点水平,分数还不低。而杨浦真正的区重点则水平更低,性价比更低。

 

如果说闵行是“牛蛙的天堂,普娃的炼狱”,那么杨浦就是“牛蛙的竞技场,普娃的地狱”。

 

静安

各区高中的性价比

本文开头讲过,这次的完整数据分析颠覆了一些G大过往的认知。这句话主要就是讲静安。

 

静安由于头部实在太弱,经常被包括我在内的各类分析归为教育弱区。

 

但是

 

静安区,如上图所示,几乎全部学校都在性价比高的那一侧。这件事你可以理解成静安整个区在高中阶段都是低进高出的。

 

静安另一个特点是他区重点进入100强的数量是除了浦东以外最多的。这支撑起了静安普娃高招良好出路。

 

所以静安就成了“牛蛙不存在,普娃的天堂”。这种局面会和目前静安大宁地区快速崛起的教育高地形成什么样的合力,G大很期待。

 

黄浦

各区高中的性价比

黄浦市重七龙珠,有没有?

 

黄浦整体而言分布性价比两侧,是很“平均”的一个区。值得注意的是,和大家都认为大同更强相反,从全口径且按照比例计算,G大看了三年都是格致比大同更强。

 

向明的高性价比一部分是靠向明浦江的闵行娃冲出来的,徐汇的市二也一样。

 

头部三校以外的四校,基本上就是比较强的区重点水平。好在除了光明以外,性价比都还不错。

 

而黄浦真正的区重点就弱得看不到了。

 

虹口

各区高中的性价比

虹口,叹口气。

 

先讲亮点:复兴没有民间刻板印象中那么弱。他主要缺点是招生数在八大中最少,所以在看总量的时代显得很渺小。但是看全渠道加计算比例,复兴意外得并不差,性价比也合适。另,2020年复兴有较大进步。

 

虹口另一个亮点是2020年首次有毕业生的上外东校首届高考战绩不错。(本图是2019年数据,没有该校)

 

除此之外,虹口就全是槽点了。华一和北郊这两家市重点性价比低得发指。战斗力不错的区重点虹高,也一样的性价比低。只有北虹在性价比线上一点。

 

长宁

各区高中的性价比

长宁是另一个被刻板印象所掩盖的区。提起长宁总是各种负面评价,G大自己也写了一堆。

 

今天的数据分析一看,啪啪打脸,疼。

 

延安正八校的位置很稳,而且性价比很高。被我说了好几次的市三女中,性价比也很高。只有复旦中学性价比略差。

 

另外对于长宁的体量来说,三家位置合适,性价比高的区重点也是该区普娃的福音。

 

G大之前的系列文章里有一个评论让我记忆犹新:是否在不鸡血的基础教育之下,能带出长宁这样的高考成绩,本身就说明这个区的教育有独特之处?

 

不鸡血的基础教育造成相对没有那么激烈的中考竞赛,并最终形成相对较高的性价比。这就是我对长宁区的新观感。

 

普陀

各区高中的性价比

近段时间G大很推崇的普陀区的数据没有辜负我,非常耀眼。该区几乎是16区数据里面最漂亮的。

 

曹二在八大段位里面拥有最高的性价比。性价比同样出色的还有区老三宜川。区老二晋元是普陀唯一性价比略低的高中。

 

普陀高中的一个特点是他多达四所的特色校。本图有三所,全在性价比线之上。其中曹杨的性价比是区重点段里面除了特殊的久隆以外最高的。

 

本图没有出现的另一家特色校,走以音乐为主艺术路线的上音安师,也是一家高招出路不错的高中。这点在2019年的数据看不出来,但是在2020年的数据里面就很明显。

 

浦东

各区高中的性价比

 

浦东高中的性价比,意料之中的低。

 

四大段三所学校,两所在性价比低的这边。另外一所上实,高考主要靠初中进去的那批娃输出,和中考进去的这批没啥大关系。

 

市重点段从建平到南汇,性价比基本全部垫底。这种整体的低性价比,反应出来的是浦东高度内卷化的教育竞争。亦即浦东的娃要在中考这关考出大幅高于别区的成绩,才能进相同等级的高中。

 

而这种在中考里面的相对整体高分,并没有在高中三年中形成在高考中的相对整体更优秀的高招结果。

 

可叹。

 

浦东的亮点在区重点段。东昌作为特色校,高考结果已经跃居南汇之上,接近川沙。高桥作为一所准市重点校,拥有全浦东最高的性价比。另外在区重点段性价比高的还有屡出奇迹的新川。

 

不知道作为近几年高中阶层跃迁奇迹的北蔡中学过两年会不会加入这个阵营。

 

本图中没出现的香山中学是本市美术类高中的一哥,高考整体出路表现在高桥和新川中间,也是不错的选择。

 

 

接下去我们进郊区,讲得会快一些。

 

宝山

各区高中的性价比

宝山有著名的性价比之王行知。从这个图看起来的确还不错。吴淞、上大这一对实力接近得市重点,双双落在性价比线上方。宝山唯一出现得区重点罗店性价比较低。

 

奉贤

各区高中的性价比

网红奉贤中学性价比是不错,但是没有想象中那么好,主要是除了一个强头部以外,奉中中段的娃表现一般。这和奉贤作为一个郊区的牛蛙深度不够有关。

 

该区曙光中学表现也不错。其实在奉中升市重点之前,曙光和它是隔着一条河各召奉贤东西两侧的娃。而现在奉中基本收走了全区的牛蛙,曙光的处境就比较艰难了。

 

格致奉贤性价比低。格致的风格和奉贤的基本盘还没调和好。

 

嘉定

各区高中的性价比

交附系的高中性价比都不错,交附嘉定也不例外。这算是给嘉定娃的一点福利。

 

嘉一在线上一点,嘉二在线下一点,都没大特色。

 

松江

各区高中的性价比

松江是唯一一个所有高中全部在性价比线以下的区。固步自封。

 

青浦

各区高中的性价比

青浦三所高中全部在性价比线上一点点的位置。这种没有特点,或许是该区考生的好事。

 

不过随着五浦汇的娃进入高考,该区会发生什么变化,G大拭目以待。

 

金山、崇明

各区高中的性价比

两个偏远区一并讲掉。三所市重点尽管表现一般,但性价比都还不错。原生态的郊区竞争都没有那么激烈,中考的内卷现象并不严重。

 

尾言 

如开篇所述,数据只代表过去。用过去来推测未来,肯定会不准。G大选择把其中相对有价值的部分挖了出来呈现给大家,供各位思考。

 

 

转自:https://mp.weixin.qq.com/s/YAppuL9BQeeF0w-ATmOg2g