Python Third Party
numpy
indexing
indexing 操作指的是 x[obj] 这种形式获取参数, 其中 x 是 np.ndarray 对象, 注意: x[1, 2] 与 x[(1, 2)] 是完全等价的, 只是语法趟, 按 obj 的不同类型, 可以区分为如下几类:
Basic Indexing
Basic Indexing 触发的条件如下:
np.newaxis is None # True
Ellipsis is ... # True
# Basic Indexing 总是返回一个 view
obj: Union[int, slice, Ellipsis, np.newaxis, List[Union[int, slice, Ellipsis, np.newaxis]]]注意 Basic Indexing 的返回的结果总是原数据的一个 View, 这暗示了一个副作用:
x = np.arange(1000000)
y = x[1]
del x # 不会释放 x, 只是不能使用 x 这个标识符一些例子:
x = np.arange(24)
x.shape = (2, 3, 4)
x[:, np.newaxis, :, :, None].shape # (2, 1, 3, 4, 1)
x[..., 1:2].shape # (2, 3, 1), 只能最多出现一个 Ellipsis, 并且 1:2 这种写法会保留这个维度本身
x[..., 1].shape # (2, 3)
# slice(i, j, k), 首先总是将 i 和 j 转为整数, 然后再区分 k 为正数还是负数, 但区间总是左开右闭: [i, j)
x[0, 0, -1:-3:-1] # slice(i=-1, j=-3, k=-1), 首先将 i, j 转换为整数, 转换方式为: i = -1 + x.shape[2] = 3, j = -3 + x.shape[2] = 1
# 等价于 x[0, 0, 3:1:-1], 因此取出: [x[0, 0, 3], x[0, 0, 2]] 得到数组 [3, 2]
# 空数组情形
x[1:1, ...].shape # (0, 3, 4)Advanced Indexing【很复杂,待续】
触发条件:
Advanced indexing is triggered when the selection object, obj, is a non-tuple sequence object, an ndarray (of data type integer or bool), or a tuple with at least one sequence object or ndarray (of data type integer or bool). There are two types of advanced indexing: integer and Boolean.
如果 obj 本身是序列类型(但不是元组)或是数组(数据类型可以是bool或int), 或者 obj 是一个元组, 但元组至少有一个元素是序列类型或是数组(数据类型可以是bool或int)
Advanced indexing always returns a copy of the data (contrast with basic slicing that returns a view).
Advanced Indexing 总是返回复制
一些例子:
奇怪的 id
topk
save & load
pandas
pandas 的 apply 系列
apply: DataFrame的方法, 可指定axis,应用于行或列
args用于指定额外参数, 但这些参数对于每行或每列是相同的
applymap: DataFrame的方法, 应用于每一个元素
applymap
DataFrame.applymap(self, func)
逐元素操作
apply
DataFrame.apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds)
按行或列操作
apply
Series.apply(self, func, convert_dtype=True, args(),**kwds)
逐元素操作
map
Series.map(self, arg, na_action=None)
替换或者逐元素操作
pandas 分组操作
总体上, 从效果上来说分为三个步骤:
Splitting: 数据分组, 对应的 API 是
pd.DataFrame.groupbyApplying: 一般来说有如下几类, 更一般地, 可以使用 splitting 的结果调用
apply函数Aggregation: 分组后, 对每一组计算一个统计值
Transformation: 分组后, 对每一组分别应用于一个变换, 例如对于 A 分组, 将空值填充为 "A", 对 B 分组, 将空值填充为 "B"
Filtration: 分组后, 根据一些条件筛选分组内的数据或者筛选整个组地数据, 例如如果一个分组的行数小于 10, 则删除整个分组的数据; 每个分组都只取前 3 行.
Combining: 将 Applying 的各个分组的结果合并在一次做返回
涉及的 API:
Splitting:
pd.DataFrame.groupby: 返回一个pandas.api.typing.DataFrameGroupBy对象, 此对象有groups,get_groups等基础方法/属性, 也具有下面的 Applying 步骤中的agg/aggregation,transform,filter,apply等方法Applying:
Aggregation: 内置的方法例如:
mean,std, 更一般地可以使用agg/aggregation(这两个方法是一样的,agg只是 short-hand 写法)Transformation: 内置的方法例如:
cumsum, 更一般地可以使用transformFiltration: 内置的方法例如:
head, 用于取每组的前几行, 使用自定义函数可以用filter, 但注意filter只能将整组全部去掉或保留上面 3 种都不满足时, 可使用
apply函数
Splitting: groupby 与 pandas.api.typing.DataFrameGroupBy 对象的方法
Applying: agg、transform, filter, apply
agg、transform,filter,apply传参时的自定义函数 (UDF: User-Defined Function) 的输入输出条件 (官方文档似乎对此语焉不详) 不尽相同这几个方法最终 Combining 之后的 DataFrame 的 index 的形式有所不同, 这里只讨论
group(..., group_keys=True)的情况:apply在 UDF 的出参是一个 DataFrame 的情况下, 会把 groupby 的列作为索引并保留原始索引以构成两级索引transform会把 groupby 的列丢弃, 原本的索引依然作为索引agg会把 groupby 的列作为索引, 原本索引丢弃filter除去索引可能会变少外, groupby 列被保留为列
太长不看系列 (transform, agg, filter 都可用 apply 实现):
参考资料:
UDF 的输入输出对比
transform
输入: 每组的每一列(Series)作为输入 输出: 与输入同等长度的列表/Series
agg
输入: 每组的每一列(Series)作为输入 输出: 标量
filter 输入: 每组的DataFrame作为输入 输出: True/False
apply 输入: 每组的DataFrame作为输入 输出: 标量/Series(不必与输入同等长度)/DataFrame
为了弄清这些函数的 UDF 的输入输出, 可以构造类似如下的测试代码
输出结果如下:
pandas 可能会自动做些优化 (与numba有关), 所以有些像上面那种测试代码可能会有比较诡异的结果, 例如:
pandas读写excel文件
pandas读写excel依赖xlrd, xlwt包, (ps: 可以尝试直接使用这两个包直接进行读写excel文件)
直接使用xlrd包示例: 参考链接
直接使用xlwt包示例: 参考链接
某些情况下.xlsx被自动转为了.xlsm格式, 可以用pandas进行修复, 注意下面的例子也演示了如何获取一个excel文档的所有sheet名称
pandas index相关的操作
merge, join技巧
easydict/addict/dotmap
这几个包均是对 python 字典这一基本数据类型的封装,使得字典的属性可以使用点来访问,具体用法及区别待补充:
一些开源项目对这些包的使用情况:
addict:mmcv
easydict:
dotmap:MaskTheFace
语言检测模块
langdetect, langid等
发送邮件模块
压缩与解压模块
zipfile模块
参考链接: https://www.datacamp.com/community/tutorials/zip-file#EZWP
pyhanlp
安装说明(1.7.8版本)
step 1
首先安装JPype1==0.7.0(版本号必须完全一致)
step 2
接下来安装pyhanlp(直接去网站下载代码pyhanlp-master.zip, 注意项目名为pyhanlp)
并下载: jar与配置文件hanlp-1.7.8-release.zip, 数据文件data-for-1.7.5.zip
注意data1.7.5是被1.7.8版本hanlp兼容的(实际上也没有data1.7.5版本), 至此原料已经准备齐全
首先将pyhanlp-master.zip解压, 并进入该目录用如下方式安装
接下来进入安装位置例如:
C:\Users\54120\anaconda3\envs\hanlp_copy\Lib\site-packages\pyhanlp-0.1.66-py3.7.egg\pyhanlp\static
将data-for-1.7.5.zip解压后的data文件夹, hanlp-1.7.8-release.zip解压后的hanlp-1.7.8-sources.jar, hanlp-1.7.8.jar, hanlp.properties都放入上述目录下, 最终此目录的结构为:
step 3
修改hanlp.properties文件的内容
step 4
检查, 在命令行输入
另外, python中应该也要确保可以正常导入
注意
上述繁琐的过程使得环境迁移时除了拷贝envs还要修改配置文件.
spacy
下载模型
解决类似如下命令因为网络原因失效的方法:
去https://github.com/explosion/spacy-models/查看相应的版本号, 下载类似如下链接的文件
更为详细的解释如下(spacy2.1.9版本源码分析)
执行 python -m spacy download en_core_web_sm 实际调用 site-packages/spacy/__main__.py。之后调用了
huggingface transformers
基本使用
模型下载目录
设置模型下载位置可参见官网介绍, 摘抄如下:
Caching models
This library provides pretrained models that will be downloaded and cached locally. Unless you specify a location with cache_dir=... when you use methods like from_pretrained, these models will automatically be downloaded in the folder given by the shell environment variable TRANSFORMERS_CACHE. The default value for it will be the Hugging Face cache home followed by /transformers/. This is (by order of priority):
shell environment variable
HF_HOMEshell environment variable
XDG_CACHE_HOME+/huggingface/default:
~/.cache/huggingface/
So if you don’t have any specific environment variable set, the cache directory will be at ~/.cache/huggingface/transformers/.
Note: If you have set a shell environment variable for one of the predecessors of this library (PYTORCH_TRANSFORMERS_CACHE or PYTORCH_PRETRAINED_BERT_CACHE), those will be used if there is no shell environment variable for TRANSFORMERS_CACHE.
开源模型
发现有英翻中的模型, 开源模型目录, 搜索zh, (https://huggingface.co/Helsinki-NLP/opus-mt-en-zh)
使用方法:
离线模型下载实例
EncoderClassifier 中有如下注释:
离线下载模型步骤如下:
这样便可以直接使用如下方式导入模型(完全绕过默认路径 ~/.cache/huggingface/hub)
备注:此处的 git clone 这一方法在离线下载时具有通用性,而修改 pretrain_path 是 speechbrain 包的内部的逻辑造成的。如果不修改 pretrain_path,将无法绕过默认下载路径 ~/.cache/huggingface/hub。
读写excel(xlsxwriter与pandas)
pandas与xlsxwriter均支持给输出的excel自定义格式.
对单元格的一些字符变成红色字符, 另一些字符仍然为黑色
数据验证(单元格内的值必须满足一定的条件)
html转pdf的(pdfkit)
依赖于wkhtmltopdf, 安装后(windows上需添加至环境变量)可以利用pdfkit包进行html到pdf的转换, 实际体验感觉对公式显示的支持不太好.
black(自动将代码规范化)
black模块可以自动将代码规范化(基本按照PEP8规范), 是一个常用工具
albumentations(待补充)
基于opencv的数据增强包
natsort
yacs
作者为 faster rcnn 的作者 Ross Girshick,用于解析 yaml 文件
timeout_decorator
超时自动退出装饰器
redis
python调用redis服务主要是如下两个第三方包:
包名(pip install): redis, import时的模块名: redis
包名(pip install): redis-py-cluster, import时的模块名: rediscluster
以上两个包存在一些版本兼容性问题:
redis-py-cluster依赖于redis, 但目前redis已经集成了redis-py-cluster的所有内容, 使用方式为:
redis-py-cluster github README
In the upstream package redis-py that this librar extends, they have since version * 4.1.0 (Dec 26, 2021) ported in this code base into the main branch.
The cluster client is based on Grokzen’s redis-py-cluster, has added bug fixes, and now supersedes that library. Support for these changes is thanks to his contributions
redis包在旧版本中存在
Redis与StrictRedis两个类, 但在目前的3.x及以上版本已经合并为一个类, 源码中有如下代码:关于StrictRedis与Redis的讨论参考stackoverflow
结论: 不要使用
redis-py-cluster, 直接安装redis 4.x及以上版本, 使用redis.Redis和redis.cluster.RedisCluster类, 不要使用redis.StrictRedis
具体使用方式为:
pytest
经过阅读多篇相关的博客, 总结如下, python 包的项目组织形式严格按照如下方式进行
备注: 很多项目其实未必采用了“正确”的方式组织代码,“正确”的含义也会随着时间的推移而改变
关于安装
关于 pyproject.toml 与 setup.py 与 setup.cfg:这三个文件与 pip install -e . 或 pip install 的行为相关。
pyproject.toml:可以配置打包工具,也可以配置包的一些基本信息。当打包工具项配置为
setuptools.build_meta时,那么pip会去按照setup.py的逻辑去执行安装命令,pyproject.toml和setup.cfg的其余配置项也会被自动用于setup.py的执行逻辑里当打包工具配置为其他选项例如:
hatchling.build时,那么pip会忽略setup.py,而按照pyproject.toml和setup.cfg的其余配置项执行安装命令。
setup.py与pyproject.toml同时存在时,执行pip install .命令时会按照pyproject.toml来执行。当然,执行python setup.py install安装时则忽略pyproject.toml。但一般情况下,推荐使用pip install .而非python setup.py install。
关于测试
关于 __import__、import、importlib.import_module:stackoverflow问答
import 实际上最终是调用了 __import__,而 __import__ 在底层是直接使用了 C 代码来实现。importlib.import_module 本质上的行为跟 __import__ 比较类似,只是实现方式上是使用纯 Python 来实现的。具体解释如下:
import tests.test_a.test_b:sys.modules增加tests、tests.test_a、tests.test_a.test_b这几个模块,当前文件可以使用tests.test_a.test_b这个命名空间,即可以使用tests.test_a.test_b.xx。mod = importlib.import_module("tests.test_a.test_b"):sys.modules增加tests、tests.test_a、tests.test_a.b这几个模块,当前文件可以使用mod这个命名空间,即可以使用mod.xx。在进行了上面两种方式之一进行导入后,可以直接使用
sys.modules获取tests.test_a:
pytest 在处理 import 的问题时, 支持了三种方式 prepend, append 与 importlib。但每种方式都有各自的缺点。目前的最佳实践是:
包放在
src目录下,所有的模块各个目录应显式地添加__init__.py。放入src目录最主要的目的是使得本地开发环境与 release 到 PyPI 后别人使用pip install的方式安装的环境相同。测试代码完全按包的形式组织,即各个目录显式地添加
__init__.py,独立于包之外,即与src目录同级方式tests文件夹。测试的目的是包在安装之后的行为是否正常,测试前应该以
pip install -e .或pip install .的方式将包安装。这也是包要放在src目录下的原因,即保证不会意外导入当前路径下的代码(很多情况下,sys.path变量会把当前路径添加至模块搜索路径,这样子可能会意外导入)。以默认的
prepend作为 pytest 的导入方式。注意:按照 pytest 内部的逻辑,使用prepend作为导入方式,不可避免地会修改sys.path,但测试代码完全按包的形式组织,已经可以尽可能小的避免了sys.path的修改,但好处是tests目录下的各个文件之间可以相互 import。import 的方式应为import tests.xx.yy。然而使用importlib作为导入方式,测试文件之间无法进行相互 import,这是一个重要的缺点。执行
pytest命令(即不要以python -m pytest的方式启动):使用python -m pytest会将当前目录添加至sys.path目录,因此要避免使用。
代码片段
打印带颜色的文本
Last updated
Was this helpful?