diff --git a/.gitignore b/.gitignore index da4f18b4..714fa7e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -.vscode +.idea +.vscode .github img/*.html notebook +venv diff --git a/README.md b/README.md index 1ca7f6b2..91ab7d1f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +
@@ -7,1712 +8,295 @@

-
- +
## 介绍 -告别枯燥,告别枯燥,致力于打造 Python 经典小例子、小案例。 如果转载本库小例子、小案例,请备注下方链接:Python小例子 https://github.com/jackzhenguo/python-small-examples - -查看完整的旧版本:[改版前V3.0](V3.md) - -## 贡献 - -欢迎贡献小例子到此库 +告别枯燥,告别枯燥,致力于打造 Python 经典小例子、小案例。 ## License -允许按照要求转载,但禁止用于任何商用目的。 - -## Python 原创教程 -这是经过很久打磨的一个Python教程,全部是个人原创,已首发在公众号,并且托管在我的[个人网站](http://www.zglg.work/python-level/)。想系统入门Python的欢迎学习: - -[Python进阶完整教程](http://www.zglg.work/python-level/) - -![](./img/大纲.png) - -适合小白的 Python 系统入门课程 - -- [0 Python引言](http://www.zglg.work/Python-20-topics/intro-python/) -- [1 数字专题](http://www.zglg.work/Python-20-topics/python-number/) -- [2 字符串专题](http://www.zglg.work/?page_id=540) -- 3 列表专题 - - [3.1 列表基础](http://www.zglg.work/?page_id=563) - - [3.2 列表进阶](http://www.zglg.work/?page_id=575) -- 4 流程控制专题 - - [4.1 流程控制基础](http://www.zglg.work/?page_id=618) - - [4.2 流程控制进阶](http://www.zglg.work/?page_id=621) -- 5 编程风格 - - [5.1 基本编程习惯](http://www.zglg.work/?page_id=654) - - [5.2 EAFP 防御编程风格](http://www.zglg.work/?page_id=657) - - [5.3 LBYL 防御编程风格](http://www.zglg.work/?page_id=659) -- 6 Python 函数 - - [6.1 函数组成](http://www.zglg.work/?page_id=883) - - [6.2 引用传参](http://www.zglg.work/?page_id=885) - - [6.3 默认参数与关键字参数](http://www.zglg.work/?page_id=887) - - [6.4 可变参数](http://www.zglg.work/?page_id=889) - - [6.6 偏函数](http://www.zglg.work/?page_id=892) - - [6.7 递归函数](http://www.zglg.work/?page_id=894) - - [6.8 匿名函数](http://www.zglg.work/?page_id=896) - - [6.9 高阶函数](http://www.zglg.work/?page_id=898) - - [6.10 嵌套函数](http://www.zglg.work/?page_id=900) -- 7 面向对象编程基础 - - [7.1 类定义](http://www.zglg.work/?page_id=904) - - [7.2 对象或实例](http://www.zglg.work/?page_id=906) - - [7.3 打印对象](http://www.zglg.work/?page_id=908) - - [7.4 属性](http://www.zglg.work/?page_id=910) - - [7.5 private,protected,public](http://www.zglg.work/?page_id=913) - - [7.6 继承](http://www.zglg.work/?page_id=916) - - [7.7 多态](http://www.zglg.work/?page_id=918) - -后续章节正在整理推送中。 - - - -## Python 小例子 - -| 小例子 | 链接 | 标签 | 版本 | 难度 | -| ---- | ---------------------------------- | ---- | ---- | ---- | -| 1 | [实现 relu](md/1.md) | max | V4.0 | ⭐️⭐️ | -| 2 | [进制转化](md/2.md) | bin,oct,hex | V4.0 | ⭐️⭐️ | -| 3 | [整数和ASCII互转](md/3.md) | chr,ord | V1.0 | ⭐️⭐️ | -| 4 | [元素都为真检查](md/4.md) | all | V2.0 | ⭐️⭐️⭐️ | -| 5 | [至少一个为真检查](md/5.md) | any | V2.0 | ⭐️⭐️⭐️ | -| 6 | [判断是真是假](md/6.md) | bool | V2.0 | ⭐️⭐️⭐️ | -| 7 | [创建复数](md/7.md) | complex | V1.0 | ⭐️⭐️⭐️ | -| 8 | [取商和余数](md/8.md) | divmod | V1.0 | ⭐️⭐️ | -| 9 | [转为浮点类型](md/9.md) | float | V1.0 | ⭐️⭐️ | -| 10 | [转为整型](md/10.md) | int | V1.0 | ⭐️ | -| 11 | [次幂](md/11.md) | pow | V1.0 | ⭐️ | -| 12 | [四舍五入](md/12.md) | round | V1.0 | ⭐️ | -| 13 | [链式比较](md/13.md) | compare | V1.0 | ⭐️⭐️ | -| 14 | [字符串转字节](md/14.md) | bytes,utf-8 | V1.0 | ⭐️⭐️ | -| 15 | [任意对象转为字符串](md/15.md) | str | V1.0 | ⭐️⭐️ | -| 16 | [执行字符串表示的代码](md/16.md) | compile | V1.0 | ⭐️⭐️⭐️ | -| 17 | [计算表达式](md/17.md) | eval | V1.0 | ⭐️⭐️⭐️⭐️ | -| 18 | [字符串格式化](md/18.md) | format | V1.0 | ⭐️⭐️⭐️⭐️ | -| 19 | [拿来就用的排序函数](md/19.md) | sorted | V1.0 | ⭐️⭐️⭐️ | -| 20 | [求和函数](md/20.md) | sum | V1.0 | ⭐️⭐️ | -| 21 | [nonlocal用于内嵌函数中](md/21.md) | nonlocal | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 22 | [global 声明全局变量](md/22.md) | global | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 23 | [交换两元素](md/23.md) | pack,unpack | V1.0 | ⭐️⭐️ | -| 24 | [操作函数对象](md/24.md) | operator | V2.0 | ⭐️⭐️⭐️⭐️ | -| 25 | [生成逆序序列](md/25.md) | range | V1.0 | ⭐️⭐️ | -| 26 | [函数的五类参数使用例子](md/26.md) | variable parameter | V2.0 | ⭐️⭐️⭐️⭐️ | -| 27 | [使用slice对象](md/27.md) | slice | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 28 | [lambda 函数](md/28.md) | lambda | V3.0 | ⭐️⭐️⭐️⭐️ | -| 29 | [转为字典](md/29.md) | dict | V1.0 | ⭐️⭐️ | -| 30 | [冻结集合](md/30.md) | frozenset | V1.0 | ⭐️⭐️ | -| 31 | [转为集合类型](md/31.md) | set | V1.0 | ⭐️⭐️ | -| 32 | [转元组](md/32.md) | tuple | V1.0 | ⭐️⭐️ | -| 33 | [对象是否可调用](md/33.md) | callable | V2.5 | ⭐️⭐️⭐️⭐️ | -| 34 | [ascii 展示对象](md/34.md) | `__repr__` | V2.5 | ⭐️⭐️⭐️ | -| 35 | [类方法](md/35.md) | classmethod | V1.5 | ⭐️⭐️⭐️ | -| 36 | [动态删除属性](md/36.md) | delattr,hasattr | V1.5 | ⭐️⭐️ | -| 37 | [一键查看对象所有方法](md/37.md) | dir | V1.5 | ⭐️⭐️ | -| 38 | [动态获取对象属性](md/38.md) | getattr | V1.5 | ⭐️⭐️ | -| 39 | [对象是否有某个属性](md/39.md) | hasattr | V1.5 | ⭐️⭐️⭐️ | -| 40 | [对象门牌号](md/40.md) | id | V1.0 | ⭐️ | -| 41 | [实例和对象关系判断](md/41.md) | isinstance | V1.5 | ⭐️⭐️⭐️ | -| 42 | [issubclass父子关系鉴定](md/42.md) | issubclass | V1.5 | ⭐️⭐️⭐️ | -| 43 | [所有对象之根](md/43.md) | object | V1.0 | ⭐️ | -| 44 | [创建属性的两种方法](md/44.md) | property | V2.5 | ⭐️⭐️⭐️⭐️⭐️ | -| 45 | [查看对象类型](md/45.md) | type | V1.0 | ⭐️ | -| 46 | [元类使用介绍](md/46.md) | type,`__class__` | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 47 | [枚举对象](md/47.md) | enumerate | V1.0 | ⭐️⭐️⭐️ | -| 48 | [查看变量所占字节数](md/48.md) | getsizeof | V1.0 | ⭐️⭐️⭐️ | -| 49 | [过滤器filter](md/49.md) | filter | V1.5 | ⭐️⭐️⭐️ | -| 50 | [返回对象哈希值](md/50.md) | hash | V1.0 | ⭐️⭐️ | -| 51 | [help 一键帮助](md/51.md) | help | V1.0 | ⭐️ | -| 52 | [获取用户输入](md/52.md) | input | V1.0 | ⭐️ | -| 53 | [创建迭代器](md/53.md) | iter,`__iter__` | V1.5 | ⭐️⭐️⭐️ | -| 54 | [文件读写和mode 取值表](md/54.md) | open,read,write,with,mode | V2.0 | ⭐️⭐️⭐️ | -| 55 | [创建range序列](md/55.md) | range | V1.0 | ⭐️⭐️ | -| 56 | [反向迭代器reversed](md/56.md) | reversed | V1.0 | ⭐️⭐️ | -| 57 | [zip迭代器](md/57.md) | zip | V1.5 | ⭐️⭐️⭐️ | -| 58 | [operator使用举例](md/58.md) | operator | V1.0 | ⭐️⭐️⭐️⭐️ | -| 59 | [传输json对象](md/59.md) | json | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 60 | [不用else和if实现计算器](md/60.md) | operator | V1.0 | ⭐️⭐️⭐️ | -| 61 | [去最求平均](md/61.md) | list,sort,round | V1.0 | ⭐️⭐️⭐️⭐️ | -| 62 | [打印99乘法表](md/62.md) | for,range,format | V1.0 | ⭐️⭐️⭐️ | -| 63 | [递归版flatten函数](md/63.md) | recursion,list,isinstance | V1.0 | ⭐️⭐️⭐️⭐️ | -| 64 | [列表等分为n份](md/64.md) | list,ceil | V1.0 | ⭐️⭐️⭐️ | -| 65 | [压缩列表](md/65.md) | list,filter | V1.0 | ⭐️⭐️⭐️⭐️ | -| 66 | [求更长的列表](md/66.md) | max,lambda | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 67 | [求列表众数](md/67.md) | max,lambda,count | V1.0 | ⭐️⭐️⭐️⭐️ | -| 68 | [所有多个列表的最大值](md/68.md) | max,lambda | V1.0 | ⭐️⭐️⭐️⭐️ | -| 69 | [列表检查重复](md/69.md) | set | V1.0 | ⭐️⭐️⭐️ | -| 70 | [一行代码实现列表反转](md/70.md) | reverse | V1.0 | ⭐️⭐️ | -| 71 | [浮点数等差数列](md/71.md) | range,float | V1.0 | ⭐️⭐️⭐️⭐️ | -| 72 | [按条件分组](md/72.md) | lambda | V1.0 | ⭐️⭐️⭐️⭐️ | -| 73 | [map实现向量运算](md/73.md) | map,lambda | V1.0 | ⭐️⭐️⭐️ | -| 74 | [值最大的字典](md/74.md) | max,lambda | V1.0 | ⭐️⭐️⭐️⭐️ | -| 75 | [合并两个字典](md/75.md) | ** | V1.0 | ⭐️⭐️⭐️ | -| 76 | [Topn 字典](md/76.md) | heapq,nlargest | V1.0 | ⭐️⭐️⭐️ | -| 77 | [判断是否为异位词](md/77.md) | collections,Counter | V1.0 | ⭐️⭐️⭐️ | -| 78 | [逻辑上合并字典](md/78.md) | ChainMap | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 79 | [带名字的元组](md/79.md) | namedtuple | V1.0 | ⭐️⭐️⭐️ | -| 80 | [sample 样本抽样](md/80.md) | random,sample | V1.0 | ⭐️⭐️⭐️ | -| 81 | [重洗数据集](md/81.md) | shuffle | V1.0 | ⭐️⭐️⭐️ | -| 82 | [10个均匀分布的坐标点](md/82.md) | random,uniform | V1.0 | ⭐️⭐️⭐️ | -| 83 | [10个高斯分布的坐标点](md/83.md) | random,gauss | V1.0 | ⭐️⭐️⭐️⭐️ | -| 84 | [chain串联小容器为大容器](md/84.md) | itertools,chain | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 85 | [product 使用案例](md/85.md) | product | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 86 | [反转字符串的两个方法](md/86.md) | reversed | V1.0 | ⭐️⭐️ | -| 87 | [join 串联字符串](md/87.md) | join | V1.0 | ⭐️⭐️ | -| 88 | [字符串字节长度](md/88.md) | encode | V1.0 | ⭐️⭐️ | -| 89 | [正则中字符 `r`作用](md/89.md) | re,r | V3.0 | ⭐️⭐️⭐️ | -| 90 | [正则原子操作](md/90.md) | re | V3.0 | ⭐️⭐️⭐️ | -| 91 | [正则中的转义](md/91.md) | re,\ | V3.0 | ⭐️⭐️⭐️ | -| 92 | [正则最普通查找](md/92.md) | re,findall | V3.0 | ⭐️⭐️⭐️ | -| 93 | [使用通用字符查找](md/93.md) | re,\s,\w,\d | V3.0 | ⭐️⭐️⭐️ | -| 94 | [使用元字符查找](md/94.md) | re,+,* | V3.0 | ⭐️⭐️⭐️ | -| 95 | [捕获子串](md/95.md) | () | V3.0 | ⭐️⭐️⭐️⭐️ | -| 96 | [贪心捕获和非贪心捕获](md/96.md) | re | V1.0 | ⭐️⭐️⭐️⭐️ | -| 97 | [使用正则做密码安全检查](md/97.md) | re | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 98 | [爬取百度首页标题](md/98.md) | re | V1.0 | ⭐️⭐️⭐️⭐️ | -| 99 | [批量转化为驼峰格式(Camel)](md/99.md) | re | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 100 | [是否互为排序词](md/100.md) | collections,defaultdict | V1.0 | ⭐️⭐️⭐️⭐️ | -| 101 | [str1是否由str2旋转而来](md/101.md) | str | V1.0 | ⭐️⭐️⭐️ | -| 102 | [使用正则判断是否为正浮点数](md/102.md) | str,re,float | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 103 | [获取文件后缀名](md/103.md) | os,splitext | V1.0 | ⭐️⭐️ | -| 104 | [获取路径中的文件名](md/104.md) | os,split | V1.0 | ⭐️⭐️ | -| 105 | [批量修改文件后缀](md/105.md) | argparse,listdir | V1.0 | ⭐️⭐️⭐️⭐️ | -| 106 | [xls批量转换成xlsx](md/106.md) | os,listdir,splitext | V1.0 | ⭐️⭐️⭐️⭐️ | -| 107 | [获取指定后缀名的文件](md/107.md) | os,listdir,splitext | V1.0 | ⭐️⭐️⭐️⭐️ | -| 108 | [批量压缩文件](md/108.md) | zipfile | V1.0 | ⭐️⭐️⭐️⭐️ | -| 109 | [32位加密](md/109.md) | hashlib | V1.0 | ⭐️⭐️⭐️⭐️ | -| 110 | [年的日历图](md/110.md) | calendar | V1.0 | ⭐️⭐️ | -| 111 | [判断是否为闰年](md/111.md) | calendar | V1.0 | ⭐️⭐️⭐️ | -| 112 | [判断月有几天](md/112.md) | calendar,datetime | V1.0 | ⭐️⭐️⭐️ | -| 113 | [月的第一天](md/113.md) | datetime | V1.0 | ⭐️⭐️ | -| 114 | [月的最后一天](md/114.md) | calendar,datetime | V1.0 | ⭐️⭐️ | -| 115 | [获取当前时间](md/115.md) | time,datetime | V1.0 | ⭐️⭐️ | -| 116 | [字符时间转时间](md/116.md) | time,datetime | V1.0 | ⭐️⭐️ | -| 117 | [时间转字符时间](md/117.md) | time,datetime | V1.0 | ⭐️⭐️ | -| 118 | [默认启动主线程](md/118.md) | threading | V1.0 | ⭐️⭐️ | -| 119 | [创建线程](md/119.md) | threading | V1.0 | ⭐️⭐️ | -| 120 | [交替获得CPU时间片](md/120.md) | threading | V1.0 | ⭐️⭐️⭐️ | -| 121 | [多线程抢夺同一个变量](md/121.md) | threading | V1.0 | ⭐️⭐️⭐️ | -| 122 | [多线程变量竞争引起的问题](md/122.md) | threading | V1.0 | ⭐️⭐️⭐️ | -| 123 | [多线程锁](md/123.md) | threading,lock | V1.0 | ⭐️⭐️⭐️ | -| 124 | [时间转数组及常用格式](md/124.md) | time,datetime,format | V1.0 | ⭐️⭐️⭐️ | -| 125 | [寻找第n次出现位置](md/125.md) | enumerator | V1.0 | ⭐️⭐️⭐️ | -| 126 | [斐波那契数列前n项](md/126.md) | yield,range | V1.0 | ⭐️⭐️⭐️ | -| 127 | [找出所有重复元素](md/127.md) | calendar,datetime | V1.0 | ⭐️⭐️⭐️⭐️ | -| 128 | [联合统计次数](md/128.md) | Counter | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 129 | [groupby单字段分组](md/129.md) | itertools, groupby,lambda | V1.0 | ⭐️⭐️⭐️ | -| 130 | [groupby多字段分组](md/130.md) | itemgetter,itertools,groupby | V1.0 | ⭐️⭐️⭐️⭐️ | -| 131 | [itemgetter和key函数](md/131.md) | operator,itemgetter,itertools | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 132 | [sum函数计算和聚合同时做](md/132.md) | sum,generator | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -| 133 | [获得某天后的1~n天](md/133.md) | Calendar,monthrange | V4.0 | ⭐️⭐️⭐️ | -| 134 | [list分组(生成器版)](md/134.md) | yield,generator | V1.0 | ⭐️⭐️⭐️ | -| 135 | [列表全展开(生成器版)](md/135.md) | list,yield,generator | V1.0 | ⭐️⭐️⭐️ | -| 136 | [测试函数运行时间的装饰器](md/136.md) | decorator | V1.0 | ⭐️⭐️⭐️⭐️ | -| 137 | [统计异常次数装饰器](md/137.md) | decorator,nonlocal | V1.5 | ⭐️⭐️⭐️⭐️ | -| 138 | [通俗理解装饰器](md/138.md) | decorator | V1.0 | ⭐️⭐️⭐️ | -| 139 | [定制递减迭代器](md/139.md) | Iterator | V3.0 | ⭐️⭐️⭐️⭐️ | -| 140 | [turtle绘制奥运五环图](md/140.md) | turtle | V1.0 | ⭐️⭐️⭐️ | -| 141 | [turtle绘制漫天雪花](md/141.md) | turtle | V1.0 | ⭐️⭐️⭐️ | -| 142 | [Python词云图](md/142.md) | WordCloud | V1.0 | ⭐️⭐️⭐ | -| 143 | [Plotly柱状图和折线图](md/143.md) | plotly | V1.0 | ⭐️⭐ | -| 144 | [seaborn热力图](md/144.md) | seaborn | V1.0 | ⭐️⭐ | -| 145 | [Pyecharts仪表盘](md/145.md) | pyecharts | V1.0 | ⭐️⭐ | -| 146 | [Pyecharts漏斗图](md/146.md) | pyecharts | V1.0 | ⭐️⭐ | -| 147 | [Pyecharts水球图](md/147.md) | pyecharts | V1.0 | ⭐️⭐ | -| 148 | [Pyecharts饼图](md/148.md) | pyecharts | V1.0 | ⭐️⭐ | -| 149 | [Pyecharts极坐标图](md/149.md) | pyecharts | V1.0 | ⭐️⭐ | -| 150 | [Pyecharts词云图](md/150.md) | pyecharts | V1.0 | ⭐️⭐ | -| 151 | [Pyecharts热力图](md/151.md) | pyecharts | V1.0 | ⭐️⭐ | -| 152 | [matplotlib绘制动图](md/152.md) | matplotlib | V1.0 | ⭐️⭐ | -| 153 | [seaborn pairplot图](md/153.md) | seaborn | V1.0 | ⭐️⭐⭐⭐ | -| 154 | [含单个元素的元组](md/154.md) | tuple | V1.0 | ⭐️⭐ | -| 155 | [默认参数设为空](md/155.md) | function | V1.0 | ⭐️⭐⭐ | -| 156 | [共享变量未绑定之坑](md/156.md) | global | V1.0 | ⭐️⭐⭐ | -| 157 | [lambda自由参数之坑](md/157.md) | lambda | V1.0 | ⭐️⭐⭐ | -| 158 | [各种参数使用之坑](md/158.md) | function paremeter | V1.0 | ⭐️⭐⭐ | -| 159 | [列表删除之坑](md/159.md) | list | V1.0 | ⭐️⭐ | -| 160 | [列表快速复制之坑](md/160.md) | list | V1.0 | ⭐️⭐⭐ | -| 161 | [字符串驻留](md/161.md) | str | V1.0 | ⭐️⭐⭐⭐⭐ | -| 162 | [相同值的不可变对象](md/162.md) | mutable | V1.0 | ⭐️⭐⭐ | -| 163 | [对象销毁顺序](md/163.md) | OOP del | V1.0 | ⭐️⭐⭐⭐ | -| 164 | [充分认识for](md/164.md) | for | V1.0 | ⭐️⭐⭐ | -| 165 | [认识执行时机](md/165.md) | generator | V1.0 | ⭐️⭐⭐⭐⭐ | -| 166 | [创建空集合错误](md/166.md) | set | V1.0 | ⭐️⭐ | -| 167 | [pyecharts传入Numpy数据绘图失败](md/167.md) | numpy pyecharts | V1.0 | ⭐️⭐⭐ | -| 168 | [优化代码异常输出包](md/168.md) | debugger | V1.0 | ⭐️⭐⭐ | -| 169 | [图像处理包pillow](md/169.md) | pillow | V1.0 | ⭐️⭐⭐ | -| 170 | [一行代码找到编码](md/170.md) | chardet | V1.0 | ⭐️⭐⭐ | -| 171 | [子类继承父类的静态方法吗?](md/171.md) | staticmethod | V1.0 | ⭐️⭐⭐ | -| 172 | [NumPy 的pad填充方法](md/172.md) | NumPy pad | V1.0 | ⭐️⭐⭐⭐ | -| 173 | [创建下对角线为1、2、3、4的对角矩阵](md/173.md) | NumPy diag | V1.0 | ⭐️⭐⭐ | -| 174 | [cut 数据分箱](md/174.md) | Pandas cut | v1.0 | ⭐️⭐⭐ | -| 175 | [丢弃空值和填充空值](./md/175.md) | Pandas dropna fillna | v1.0 | ⭐️⭐⭐ | -| 176 | [一行代码让 pip 安装加速 100 倍](md/176.md) | pip install | v1.0 | ⭐️⭐⭐ | -| 177 | [数据分析神器:deepnote](./md/177.md) | deepnote | v1.0 | ⭐️⭐⭐ | -| 178 | [apply 方法去掉特殊字符](./md/178.md) | pandas apply | v1.0 | ⭐️⭐⭐ | -| 179 | [使用map对列做特征工程](./md/179.md) | pandas map | v1.0 | ⭐️⭐⭐ | -| 180 | [category列转数值](./md/180.md) | pandas category | v1.0 | ⭐️⭐⭐ | -| 181 | [rank排名](./md/181.md) | pandas rank | v1.0 | ⭐️⭐⭐| -| 182 | [完成数据下采样,调整步长由小时为天](./md/182.md) | pandas resample | v1.0 | ⭐️⭐⭐ | -| 183 | [如何用 Pandas 快速生成时间序列数据](./md/183.md) | pandas util | v1.0 | ⭐️⭐⭐ | -| 184 | [如何快速找出 DataFrame 所有列 null 值个数](./md/184.md) | pandas isnull sum | v1.0 | ⭐️⭐⭐ | -| 185 | [重新排序 DataFrame 的列](./md/185.md) | pandas dataframe | v1.0 | ⭐️⭐⭐ | -| 186 | [使用 count 统计词条 出现次数](./md/186.md) | pandas count | v1.0 | ⭐️⭐⭐ | -| 187 | [split 求时分(HH:mm)的分钟差](./md/187.md) | pandas split | v1.0 | ⭐️⭐⭐ | -| 188 | [melt透视数据小技巧](./md/188.md) | pandas melt | v1.0 | ⭐️⭐⭐ | -| 189 | [pivot 透视小技巧](./md/189.md) | pandas melt | v1.0 | ⭐️⭐⭐ | -| 190 | [p随机读取文件的K行,生成N个](./md/190.md) | pandas sample | v1.0 | ⭐️⭐⭐ | -| 191 | [格式化Pandas的时间列](md/191.md) | pandas apply | v1.0 | ⭐️⭐⭐⭐ | -| 192 | [创建SQLite连接](md/192.md) | SQLite | v1.0 | ⭐️⭐⭐⭐ | -| 193 | [json对象转python对象](md/193.md) | python json | v1.0 | ⭐️⭐⭐⭐ | -| 194 | [python对象转json对象](md/194.md) | python json | v1.0 | ⭐️⭐⭐⭐ | -| 195 | [发现列表前3个最大或最小数](md/195.md) | list heapq | v1.0 | ⭐️⭐⭐⭐ | -| 196 | [使用堆排序列表为升序](md/196.md) | sort heapq | v1.0 | ⭐️⭐⭐⭐ | -### Python 实战 - - -#### 221 自动群发邮件 - -Python自动群发邮件 - -```python -import smtplib -from email import (header) -from email.mime import (text, application, multipart) -import time - -def sender_mail(): - smt_p = smtplib.SMTP() - smt_p.connect(host='smtp.qq.com', port=25) - sender, password = '113097485@qq.com', "**************" - smt_p.login(sender, password) - receiver_addresses, count_num = [ - 'guozhennianhua@163.com', 'xiaoxiazi99@163.com'], 1 - for email_address in receiver_addresses: - try: - msg = multipart.MIMEMultipart() - msg['From'] = "zhenguo" - msg['To'] = email_address - msg['subject'] = header.Header('这是邮件主题通知', 'utf-8') - msg.attach(text.MIMEText( - '这是一封测试邮件,请勿回复本邮件~', 'plain', 'utf-8')) - smt_p.sendmail(sender, email_address, msg.as_string()) - time.sleep(10) - print('第%d次发送给%s' % (count_num, email_address)) - count_num = count_num + 1 - except Exception as e: - print('第%d次给%s发送邮件异常' % (count_num, email_address)) - continue - smt_p.quit() - -sender_mail() -``` - - - -注意: -发送邮箱是qq邮箱,所以要在qq邮箱中设置开启SMTP服务,设置完成时会生成一个授权码,将这个授权码赋值给文中的`password`变量 - -#### 222 二分搜索 - -二分搜索是程序员必备的算法,无论什么场合,都要非常熟练地写出来。 - -小例子描述: -在**有序数组**`arr`中,指定区间`[left,right]`范围内,查找元素`x` -如果不存在,返回`-1` - -二分搜索`binarySearch`实现的主逻辑 - -```python -def binarySearch(arr, left, right, x): - while left <= right: - - mid = int(left + (right - left) / 2); # 找到中间位置。求中点写成(left+right)/2更容易溢出,所以不建议这样写 - - # 检查x是否出现在位置mid - if arr[mid] == x: - print('found %d 在索引位置%d 处' %(x,mid)) - return mid - - # 假如x更大,则不可能出现在左半部分 - elif arr[mid] < x: - left = mid + 1 #搜索区间变为[mid+1,right] - print('区间缩小为[%d,%d]' %(mid+1,right)) - - # 同理,假如x更小,则不可能出现在右半部分 - elif x - -```python -import requests -from lxml import etree -import pandas as pd -import re - -url = 'http://www.weather.com.cn/weather1d/101010100.shtml#input' -with requests.get(url) as res: - content = res.content - html = etree.HTML(content) -``` - - - -通过lxml模块提取值 - -lxml比beautifulsoup解析在某些场合更高效 - -```python -location = html.xpath('//*[@id="around"]//a[@target="_blank"]/span/text()') -temperature = html.xpath('//*[@id="around"]/div/ul/li/a/i/text()') -``` - -结果: - -```python -['香河', '涿州', '唐山', '沧州', '天津', '廊坊', '太原', '石家庄', '涿鹿', '张家口', '保定', '三河', '北京孔庙', '北京国子监', '中国地质博物馆', '月坛公 -园', '明城墙遗址公园', '北京市规划展览馆', '什刹海', '南锣鼓巷', '天坛公园', '北海公园', '景山公园', '北京海洋馆'] - -['11/-5°C', '14/-5°C', '12/-6°C', '12/-5°C', '11/-1°C', '11/-5°C', '8/-7°C', '13/-2°C', '8/-6°C', '5/-9°C', '14/-6°C', '11/-4°C', '13/-3°C' -, '13/-3°C', '12/-3°C', '12/-3°C', '13/-3°C', '12/-2°C', '12/-3°C', '13/-3°C', '12/-2°C', '12/-2°C', '12/-2°C', '12/-3°C'] -``` - - -构造DataFrame对象 - -```python -df = pd.DataFrame({'location':location, 'temperature':temperature}) -print('温度列') -print(df['temperature']) -``` - -正则解析温度值 - -```python -df['high'] = df['temperature'].apply(lambda x: int(re.match('(-?[0-9]*?)/-?[0-9]*?°C', x).group(1) ) ) -df['low'] = df['temperature'].apply(lambda x: int(re.match('-?[0-9]*?/(-?[0-9]*?)°C', x).group(1) ) ) -print(df) -``` - -详细说明子字符创捕获 - -除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用`()`表示的就是要提取的分组(group)。比如:`^(\d{3})-(\d{3,8})$`分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码 - -```python -m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345') -print(m.group(0)) -print(m.group(1)) -print(m.group(2)) - -# 010-12345 -# 010 -# 12345 -``` - -如果正则表达式中定义了组,就可以在`Match`对象上用`group()`方法提取出子串来。 - -注意到`group(0)`永远是原始字符串,`group(1)`、`group(2)`……表示第1、2、……个子串。 - - -最终结果 - -```kepython -Name: temperature, dtype: object - location temperature high low -0 香河 11/-5°C 11 -5 -1 涿州 14/-5°C 14 -5 -2 唐山 12/-6°C 12 -6 -3 沧州 12/-5°C 12 -5 -4 天津 11/-1°C 11 -1 -5 廊坊 11/-5°C 11 -5 -6 太原 8/-7°C 8 -7 -7 石家庄 13/-2°C 13 -2 -8 涿鹿 8/-6°C 8 -6 -9 张家口 5/-9°C 5 -9 -10 保定 14/-6°C 14 -6 -11 三河 11/-4°C 11 -4 -12 北京孔庙 13/-3°C 13 -3 -13 北京国子监 13/-3°C 13 -3 -14 中国地质博物馆 12/-3°C 12 -3 -15 月坛公园 12/-3°C 12 -3 -16 明城墙遗址公园 13/-3°C 13 -3 -17 北京市规划展览馆 12/-2°C 12 -2 -18 什刹海 12/-3°C 12 -3 -19 南锣鼓巷 13/-3°C 13 -3 -20 天坛公园 12/-2°C 12 -2 -21 北海公园 12/-2°C 12 -2 -22 景山公园 12/-2°C 12 -2 -23 北京海洋馆 12/-3°C 12 -3 -``` - -### 十、数据分析 - -本项目基于Kaggle电影影评数据集,通过这个系列,你将学到如何进行数据探索性分析(EDA),学会使用数据分析利器`pandas`,会用绘图包`pyecharts`,以及EDA时可能遇到的各种实际问题及一些处理技巧。 - - - -本项目需要导入的包: - -```python -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -from pyecharts.charts import Bar,Grid,Line -import pyecharts.options as opts -from pyecharts.globals import ThemeType -``` - -#### 1 创建DataFrame -pandas中一个dataFrame实例: -```python -Out[89]: - a val -0 apple1 1.0 -1 apple2 2.0 -2 apple3 3.0 -3 apple4 4.0 -4 apple5 5.0 -``` - -我们的**目标**是变为如下结构: -```python -a apple1 apple2 apple3 apple4 apple5 -0 1.0 2.0 3.0 4.0 5.0 -``` - -乍看可使用`pivot`,但很难一步到位。 - -所以另辟蹊径,提供一种简单且好理解的方法: - -```python -In [113]: pd.DataFrame(index=[0],columns=df.a,data=dict(zip(df.a,df.val))) -Out[113]: -a apple1 apple2 apple3 apple4 apple5 -0 1.0 2.0 3.0 4.0 5.0 -``` -以上方法是重新创建一个DataFrame,直接把`df.a`所有可能取值作为新dataframe的列,index调整为`[0]`,注意类型必须是数组类型(array-like 或者 Index),两个轴确定后,`data`填充数据域。 - -```python -In [116]: dict(zip(df.a,df.val)) -Out[116]: {'apple1': 1.0, 'apple2': 2.0, 'apple3': 3.0, 'apple4': 4.0, 'apple5': 5.0} -``` - - - -#### 2 导入数据 -数据来自kaggle,共包括三个文件: - -1. movies.dat -2. ratings.dat -3. users.dat - -`movies.dat`包括三个字段:['Movie ID', 'Movie Title', 'Genre'] - -使用pandas导入此文件: - -```python -import pandas as pd - -movies = pd.read_csv('./data/movietweetings/movies.dat', delimiter='::', engine='python', header=None, names = ['Movie ID', 'Movie Title', 'Genre']) -``` - -导入后,显示前5行: - -```python - Movie ID Movie Title \ -0 8 Edison Kinetoscopic Record of a Sneeze (1894) -1 10 La sortie des usines Lumi猫re (1895) -2 12 The Arrival of a Train (1896) -3 25 The Oxford and Cambridge University Boat Race ... -4 91 Le manoir du diable (1896) -5 131 Une nuit terrible (1896) -6 417 Le voyage dans la lune (1902) -7 439 The Great Train Robbery (1903) -8 443 Hiawatha, the Messiah of the Ojibway (1903) -9 628 The Adventures of Dollie (1908) - Genre -0 Documentary|Short -1 Documentary|Short -2 Documentary|Short -3 NaN -4 Short|Horror -5 Short|Comedy|Horror -6 Short|Action|Adventure|Comedy|Fantasy|Sci-Fi -7 Short|Action|Crime|Western -8 NaN -9 Action|Short -``` - - - -次导入其他两个数据文件 - -`users.dat`: - -```python -users = pd.read_csv('./data/movietweetings/users.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Twitter ID']) -print(users.head()) -``` - -结果: - -```python - User ID Twitter ID -0 1 397291295 -1 2 40501255 -2 3 417333257 -3 4 138805259 -4 5 2452094989 -5 6 391774225 -6 7 47317010 -7 8 84541461 -8 9 2445803544 -9 10 995885060 -``` - - - -`rating.data`: - -```python -ratings = pd.read_csv('./data/movietweetings/ratings.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Movie ID', 'Rating', 'Rating Timestamp']) -print(ratings.head()) -``` - -结果: - -```python - User ID Movie ID Rating Rating Timestamp -0 1 111161 10 1373234211 -1 1 117060 7 1373415231 -2 1 120755 6 1373424360 -3 1 317919 6 1373495763 -4 1 454876 10 1373621125 -5 1 790724 8 1374641320 -6 1 882977 8 1372898763 -7 1 1229238 9 1373506523 -8 1 1288558 5 1373154354 -9 1 1300854 8 1377165712 -``` - - **read_csv 使用说明** - -说明,本次导入`dat`文件使用`pandas.read_csv`函数。 - -第一个位置参数`./data/movietweetings/ratings.dat` 表示文件的相对路径 - -第二个关键字参数:`delimiter='::'`,表示文件分隔符使用`::` - -后面几个关键字参数分别代表使用的引擎,文件没有表头,所以`header`为`None;` - -导入后dataframe的列名使用`names`关键字设置,这个参数大家可以记住,比较有用。 - - - -Kaggle电影数据集第一节,我们使用数据处理利器 `pandas`, 函数`read_csv` 导入给定的三个数据文件。 - -```python -import pandas as pd - -movies = pd.read_csv('./data/movietweetings/movies.dat', delimiter='::', engine='python', header=None, names = ['Movie ID', 'Movie Title', 'Genre']) -users = pd.read_csv('./data/movietweetings/users.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Twitter ID']) -ratings = pd.read_csv('./data/movietweetings/ratings.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Movie ID', 'Rating', 'Rating Timestamp']) -``` - -用到的`read_csv`,某些重要的参数,如何使用在上一节也有所提到。下面开始数据探索分析(EDA) - -> 找出得分前10喜剧(comedy) - - - -#### 3 处理组合值 - -表`movies`字段`Genre`表示电影的类型,可能有多个值,分隔符为`|`,取值也可能为`None`. - -针对这类字段取值,可使用Pandas中Series提供的`str`做一步转化,**注意它是向量级的**,下一步,如Python原生的`str`类似,使用`contains`判断是否含有`comedy`字符串: - -```python -mask = movies.Genre.str.contains('comedy',case=False,na=False) -``` - -注意使用的两个参数:`case`, `na` - -case为 False,表示对大小写不敏感; -na Genre列某个单元格为`NaN`时,我们使用的充填值,此处填充为`False` - -返回的`mask`是一维的`Series`,结构与 movies.Genre相同,取值为True 或 False. - -观察结果: - -```python -0 False -1 False -2 False -3 False -4 False -5 True -6 True -7 False -8 False -9 False -Name: Genre, dtype: bool - -``` - - - #### 4 访问某列 - -得到掩码mask后,pandas非常方便地能提取出目标记录: - -```python -comedy = movies[mask] -comdey_ids = comedy['Movie ID'] - -``` - -以上,在pandas中被最频率使用,不再解释。看结果`comedy_ids.head()`: - -```python -5 131 -6 417 -15 2354 -18 3863 -19 4099 -20 4100 -21 4101 -22 4210 -23 4395 -25 4518 -Name: Movie ID, dtype: int64 - -``` - - - -1-4介绍`数据读入`,`处理组合值`,`索引数据`等, pandas中使用较多的函数,基于Kaggle真实电影影评数据集,最后得到所有`喜剧 ID`: - -```python -5 131 -6 417 -15 2354 -18 3863 -19 4099 -20 4100 -21 4101 -22 4210 -23 4395 -25 4518 -Name: Movie ID, dtype: int64 - -``` - -下面继续数据探索之旅~ - -#### 5 连接两个表 - -拿到所有喜剧的ID后,要想找出其中平均得分最高的前10喜剧,需要关联另一张表:`ratings`: - -再回顾下ratings表结构: - -```python - User ID Movie ID Rating Rating Timestamp -0 1 111161 10 1373234211 -1 1 117060 7 1373415231 -2 1 120755 6 1373424360 -3 1 317919 6 1373495763 -4 1 454876 10 1373621125 -5 1 790724 8 1374641320 -6 1 882977 8 1372898763 -7 1 1229238 9 1373506523 -8 1 1288558 5 1373154354 -9 1 1300854 8 1377165712 - -``` - - -pandas 中使用`join`关联两张表,连接字段是`Movie ID`,如果顺其自然这么使用`join`: - -```python -combine = ratings.join(comedy, on='Movie ID', rsuffix='2') - -``` - -左右滑动,查看完整代码 - -大家可验证这种写法,仔细一看,会发现结果非常诡异。 - -究其原因,这是pandas join函数使用的一个算是坑点,它在官档中介绍,连接右表时,此处右表是`comedy`,它的`index`要求是连接字段,也就是 `Movie ID`. - -左表的index不要求,但是要在参数 `on`中给定。 - -**以上是要注意的一点** - -修改为: - -```python -combine = ratings.join(comedy.set_index('Movie ID'), on='Movie ID') -print(combine.head(10)) - -``` - -以上是OK的写法 - -观察结果: - -```python - User ID Movie ID Rating Rating Timestamp Movie Title Genre -0 1 111161 10 1373234211 NaN NaN -1 1 117060 7 1373415231 NaN NaN -2 1 120755 6 1373424360 NaN NaN -3 1 317919 6 1373495763 NaN NaN -4 1 454876 10 1373621125 NaN NaN -5 1 790724 8 1374641320 NaN NaN -6 1 882977 8 1372898763 NaN NaN -7 1 1229238 9 1373506523 NaN NaN -8 1 1288558 5 1373154354 NaN NaN -9 1 1300854 8 1377165712 NaN NaN - -``` - -Genre列为`NaN`表明,这不是喜剧。需要筛选出此列不为`NaN` 的记录。 - -#### 6 按列筛选 - -pandas最方便的地方,就是向量化运算,尽可能减少了for循环的嵌套。 - -按列筛选这种常见需求,自然可以轻松应对。 - -为了照顾初次接触 pandas 的朋友,分两步去写: - -```python -mask = pd.notnull(combine['Genre']) - -``` - -结果是一列只含`True 或 False`的值 - -```python -result = combine[mask] -print(result.head()) - -``` - -结果中,Genre字段中至少含有一个Comedy字符串,表明验证了我们以上操作是OK的。 - -```python - User ID Movie ID Rating Rating Timestamp Movie Title \ -12 1 1588173 9 1372821281 Warm Bodies (2013) -13 1 1711425 3 1372604878 21 & Over (2013) -14 1 2024432 8 1372703553 Identity Thief (2013) -17 1 2101441 1 1372633473 Spring Breakers (2012) -28 2 1431045 7 1457733508 Deadpool (2016) - - Genre -12 Comedy|Horror|Romance -13 Comedy -14 Adventure|Comedy|Crime|Drama -17 Comedy|Crime|Drama -28 Action|Adventure|Comedy|Sci-Fi - - -``` - - - -截止目前已经求出所有喜剧电影`result`,前5行如下,Genre中都含有`Comedy`字符串: -```python - User ID Movie ID Rating Rating Timestamp Movie Title \ -12 1 1588173 9 1372821281 Warm Bodies (2013) -13 1 1711425 3 1372604878 21 & Over (2013) -14 1 2024432 8 1372703553 Identity Thief (2013) -17 1 2101441 1 1372633473 Spring Breakers (2012) -28 2 1431045 7 1457733508 Deadpool (2016) - - Genre -12 Comedy|Horror|Romance -13 Comedy -14 Adventure|Comedy|Crime|Drama -17 Comedy|Crime|Drama -28 Action|Adventure|Comedy|Sci-Fi -``` - +允许按照要求转载,但禁止用于任何商用目的。如果转载本库小例子、小案例,请备注下方链接: +[Python小例子所有汇总](https://ai-jupyter.com/python-small-examples/) -#### 7 按照Movie ID 分组 +### 更多教程 -result中会有很多观众对同一部电影的打分,所以要求得分前10的喜剧,先按照`Movie ID`分组,然后求出平均值: -```python -score_as_movie = result.groupby('Movie ID').mean() -``` +[AI消息](https://ai-jupyter.com/) -前5行显示如下: -```python - User ID Rating Rating Timestamp -Movie ID -131 34861.000000 7.0 1.540639e+09 -417 34121.409091 8.5 1.458680e+09 -2354 6264.000000 8.0 1.456343e+09 -3863 43803.000000 10.0 1.430439e+09 -4099 25084.500000 7.0 1.450323e+09 -``` +[AI新闻报道](https://ai-jupyter.com/ai-news-all/) -#### 8 按照电影得分排序 +[AI大模型](https://ai-jupyter.com/ai-llm/) -```python -score_as_movie.sort_values(by='Rating', ascending = False,inplace=True) -score_as_movie -``` -前5行显示如下: -```python - User ID Rating Rating Timestamp -Movie ID -7134690 30110.0 10.0 1.524974e+09 -416889 1319.0 10.0 1.543320e+09 -57840 23589.0 10.0 1.396802e+09 -5693562 50266.0 10.0 1.511024e+09 -5074 43803.0 10.0 1.428352e+09 -``` -都是满分?这有点奇怪,会不会这些电影都只有几个人评分,甚至只有1个?评分样本个数太少,显然最终的平均分数不具有太强的说服力。 +[AI工具集](https://ai-jupyter.com/ai-chatgpt/) -所以,下面要进行每部电影的评分人数统计 +[ChatGPT4o免费使用六种方法](https://ai-jupyter.com/ai-chatgpt/) -#### 9 分组后使用聚合函数 +[Python教程](https://ai-jupyter.com/python-packages/) -根据`Movie ID`分组后,使用`count`函数统计`每组个数`,只保留count列,最后得到`watchs2`: +[数据分析教程](https://ai-jupyter.com/numpy-intro/) -```python -watchs = result.groupby('Movie ID').agg(['count']) -watchs2 = watchs['Rating']['count'] -``` -打印前20行: -```python -print(watchs2.head(20)) -``` -结果: -```python -Movie ID -131 1 -417 22 -2354 1 -3863 1 -4099 2 -4100 1 -4101 1 -4210 1 -4395 1 -4518 1 -4546 2 -4936 2 -5074 1 -5571 1 -6177 1 -6414 3 -6684 1 -6689 1 -7145 1 -7162 2 -Name: count, dtype: int64 -``` -果然,竟然有这么多电影的评论数只有1次!样本个数太少,评论的平均值也就没有什么说服力。 +[算法教程](https://ai-jupyter.com/algorithm-basic/) -查看`watchs2`一些重要统计量: -```python -watchs2.describe() -``` -结果: -```python -count 10740.000000 -mean 20.192086 -std 86.251411 -min 1.000000 -25% 1.000000 -50% 2.000000 -75% 7.000000 -max 1843.000000 -Name: count, dtype: float64 -``` -共有10740部**喜剧**电影被评分,平均打分次数20次,标准差86,75%的电影样本打分次数小于7次,最小1次,最多1843次。 +[AI教程](https://ai-jupyter.com/statistics/) -#### 10 频率分布直方图 +[Git教程](https://ai-jupyter.com/git/) -绘制评论数的频率分布直方图,便于更直观的观察电影被评论的分布情况。上面分析到,75%的电影打分次数小于7次,所以绘制打分次数小于20次的直方图: +[程序员](https://ai-jupyter.com/others/) -```python -fig = plt.figure(figsize=(12,8)) -histn = plt.hist(watchs2[watchs2 <=19],19,histtype='step') -plt.scatter([i+1 for i in range(len(histn[0]))],histn[0]) -``` +[资料下载](https://ai-jupyter.com/python-20/) -![](./img/20200131094927.jpg) -`histn`元祖表示个数和对应的被分割的区间,查看`histn[0]`: -```python -array([4383., 1507., 787., 541., 356., 279., 209., 163., 158., - 118., 114., 90., 104., 81., 80., 73., 62., 65., - 52.]) -``` -```python -sum(histn[0]) # 9222 -``` -看到电影评论次数1到19次的喜剧电影9222部,共有10740部喜剧电影,大约`86%`的喜剧电影评论次数`小于20次`,有`1518`部电影评论数不小于20次。 - -我们肯定希望挑选出被评论次数尽可能多的电影,因为难免会有水军和滥竽充数等`异常评论`行为。那么,如何准确的量化最小抽样量呢? - - - -#### 11 最小抽样量 - -根据统计学的知识,最小抽样量和Z值、样本方差和样本误差相关,下面给出具体的求解最小样本量的计算方法。 - -采用如下计算公式: - -$$ n = \frac{Z^2\sigma^2}{E^2} $$ - - -此处,$Z$ 值取为95%的置信度对应的Z值也就是1.96,样本误差取为均值的2.5%. - -根据以上公式,编写下面代码: - -```python -n3 = result.groupby('Movie ID').agg(['count','mean','std']) -n3r = n3[n3['Rating']['count']>=20]['Rating'] -``` -只计算影评超过20次,且满足最小样本量的电影。计算得到的`n3r`前5行: -```python - count mean std -Movie ID -417 22 8.500000 1.263027 -12349 68 8.485294 1.227698 -15324 20 8.350000 1.039990 -15864 51 8.431373 1.374844 -17925 44 8.636364 1.259216 -``` -进一步求出最小样本量: -```python -nmin = (1.96**2*n3r['std']**2) / ( (n3r['mean']*0.025)**2 ) -``` -`nmin`前5行: -```python -Movie ID -417 135.712480 -12349 128.671290 -15324 95.349276 -15864 163.434005 -17925 130.668350 -``` - -筛选出满足最小抽样量的喜剧电影: - -```python -n3s = n3r[ n3r['count'] >= nmin ] -``` -结果显示如下,因此共有`173`部电影满足最小样本抽样量。 - -```python - -count mean std -Movie ID -53604 129 8.635659 1.230714 -57012 207 8.449275 1.537899 -70735 224 8.839286 1.190799 -75686 209 8.095694 1.358885 -88763 296 8.945946 1.026984 -... ... ... ... -6320628 860 7.966279 1.469924 -6412452 276 7.510870 1.389529 -6662050 22 10.000000 0.000000 -6966692 907 8.673649 1.286455 -7131622 1102 7.851180 1.751500 -173 rows × 3 columns -``` - -#### 12 去重和连表 - -按照平均得分从大到小排序: -```python -n3s_sort = n3s.sort_values(by='mean',ascending=False) -``` -结果: -```python - count mean std -Movie ID -6662050 22 10.000000 0.000000 -4921860 48 10.000000 0.000000 -5262972 28 10.000000 0.000000 -5512872 353 9.985836 0.266123 -3863552 199 9.010050 1.163372 -... ... ... ... -1291150 647 6.327666 1.785968 -2557490 546 6.307692 1.858434 -1478839 120 6.200000 0.728761 -2177771 485 6.150515 1.523922 -1951261 1091 6.083410 1.736127 -173 rows × 3 columns -``` -仅靠`Movie ID`还是不知道哪些电影,连接`movies`表: -```python -ms = movies.drop_duplicates(subset=['Movie ID']) -ms = ms.set_index('Movie ID') -n3s_final = n3s_drops.join(ms,on='Movie ID') -``` - -#### 13 结果分析 - -喜剧榜单前50名: -```python -Movie Title -Five Minutes (2017) -MSG 2 the Messenger (2015) -Avengers: Age of Ultron Parody (2015) -Be Somebody (2016) -Bajrangi Bhaijaan (2015) -Back to the Future (1985) -La vita 鐚?bella (1997) -The Intouchables (2011) -The Sting (1973) -Coco (2017) -Toy Story 3 (2010) -3 Idiots (2009) -Green Book (2018) -Dead Poets Society (1989) -The Apartment (1960) -P.K. (2014) -The Truman Show (1998) -Am鑼卨ie (2001) -Inside Out (2015) -Toy Story 4 (2019) -Toy Story (1995) -Finding Nemo (2003) -Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb (1964) -Home Alone (1990) -Zootopia (2016) -Up (2009) -Monsters, Inc. (2001) -La La Land (2016) -Relatos salvajes (2014) -En man som heter Ove (2015) -Snatch (2000) -Lock, Stock and Two Smoking Barrels (1998) -How to Train Your Dragon 2 (2014) -As Good as It Gets (1997) -Guardians of the Galaxy (2014) -The Grand Budapest Hotel (2014) -Fantastic Mr. Fox (2009) -Silver Linings Playbook (2012) -Sing Street (2016) -Deadpool (2016) -Annie Hall (1977) -Pride (2014) -In Bruges (2008) -Big Hero 6 (2014) -Groundhog Day (1993) -The Breakfast Club (1985) -Little Miss Sunshine (2006) -Deadpool 2 (2018) -The Terminal (2004) -``` - -前10名评论数图: - -![](./img/2020013109495711.jpg) - -代码: -```python -x = n3s_final['Movie Title'][:10].tolist()[::-1] -y = n3s_final['count'][:10].tolist()[::-1] -bar = ( - Bar() - .add_xaxis(x) - .add_yaxis('评论数',y,category_gap='50%') - .reversal_axis() - .set_global_opts(title_opts=opts.TitleOpts(title="喜剧电影被评论次数"), - toolbox_opts=opts.ToolboxOpts(),) -) -grid = ( - Grid(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) - .add(bar, grid_opts=opts.GridOpts(pos_left="30%")) -) -grid.render_notebook() -``` - -前10名得分图: - -![](./img/2020013109500812.jpg) - -代码: -```python -x = n3s_final['Movie Title'][:10].tolist()[::-1] -y = n3s_final['mean'][:10].round(3).tolist()[::-1] -bar = ( - Bar() - .add_xaxis(x) - .add_yaxis('平均得分',y,category_gap='50%') - .reversal_axis() - .set_global_opts(title_opts=opts.TitleOpts(title="喜剧电影平均得分"), - xaxis_opts=opts.AxisOpts(min_=8.0,name='平均得分'), - toolbox_opts=opts.ToolboxOpts(),) -) -grid = ( - Grid(init_opts=opts.InitOpts(theme=ThemeType.MACARONS)) - .add(bar, grid_opts=opts.GridOpts(pos_left="30%")) -) -grid.render_notebook() -``` - - - -#### 14 生成哑变量 - -分类变量的数值化,是指将枚举类变量转化为indicator变量或称dummy变量。 - -那么什么是`indicator变量`,看看如下例子,A变量解析为:`[1,0,0]`, B解析为:`[0,1,0]`, C解析为:`[0,0,1]` -```python -In [8]: s = pd.Series(list('ABCA')) -In [9]: pd.get_dummies(s) -Out[9]: - A B C -0 1 0 0 -1 0 1 0 -2 0 0 1 -3 1 0 0 -``` - -如果输入的字符有4个唯一值,看到字符a被解析为[1,0,0,0],向量长度为4. - -```python -In [5]: s = pd.Series(list('abaccd')) -In [6]: pd.get_dummies(s) -Out[6]: - a b c d -0 1 0 0 0 -1 0 1 0 0 -2 1 0 0 0 -3 0 0 1 0 -4 0 0 1 0 -5 0 0 0 1 -``` - -也就是说dummy向量的长度等于输入字符串中,唯一字符的个数。 - -#### 15 讨厌的SettingWithCopyWarning!!! - -Pandas 处理数据,太好用了,谁用谁知道! - -使用过 Pandas 的,几乎都会遇到一个警告: - -*SettingWithCopyWarning* - -非常烦人! - -尤其是刚接触 Pandas 的,完全不理解为什么弹出这么一串: - -```python -d:\source\test\settingwithcopy.py:9: SettingWithCopyWarning: -A value is trying to be set on a copy of a slice from a DataFrame. -Try using .loc[row_indexer,col_indexer] = value instead - -See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy -``` - -归根结底,是因为代码中出现`链式操作`... - -有人就问了,什么是`链式操作`? - -这样的: - -```python -tmp = df[df.a<4] -tmp['c'] = 200 -``` - -先记住这个最典型的情况,即可! - -有的人就问了:出现这个 Warning, 需要理会它吗? - -如果结果不对,当然要理会;如果结果对,不care. - -举个例子~~ - -```python -import pandas as pd - -df = pd.DataFrame({'a':[1,3,5],'b':[4,2,7]},index=['a','b','c']) -df.loc[df.a<4,'c'] = 100 -print(df) -print('it\'s ok') - -tmp = df[df.a<4] -tmp['c'] = 200 -print('-----tmp------') -print(tmp) -print('-----df-------') -print(df) -``` - -输出结果: -```python - a b c -a 1 4 100.0 -b 3 2 100.0 -c 5 7 NaN -it's ok -d:\source\test\settingwithcopy.py:9: SettingWithCopyWarning: -A value is trying to be set on a copy of a slice from a DataFrame. -Try using .loc[row_indexer,col_indexer] = value instead - -See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy - tmp['c'] = 200 ------tmp------ - a b c -a 1 4 200 -b 3 2 200 ------df------- - a b c -a 1 4 100.0 -b 3 2 100.0 -c 5 7 NaN -``` - -it's ok 行后面的发生链式赋值,导致结果错误。因为 tmp 变了,df 没赋上值啊,所以必须理会。 - -it's ok 行前的是正解。 - -以上,链式操作尽量避免,如何避免?多使用 `.loc[row_indexer,col_indexer]`,提示告诉我们的~ - -#### 16 NumPy 数据归一化、分布可视化 - -仅使用 `NumPy`,下载数据,归一化,使用 `seaborn` 展示数据分布。 - -**下载数据** - -```python -import numpy as np - -url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data' -wid = np.genfromtxt(url, delimiter=',', dtype='float', usecols=[1]) -``` -仅提取 `iris` 数据集的第二列 `usecols = [1]` - -**展示数据** - -```python -array([3.5, 3. , 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3. , - 3. , 4. , 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3. , - 3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.1, 3. , - 3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3. , 3.8, 3.2, 3.7, 3.3, 3.2, 3.2, - 3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2. , 3. , 2.2, 2.9, 2.9, - 3.1, 3. , 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3. , 2.8, 3. , - 2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3. , 3.4, 3.1, 2.3, 3. , 2.5, 2.6, - 3. , 2.6, 2.3, 2.7, 3. , 2.9, 2.9, 2.5, 2.8, 3.3, 2.7, 3. , 2.9, - 3. , 3. , 2.5, 2.9, 2.5, 3.6, 3.2, 2.7, 3. , 2.5, 2.8, 3.2, 3. , - 3.8, 2.6, 2.2, 3.2, 2.8, 2.8, 2.7, 3.3, 3.2, 2.8, 3. , 2.8, 3. , - 2.8, 3.8, 2.8, 2.8, 2.6, 3. , 3.4, 3.1, 3. , 3.1, 3.1, 3.1, 2.7, - 3.2, 3.3, 3. , 2.5, 3. , 3.4, 3. ]) - -``` - -这是单变量(univariate)长度为 150 的一维 NumPy 数组。 - -**归一化** - -求出最大值、最小值 -```python -smax = np.max(wid) -smin = np.min(wid) - -In [51]: smax,smin -Out[51]: (4.4, 2.0) -```` -归一化公式: -```python -s = (wid - smin) / (smax - smin) -``` -只打印小数点后三位设置: -```python -np.set_printoptions(precision=3) -``` - -归一化结果: -```markdown -array([0.625, 0.417, 0.5 , 0.458, 0.667, 0.792, 0.583, 0.583, 0.375, - 0.458, 0.708, 0.583, 0.417, 0.417, 0.833, 1. , 0.792, 0.625, - 0.75 , 0.75 , 0.583, 0.708, 0.667, 0.542, 0.583, 0.417, 0.583, - 0.625, 0.583, 0.5 , 0.458, 0.583, 0.875, 0.917, 0.458, 0.5 , - 0.625, 0.458, 0.417, 0.583, 0.625, 0.125, 0.5 , 0.625, 0.75 , - 0.417, 0.75 , 0.5 , 0.708, 0.542, 0.5 , 0.5 , 0.458, 0.125, - 0.333, 0.333, 0.542, 0.167, 0.375, 0.292, 0. , 0.417, 0.083, - 0.375, 0.375, 0.458, 0.417, 0.292, 0.083, 0.208, 0.5 , 0.333, - 0.208, 0.333, 0.375, 0.417, 0.333, 0.417, 0.375, 0.25 , 0.167, - 0.167, 0.292, 0.292, 0.417, 0.583, 0.458, 0.125, 0.417, 0.208, - 0.25 , 0.417, 0.25 , 0.125, 0.292, 0.417, 0.375, 0.375, 0.208, - 0.333, 0.542, 0.292, 0.417, 0.375, 0.417, 0.417, 0.208, 0.375, - 0.208, 0.667, 0.5 , 0.292, 0.417, 0.208, 0.333, 0.5 , 0.417, - 0.75 , 0.25 , 0.083, 0.5 , 0.333, 0.333, 0.292, 0.542, 0.5 , - 0.333, 0.417, 0.333, 0.417, 0.333, 0.75 , 0.333, 0.333, 0.25 , - 0.417, 0.583, 0.458, 0.417, 0.458, 0.458, 0.458, 0.292, 0.5 , - 0.542, 0.417, 0.208, 0.417, 0.583, 0.417]) -``` - -**分布可视化** - -```python -import seaborn as sns -sns.distplot(s,kde=False,rug=True) -``` -频率分布直方图: - - -![](https://imgkr.cn-bj.ufileos.com/49bf5190-429c-4172-a53c-e3f6b66d4e64.png) - - -```python -sns.distplot(s,hist=True,kde=True,rug=True) -``` -带高斯密度核函数的直方图: - -![](https://imgkr.cn-bj.ufileos.com/4e4a72a5-8f59-4893-b435-e4b57e22a18e.png) - - - -**分布 fit 图** - -拿 `gamma` 分布去 fit : -```python -from scipy import stats -sns.distplot(s, kde=False, fit = stats.gamma) -``` - - - -![](https://imgkr.cn-bj.ufileos.com/89446755-7420-4f96-97fe-c4e45d0d3dec.png) - - -拿双 `gamma` 去 fit: -```python -from scipy import stats -sns.distplot(s, kde=False, fit = stats.dgamma) -``` - -![](https://imgkr.cn-bj.ufileos.com/f2c2a660-5433-4b4f-ad7b-d01da4121319.png) - -#### 17 Pandas 使用技巧 - -对于动辄就几十或几百个 G 的数据,在读取的这么大数据的时候,我们有没有办法随机选取一小部分数据,然后读入内存,快速了解数据和开展 EDA ? - -使用 Pandas 的 skiprows 和 概率知识,就能做到。 - -下面解释具体怎么做。 - -如下所示,读取某 100 G 大小的 big_data.csv 数据 - -1) 使用 skiprows 参数, - -2) x > 0 确保首行读入, - -3) np.random.rand() > 0.01 表示 99% 的数据都会被随机过滤掉 - -言外之意,只有全部数据的 1% 才有机会选入内存中。 - -```python -import pandas as pd -import numpy as np - -df = pd.read_csv("big_data.csv", -skiprows = -lambda x: x>0 and np.random.rand() > 0.01) - -print("The shape of the df is {}. -It has been reduced 100 times!".format(df.shape)) -``` - -使用这种方法,读取的数据量迅速缩减到原来的 1% ,对于迅速展开数据分析有一定的帮助。 - -### 十一、一步一步掌握Flask web开发 - -#### 1 Flask版 hello world - -Flask是Python轻量级web框架,容易上手,被广大Python开发者所喜爱。 - -今天我们先从hello world开始,一步一步掌握Flask web开发。例子君是Flask框架的小白,接下来与读者朋友们,一起学习这个对我而言的新框架,大家多多指导。 - -首先`pip install Flask`,安装Flask,然后import Flask,同时创建一个 `app` -```python -from flask import Flask - -App = Flask(__name__) -``` - -写一个index页的入口函数,返回hello world. - -通过装饰器:App.route('/')创建index页的路由或地址,一个`/`表示index页,也就是主页。 - -```python -@App.route('/') -def index(): - return "hello world" -``` - -调用 `index`函数: -```python -if __name__ == "__main__": - App.run(debug=True) -``` - -然后启动,会在console下看到如下启动信息,表明`服务启动成功`。 -```python -* Debug mode: on - * Restarting with stat - * Debugger is active! - * Debugger PIN: 663-788-611 - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) -``` - - 接下来,打开一个网页,相当于启动客户端,并在Url栏中输入:`http://127.0.0.1:5000/`,看到页面上答应出`hello world`,证明服务访问成功。 - - 同时在服务端后台看到如下信息,表示处理一次来自客户端的`get`请求。 - ```python - 27.0.0.1 - - [03/Feb/2020 21:26:50] "GET / HTTP/1.1" 200 - - ``` - - 以上就是flask的hello world 版 - -#### 2 Flask之数据入库操作 - -数据持久化就是将数据写入到数据库存储的过程。 - -本例子使用`sqlite3`数据库。 - -1)导入`sqlite3`,未安装前使用命令`pip install sqlite3` - -创建一个`py`文件:`sqlite3_started.py`,并写下第一行代码: -```python -import sqlite3 -``` -2)手动创建一个数据库实例`db`, 命名`test.db` - -3)创建与数据库实例`test.db`的连接: -```python -conn = sqlite3.connect("test.db") -``` - -4)拿到连接`conn`的cursor -```python -c = conn.cursor() -``` - -5)创建第一张表`books` - -共有四个字段:`id`,`sort`,`name`,`price`,类型分别为:`int`,`int`,`text`,`real`. 其中`id`为`primary key`. 主键的取值必须是唯一的(`unique`),否则会报错。 - - -```python -c.execute('''CREATE TABLE books - (id int primary key, - sort int, - name text, - price real)''') -``` -第一次执行上面语句,表`books`创建完成。当再次执行时,就会报`重复建表`的错误。需要优化脚本,检查表是否存在`IF NOT EXISTS books`,不存在再创建: -```python -c.execute('''CREATE TABLE IF NOT EXISTS books - (id int primary key, - sort int, - name text, - price real)''') -``` - -6)插入一行记录 - -共为4个字段赋值 - -```python -c.execute('''INSERT INTO books VALUES - (1, - 1, - 'computer science', - 39.0)''') -``` - -7)一次插入多行记录 - -先创建一个list:`books`,使用`executemany`一次插入多行。 -```python -books = [(2, 2, 'Cook book', 68), - (3, 2, 'Python intro', 89), - (4, 3, 'machine learning', 59), - ] - - -c.executemany('INSERT INTO books VALUES (?, ?, ?, ?)', books) -``` - -8)提交 - -提交后才会真正生效,写入到数据库 - -```python -conn.commit() -``` - -9)关闭期初建立的连接conn - -务必记住手动关闭,否则会出现内存泄漏 -```python -conn.close() -print('Done') -``` - -10)查看结果 -例子君使用`vs code`,在扩展库中选择:`SQLite`安装。 - -![image-20200208211721377](./img/image-20200208211721377.png) - -新建一个`sq`文件:`a.sql`,内容如下: - -```sql -SELECT * from books -``` -右键`run query`,得到表`books`插入的4行记录可视化图: - -![image-20200208211806853](./img/image-20200208211806853.png) - -以上十步就是sqlite3写入数据库的主要步骤,作为Flask系列的第二篇,为后面的前端讲解打下基础。 - -#### 3 Flask各层调用关系 - -这篇介绍Flask和B/S模式,即浏览器/服务器模式,是接下来快速理解Flask代码的关键理论篇:**理解Views、models和渲染模板层的调用关系**。 - -1) 发出请求 - -当我们在浏览器地址栏中输入某个地址,按回车后,完成第一步。 - -2) 视图层 views接收1)步发出的请求,Flask中使用解释器的方式处理这个求情,实例代码如下,它通常涉及到调用models层和模板文件层 - -```python -@main_blue.route('/', methods=['GET', 'POST']) -def index(): - form = TestForm() - print('test') -``` - -3) models层会负责创建数据模型,执行CRUD操作 - -4) 模板文件层处理html模板 - -5) 组合后返回html - -6) models层和html模板组合后返回给views层 - -7)最后views层响应并渲染到浏览器页面,我们就能看到请求的页面。 - -完整过程图如下所示: - -![image-20200211152007983](./img/web1.png) - -读者朋友们,如果你和例子君一样都是初学Flask编程,需要好好理解上面的过程。理解这些对于接下来的编程会有一定的理论指导,方向性指导价值。 - -### Python 问答 - -#### Python 如何生成二维码? - - - - - -## qrcode - -今天先来解答如何生成二维码。Python的`qrcode`包支持生成二维码。 - -用法也很简单: - -```python -import qrcode - -# 二维码内容 -data = "http://www.zglg.work/wp-content/uploads/2020/10/image-3.png" -# 生成二维码 -img = qrcode.make(data=data) -# 直接显示二维码 -img.show() -# 保存二维码为文件 -img.save("我的微信.jpg") -``` - -生成的二维码如下: - -![](https://imgkr2.cn-bj.ufileos.com/f0b08c53-0107-483b-bbe5-072bebc58e8d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=rVtaeBWhzLPPq%252BFCVtiOv6rS0tI%253D&Expires=1603544615) - - -大家微信扫描后,会出现我的二维码。 +## Python 小例子 -另外,还可以设置二维码的颜色等样式: +### 基本操作 -```python -import qrcode +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +|1 | [常见算术运算](md/198.md)| 运算 | v1| ⭐⭐ | +| 2 | [实现 relu](md/1.md) | max | V4.0 | ⭐️⭐️ | +| 3 | [进制转化](md/2.md) | bin,oct,hex | V4.0 | ⭐️⭐️| +| 4 | [整数和ASCII互转](md/3.md) | chr,ord | V1.0 | ⭐️⭐️ | +| 5 | [元素都为真检查](md/4.md) | all | V2.0 | ⭐️⭐️⭐️ | +| 6 | [至少一个为真检查](md/5.md) | any | V2.0 | ⭐️⭐️⭐️ | +| 7 | [判断是真是假](md/6.md) | bool | V2.0 | ⭐️⭐️⭐️ | +| 8 | [创建复数](md/7.md) | complex | V1.0 | ⭐️⭐️⭐️ | +| 9 | [取商和余数](md/8.md) | divmod | V1.0 | ⭐️⭐️ | +| 10 | [转为浮点类型](md/9.md) | float | V1.0 | ⭐️⭐️ | +| 11 | [转为整型](md/10.md) | int | V1.0 | ⭐️ | +| 12 | [次幂](md/11.md) | pow | V1.0 | ⭐️ | +| 13 | [四舍五入](md/12.md) | round | V1.0 | ⭐️ | +| 14 | [链式比较](md/13.md) | compare | V1.0 | ⭐️⭐️ | +| 15 | [字符串转字节](md/14.md) | bytes,utf-8 | V1.0 | ⭐️⭐️ | +| 16 | [任意对象转为字符串](md/15.md) | str | V1.0 | ⭐️⭐️ | +| 17 | [执行字符串表示的代码](md/16.md) | compile | V1.0 | ⭐️⭐️⭐️ | +| 18 | [计算表达式](md/17.md) | eval | V1.0 | ⭐️⭐️⭐️⭐️ | +| 19 | [字符串格式化](md/18.md) | format | V1.0 | ⭐️⭐️⭐️⭐️ | +| 20 | [交换两元素](md/23.md) | pack,unpack | V1.0 | ⭐️⭐️ | +| 21 | [转为字典](md/29.md) | dict | V1.0 | ⭐️⭐️ | +| 22 | [冻结集合](md/30.md) | frozenset | V1.0 | ⭐️⭐️ | +| 23 | [转为集合类型](md/31.md) | set | V1.0 | ⭐️⭐️ | +| 24 | [转元组](md/32.md) | tuple | V1.0 | ⭐️⭐️ | +| 25 | [查看变量所占字节数](md/48.md) | getsizeof | V1.0 | ⭐️⭐️⭐️ | +| 26 | [含单个元素的元组](md/154.md) | tuple | V1.0 | ⭐️⭐ | +| 27 | [列表删除之坑](md/159.md) | list | V1.0 | ⭐️⭐ | +| 28 | [列表快速复制之坑](md/160.md) | list | V1.0 | ⭐️⭐⭐ | +| 29 | [发现列表前3个最大或最小数](md/195.md) | list heapq | v1.0 | ⭐️⭐⭐⭐ | +| 30 | [字符串驻留](md/161.md) | str | V1.0 | ⭐️⭐⭐⭐⭐ | +| 31 | [创建空集合错误](md/166.md) | set | V1.0 | ⭐️⭐ | +| 32 | [充分认识for](md/164.md) | for | V1.0 | ⭐️⭐⭐ | +| 33 | [认识执行时机](md/165.md) | generator | V1.0 | ⭐️⭐⭐⭐⭐ | + + +### 函数和模块常见用法 -# 实例化二维码生成类 -qr = qrcode.QRCode(border=2) -# 设置二维码数据 -data = "http://www.zglg.work/wp-content/uploads/2020/10/image-3.png" -qr.add_data(data=data) -# 启用二维码颜色设置 -qr.make(fit=True) -img = qr.make_image(fill_color="orange", back_color="white") +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [操作函数对象](md/24.md) | operator | V2.0 | ⭐️⭐️⭐️⭐️ | +| 2 | [创建range序列](md/55.md) | range | V1.0 | ⭐️⭐️ | +| 3 | [生成逆序序列](md/25.md) | range | V1.0 | ⭐️⭐️ | +| 4 | [拿来就用的排序函数](md/19.md) | sorted | V1.0 | ⭐️⭐️⭐️ | +| 5 | [求和函数](md/20.md) | sum | V1.0 | ⭐️⭐️ | +| 6 | [函数的五类参数使用例子](md/26.md) | variable parameter | V2.0 | ⭐️⭐️⭐️⭐️ | +| 7 | [使用slice对象](md/27.md) | slice | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 8 | [lambda 函数](md/28.md) | lambda | V3.0 | ⭐️⭐️⭐️⭐️ | +| 9 | [枚举对象](md/47.md) | enumerate | V1.0 | ⭐️⭐️⭐️ | +| 10 | [过滤器filter](md/49.md) | filter | V1.5 | ⭐️⭐️⭐️ | +| 11 | [返回对象哈希值](md/50.md) | hash | V1.0 | ⭐️⭐️ | +| 12 | [带名字的元组](md/79.md) | namedtuple | V1.0 | ⭐️⭐️⭐️ | +| 13 | [一行代码实现列表反转](md/70.md) | reverse | V1.0 | ⭐️⭐️ | +| 14 | [反转字符串的两个方法](md/86.md) | reversed | V1.0 | ⭐️⭐️ | +| 15 | [join 串联字符串](md/87.md) | join | V1.0 | ⭐️⭐️ | +| 16 | [字符串字节长度](md/88.md) | encode | V1.0 | ⭐️⭐️ | +| 17 | [groupby单字段分组](md/129.md) | itertools, groupby,lambda | V1.0 | ⭐️⭐️⭐️ | +| 18 | [groupby多字段分组](md/130.md) | itemgetter,itertools,groupby | V1.0 | ⭐️⭐️⭐️⭐️ | +| 19 | [itemgetter和key函数](md/131.md) | operator,itemgetter,itertools | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 20 | [sum函数计算和聚合同时做](md/132.md) | sum,generator | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 21 | [默认参数设为空](md/155.md) | function | V1.0 | ⭐️⭐⭐ | +| 22 | [各种参数使用之坑](md/158.md) | function paremeter | V1.0 | ⭐️⭐⭐ | +| 23 | [lambda自由参数之坑](md/157.md) | lambda | V1.0 | ⭐️⭐⭐ | +| 24 | [使用堆升序列表](md/196.md) | sort heapq | v1.0 | ⭐️⭐⭐⭐ | + + +### 面向对象 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [所有对象之根](md/43.md) | object | V1.0 | ⭐️ | +| 2 | [对象是否可调用](md/33.md) | callable | V2.5 | ⭐️⭐️⭐️⭐️ | +| 3 | [ascii 展示对象](md/34.md) | `__repr__` | V2.5 | ⭐️⭐️⭐️ | +| 4 | [类方法](md/35.md) | classmethod | V1.5 | ⭐️⭐️⭐️ | +| 5 | [动态删除属性](md/36.md) | delattr,hasattr | V1.5 | ⭐️⭐️ | +| 6 | [一键查看对象所有方法](md/37.md) | dir | V1.5 | ⭐️⭐️ | +| 7 | [动态获取对象属性](md/38.md) | getattr | V1.5 | ⭐️⭐️ | +| 8 | [对象是否有某个属性](md/39.md) | hasattr | V1.5 | ⭐️⭐️⭐️ | +| 9 | [对象门牌号](md/40.md) | id | V1.0 | ⭐️ | +| 10 | [实例和对象关系判断](md/41.md) | isinstance | V1.5 | ⭐️⭐️⭐️ | +| 11 | [issubclass父子关系鉴定](md/42.md) | issubclass | V1.5 | ⭐️⭐️⭐️ | +| 12 | [创建属性的两种方法](md/44.md) | property | V2.5 | ⭐️⭐️⭐️⭐️⭐️ | +| 13 | [查看对象类型](md/45.md) | type | V1.0 | ⭐️ | +| 14 | [元类使用介绍](md/46.md) | type,`__class__` | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 15 | [相同值的不可变对象](md/162.md) | mutable | V1.0 | ⭐️⭐⭐ | +| 16 | [对象销毁顺序](md/163.md) | OOP del | V1.0 | ⭐️⭐⭐⭐ | +| 17 | [子类继承父类的静态方法吗?](md/171.md) | staticmethod | V1.0 | ⭐️⭐⭐ | + + + +### 正则 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [正则中字符 `r`作用](md/89.md) | re,r | V3.0 | ⭐️⭐️⭐️ | +| 2 | [正则原子操作](md/90.md) | re | V3.0 | ⭐️⭐️⭐️ | +| 3 | [正则中的转义](md/91.md) | re,\ | V3.0 | ⭐️⭐️⭐️ | +| 4 | [正则最普通查找](md/92.md) | re,findall | V3.0 | ⭐️⭐️⭐️ | +| 5 | [使用通用字符查找](md/93.md) | re,\s,\w,\d | V3.0 | ⭐️⭐️⭐️ | +| 6 | [使用元字符查找](md/94.md) | re,+,* | V3.0 | ⭐️⭐️⭐️ | +| 7 | [捕获子串](md/95.md) | () | V3.0 | ⭐️⭐️⭐️⭐️ | +| 8 | [贪心捕获和非贪心捕获](md/96.md) | re | V1.0 | ⭐️⭐️⭐️⭐️ | +| 9 | [使用正则做密码安全检查](md/97.md) | re | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 10 | [爬取百度首页标题](md/98.md) | re | V1.0 | ⭐️⭐️⭐️⭐️ | +| 11 | [批量转化为驼峰格式(Camel)](md/99.md) | re | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 12 | [使用正则判断是否为正浮点数](md/102.md) | str,re,float | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 13 | [使用正则提取正整数和大于0的浮点数](md/197.md) | re findall | v2 | ⭐️⭐⭐⭐ | + +### 装饰器迭代器生成器 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [通俗理解装饰器](md/138.md) | decorator | V1.0 | ⭐️⭐️⭐️ | +| 2 | [测试函数运行时间的装饰器](md/136.md) | decorator | V1.0 | ⭐️⭐️⭐️⭐️ | +| 3 | [统计异常次数装饰器](md/137.md) | decorator,nonlocal | V1.5 | ⭐️⭐️⭐️⭐️ | +| 4 | [定制递减迭代器](md/139.md) | Iterator | V3.0 | ⭐️⭐️⭐️⭐️ | +| 5 | [创建迭代器](md/53.md) | iter,`__iter__` | V1.5 | ⭐️⭐️⭐️ | +| 6 | [反向迭代器reversed](md/56.md) | reversed | V1.0 | ⭐️⭐️ | +| 7 | [zip迭代器](md/57.md) | zip | V1.5 | ⭐️⭐️⭐️ | +| 8 | [list分组(生成器版)](md/134.md) | yield,generator | V1.0 | ⭐️⭐️⭐️ | +| 9 | [列表全展开(生成器版)](md/135.md) | list,yield,generator | V1.0 | ⭐️⭐️⭐️ | +| 10 | [chain串联小容器为大容器](md/84.md) | itertools,chain | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 11 | [product 使用案例](md/85.md) | product | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 12 | [斐波那契数列前n项](md/126.md) | yield,range | V1.0 | ⭐️⭐️⭐️ | + + +### 绘图 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [turtle绘制奥运五环图](md/140.md) | turtle | V1.0 | ⭐️⭐️⭐️ | +| 2 | [turtle绘制漫天雪花](md/141.md) | turtle | V1.0 | ⭐️⭐️⭐️ | +| 3 | [Python词云图](md/142.md) | WordCloud | V1.0 | ⭐️⭐️⭐ | +| 4 | [Plotly柱状图和折线图](md/143.md) | plotly | V1.0 | ⭐️⭐ | +| 5 | [seaborn热力图](md/144.md) | seaborn | V1.0 | ⭐️⭐ | +| 6 | [Pyecharts仪表盘](md/145.md) | pyecharts | V1.0 | ⭐️⭐ | +| 7 | [Pyecharts漏斗图](md/146.md) | pyecharts | V1.0 | ⭐️⭐ | +| 8 | [Pyecharts水球图](md/147.md) | pyecharts | V1.0 | ⭐️⭐ | +| 9 | [Pyecharts饼图](md/148.md) | pyecharts | V1.0 | ⭐️⭐ | +| 10 | [Pyecharts极坐标图](md/149.md) | pyecharts | V1.0 | ⭐️⭐ | +| 11 | [Pyecharts词云图](md/150.md) | pyecharts | V1.0 | ⭐️⭐ | +| 12 | [Pyecharts热力图](md/151.md) | pyecharts | V1.0 | ⭐️⭐ | +| 13 | [matplotlib绘制动图](md/152.md) | matplotlib | V1.0 | ⭐️⭐ | +| 14 | [seaborn pairplot图](md/153.md) | seaborn | V1.0 | ⭐️⭐⭐⭐ | +| 15 | [pyecharts传入Numpy数据绘图失败](md/167.md) | numpy pyecharts | V1.0 | ⭐️⭐⭐ | +| 16 | [图像处理包pillow](md/169.md) | pillow | V1.0 | ⭐️⭐⭐ | + +### 数据分析 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [数据分析神器:deepnote](./md/177.md) | deepnote | v1.0 | ⭐️⭐⭐ | +| 2 | [NumPy 的pad填充方法](md/172.md) | NumPy pad | V1.0 | ⭐️⭐⭐⭐ | +| 3 | [创建下对角线为1、2、3、4的对角矩阵](md/173.md) | NumPy diag | V1.0 | ⭐️⭐⭐ | +| 4 | [cut 数据分箱](md/174.md) | Pandas cut | v1.0 | ⭐️⭐⭐ | +| 5 | [丢弃空值和填充空值](./md/175.md) | Pandas dropna fillna | v1.0 | ⭐️⭐⭐ | +| 6 | [apply 方法去掉特殊字符](./md/178.md) | pandas apply | v1.0 | ⭐️⭐⭐ | +| 7 | [使用map对列做特征工程](./md/179.md) | pandas map | v1.0 | ⭐️⭐⭐ | +| 8 | [category列转数值](./md/180.md) | pandas category | v1.0 | ⭐️⭐⭐ | +| 9 | [rank排名](./md/181.md) | pandas rank | v1.0 | ⭐️⭐⭐| +| 10 | [完成数据下采样,调整步长由小时为天](./md/182.md) | pandas resample | v1.0 | ⭐️⭐⭐ | +| 11 | [如何用 Pandas 快速生成时间序列数据](./md/183.md) | pandas util | v1.0 | ⭐️⭐⭐ | +| 12 | [如何快速找出 DataFrame 所有列 null 值个数](./md/184.md) | pandas isnull sum | v1.0 | ⭐️⭐⭐ | +| 13 | [重新排序 DataFrame 的列](./md/185.md) | pandas dataframe | v1.0 | ⭐️⭐⭐ | +| 14 | [使用 count 统计词条 出现次数](./md/186.md) | pandas count | v1.0 | ⭐️⭐⭐ | +| 15 | [split 求时分(HH:mm)的分钟差](./md/187.md) | pandas split | v1.0 | ⭐️⭐⭐ | +| 16 | [melt透视数据小技巧](./md/188.md) | pandas melt | v1.0 | ⭐️⭐⭐ | +| 17 | [pivot 透视小技巧](./md/189.md) | pandas melt | v1.0 | ⭐️⭐⭐ | +| 18 | [p随机读取文件的K行,生成N个](./md/190.md) | pandas sample | v1.0 | ⭐️⭐⭐ | +| 19 | [格式化Pandas的时间列](md/191.md) | pandas apply | v1.0 | ⭐️⭐⭐⭐ | + +### 其他常用 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [help 一键帮助](md/51.md) | help | V1.0 | ⭐️ | +| 2 | [获取用户输入](md/52.md) | input | V1.0 | ⭐️ | +| 3 | [文件读写和mode 取值表](md/54.md) | open,read,write,with,mode | V2.0 | ⭐️⭐️⭐️ | +| 4 | [operator使用举例](md/58.md) | operator | V1.0 | ⭐️⭐️⭐️⭐️ | +| 5 | [传输json对象](md/59.md) | json | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 6 | [获取文件后缀名](md/103.md) | os,splitext | V1.0 | ⭐️⭐️ | +| 7 | [获取路径中的文件名](md/104.md) | os,split | V1.0 | ⭐️⭐️ | +| 8 | [批量修改文件后缀](md/105.md) | argparse,listdir | V1.0 | ⭐️⭐️⭐️⭐️ | +| 9 | [xls批量转换成xlsx](md/106.md) | os,listdir,splitext | V1.0 | ⭐️⭐️⭐️⭐️ | +| 10 | [获取指定后缀名的文件](md/107.md) | os,listdir,splitext | V1.0 | ⭐️⭐️⭐️⭐️ | +| 11 | [批量压缩文件](md/108.md) | zipfile | V1.0 | ⭐️⭐️⭐️⭐️ | +| 12 | [32位加密](md/109.md) | hashlib | V1.0 | ⭐️⭐️⭐️⭐️ | +| 13 | [年的日历图](md/110.md) | calendar | V1.0 | ⭐️⭐️ | +| 14 | [判断是否为闰年](md/111.md) | calendar | V1.0 | ⭐️⭐️⭐️ | +| 15 | [判断月有几天](md/112.md) | calendar,datetime | V1.0 | ⭐️⭐️⭐️ | +| 16 | [月的第一天](md/113.md) | datetime | V1.0 | ⭐️⭐️ | +| 17 | [月的最后一天](md/114.md) | calendar,datetime | V1.0 | ⭐️⭐️ | +| 18 | [获取当前时间](md/115.md) | time,datetime | V1.0 | ⭐️⭐️ | +| 19 | [字符时间转时间](md/116.md) | time,datetime | V1.0 | ⭐️⭐️ | +| 20 | [时间转字符时间](md/117.md) | time,datetime | V1.0 | ⭐️⭐️ | +| 21 | [获得某天后的1~n天](md/133.md) | Calendar,monthrange | V4.0 | ⭐️⭐️⭐️ | +| 22 | [默认启动主线程](md/118.md) | threading | V1.0 | ⭐️⭐️ | +| 23 | [创建线程](md/119.md) | threading | V1.0 | ⭐️⭐️ | +| 24 | [交替获得CPU时间片](md/120.md) | threading | V1.0 | ⭐️⭐️⭐️ | +| 25 | [多线程抢夺同一个变量](md/121.md) | threading | V1.0 | ⭐️⭐️⭐️ | +| 26 | [多线程变量竞争引起的问题](md/122.md) | threading | V1.0 | ⭐️⭐️⭐️ | +| 27 | [多线程锁](md/123.md) | threading,lock | V1.0 | ⭐️⭐️⭐️ | +| 28 | [时间转数组及常用格式](md/124.md) | time,datetime,format | V1.0 | ⭐️⭐️⭐️ | +| 29 | [nonlocal用于内嵌函数中](md/21.md) | nonlocal | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 30 | [global 声明全局变量](md/22.md) | global | V2.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 31 | [共享变量未绑定之坑](md/156.md) | global | V1.0 | ⭐️⭐⭐ | +| 32 | [优化代码异常输出包](md/168.md) | debugger | V1.0 | ⭐️⭐⭐ | +| 33 | [一行代码找到编码](md/170.md) | chardet | V1.0 | ⭐️⭐⭐ | +| 34 | [创建SQLite连接](md/192.md) | SQLite | v1.0 | ⭐️⭐⭐⭐ | +| 35 | [json对象转python对象](md/193.md) | python json | v1.0 | ⭐️⭐⭐⭐ | +| 36 | [python对象转json对象](md/194.md) | python json | v1.0 | ⭐️⭐⭐⭐ | +| 37 | [一行代码让 pip 安装加速 100 倍](md/176.md) | pip install | v1.0 | ⭐️⭐⭐ | + + +### 工作常用案例 +| 小例子 | 链接 | 标签 | 版本 | 难度 | +| ---- | ---------------------------------- | ---- | ---- | ---- | +| 1 | [不用else和if实现计算器](md/60.md) | operator | V1.0 | ⭐️⭐️⭐️ | +| 2 | [去最求平均](md/61.md) | list,sort,round | V1.0 | ⭐️⭐️⭐️⭐️ | +| 3 | [打印99乘法表](md/62.md) | for,range,format | V1.0 | ⭐️⭐️⭐️ | +| 4 | [递归版flatten函数](md/63.md) | recursion,list,isinstance | V1.0 | ⭐️⭐️⭐️⭐️ | +| 5 | [列表等分为n份](md/64.md) | list,ceil | V1.0 | ⭐️⭐️⭐️ | +| 6 | [压缩列表](md/65.md) | list,filter | V1.0 | ⭐️⭐️⭐️⭐️ | +| 7 | [求更长的列表](md/66.md) | max,lambda | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 8 | [求列表众数](md/67.md) | max,lambda,count | V1.0 | ⭐️⭐️⭐️⭐️ | +| 9 | [所有多个列表的最大值](md/68.md) | max,lambda | V1.0 | ⭐️⭐️⭐️⭐️ | +| 10 | [列表检查重复](md/69.md) | set | V1.0 | ⭐️⭐️⭐️ | +| 11 | [浮点数等差数列](md/71.md) | range,float | V1.0 | ⭐️⭐️⭐️⭐️ | +| 12 | [按条件分组](md/72.md) | lambda | V1.0 | ⭐️⭐️⭐️⭐️ | +| 13 | [map实现向量运算](md/73.md) | map,lambda | V1.0 | ⭐️⭐️⭐️ | +| 14 | [值最大的字典](md/74.md) | max,lambda | V1.0 | ⭐️⭐️⭐️⭐️ | +| 15 | [合并两个字典](md/75.md) | ** | V1.0 | ⭐️⭐️⭐️ | +| 16 | [Topn 字典](md/76.md) | heapq,nlargest | V1.0 | ⭐️⭐️⭐️ | +| 17 | [判断是否为异位词](md/77.md) | collections,Counter | V1.0 | ⭐️⭐️⭐️ | +| 18 | [逻辑上合并字典](md/78.md) | ChainMap | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 19 | [sample 样本抽样](md/80.md) | random,sample | V1.0 | ⭐️⭐️⭐️ | +| 20 | [重洗数据集](md/81.md) | shuffle | V1.0 | ⭐️⭐️⭐️ | +| 21 | [10个均匀分布的坐标点](md/82.md) | random,uniform | V1.0 | ⭐️⭐️⭐️ | +| 22 | [10个高斯分布的坐标点](md/83.md) | random,gauss | V1.0 | ⭐️⭐️⭐️⭐️ | +| 23 | [是否互为排序词](md/100.md) | collections,defaultdict | V1.0 | ⭐️⭐️⭐️⭐️ | +| 24 | [str1是否由str2旋转而来](md/101.md) | str | V1.0 | ⭐️⭐️⭐️ | +| 25 | [寻找第n次出现位置](md/125.md) | enumerator | V1.0 | ⭐️⭐️⭐️ | +| 26 | [找出所有重复元素](md/127.md) | calendar,datetime | V1.0 | ⭐️⭐️⭐️⭐️ | +| 27 | [联合统计次数](md/128.md) | Counter | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 28 | [求两点球面距离](md/199.md) | math asin | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 29 | [获取文件编码](md/200.md) | chardet | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | +| 30 | [格式化json串](md/201.md) | json | V1.0 | ⭐️⭐️⭐️⭐️⭐️ | -# 显示二维码 -img.show() -``` -生成一个orange的二维码: -![](https://imgkr2.cn-bj.ufileos.com/cbd26fd8-27cf-4630-935f-6896822ce483.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=uy1r24x%252Fp5QpI5Wy10Ebdaz%252BpLM%253D&Expires=1603544681) -更多样式,大家可以自己去玩耍。 diff --git a/dev/python-dev.md b/dev/python-dev.md new file mode 100644 index 00000000..1c5c04b8 --- /dev/null +++ b/dev/python-dev.md @@ -0,0 +1,1481 @@ +### Python 实战 + + +#### 221 自动群发邮件 + +Python自动群发邮件 + +```python +import smtplib +from email import (header) +from email.mime import (text, application, multipart) +import time + +def sender_mail(): + smt_p = smtplib.SMTP() + smt_p.connect(host='smtp.qq.com', port=25) + sender, password = '113097485@qq.com', "**************" + smt_p.login(sender, password) + receiver_addresses, count_num = [ + 'guozhennianhua@163.com', 'xiaoxiazi99@163.com'], 1 + for email_address in receiver_addresses: + try: + msg = multipart.MIMEMultipart() + msg['From'] = "zhenguo" + msg['To'] = email_address + msg['subject'] = header.Header('这是邮件主题通知', 'utf-8') + msg.attach(text.MIMEText( + '这是一封测试邮件,请勿回复本邮件~', 'plain', 'utf-8')) + smt_p.sendmail(sender, email_address, msg.as_string()) + time.sleep(10) + print('第%d次发送给%s' % (count_num, email_address)) + count_num = count_num + 1 + except Exception as e: + print('第%d次给%s发送邮件异常' % (count_num, email_address)) + continue + smt_p.quit() + +sender_mail() +``` + + + +注意: +发送邮箱是qq邮箱,所以要在qq邮箱中设置开启SMTP服务,设置完成时会生成一个授权码,将这个授权码赋值给文中的`password`变量 + +#### 222 二分搜索 + +二分搜索是程序员必备的算法,无论什么场合,都要非常熟练地写出来。 + +小例子描述: +在**有序数组**`arr`中,指定区间`[left,right]`范围内,查找元素`x` +如果不存在,返回`-1` + +二分搜索`binarySearch`实现的主逻辑 + +```python +def binarySearch(arr, left, right, x): + while left <= right: + + mid = int(left + (right - left) / 2); # 找到中间位置。求中点写成(left+right)/2更容易溢出,所以不建议这样写 + + # 检查x是否出现在位置mid + if arr[mid] == x: + print('found %d 在索引位置%d 处' %(x,mid)) + return mid + + # 假如x更大,则不可能出现在左半部分 + elif arr[mid] < x: + left = mid + 1 #搜索区间变为[mid+1,right] + print('区间缩小为[%d,%d]' %(mid+1,right)) + + # 同理,假如x更小,则不可能出现在右半部分 + elif x + +```python +import requests +from lxml import etree +import pandas as pd +import re + +url = 'http://www.weather.com.cn/weather1d/101010100.shtml#input' +with requests.get(url) as res: + content = res.content + html = etree.HTML(content) +``` + + + +通过lxml模块提取值 + +lxml比beautifulsoup解析在某些场合更高效 + +```python +location = html.xpath('//*[@id="around"]//a[@target="_blank"]/span/text()') +temperature = html.xpath('//*[@id="around"]/div/ul/li/a/i/text()') +``` + +结果: + +```python +['香河', '涿州', '唐山', '沧州', '天津', '廊坊', '太原', '石家庄', '涿鹿', '张家口', '保定', '三河', '北京孔庙', '北京国子监', '中国地质博物馆', '月坛公 +园', '明城墙遗址公园', '北京市规划展览馆', '什刹海', '南锣鼓巷', '天坛公园', '北海公园', '景山公园', '北京海洋馆'] + +['11/-5°C', '14/-5°C', '12/-6°C', '12/-5°C', '11/-1°C', '11/-5°C', '8/-7°C', '13/-2°C', '8/-6°C', '5/-9°C', '14/-6°C', '11/-4°C', '13/-3°C' +, '13/-3°C', '12/-3°C', '12/-3°C', '13/-3°C', '12/-2°C', '12/-3°C', '13/-3°C', '12/-2°C', '12/-2°C', '12/-2°C', '12/-3°C'] +``` + + +构造DataFrame对象 + +```python +df = pd.DataFrame({'location':location, 'temperature':temperature}) +print('温度列') +print(df['temperature']) +``` + +正则解析温度值 + +```python +df['high'] = df['temperature'].apply(lambda x: int(re.match('(-?[0-9]*?)/-?[0-9]*?°C', x).group(1) ) ) +df['low'] = df['temperature'].apply(lambda x: int(re.match('-?[0-9]*?/(-?[0-9]*?)°C', x).group(1) ) ) +print(df) +``` + +详细说明子字符创捕获 + +除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用`()`表示的就是要提取的分组(group)。比如:`^(\d{3})-(\d{3,8})$`分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码 + +```python +m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345') +print(m.group(0)) +print(m.group(1)) +print(m.group(2)) + +# 010-12345 +# 010 +# 12345 +``` + +如果正则表达式中定义了组,就可以在`Match`对象上用`group()`方法提取出子串来。 + +注意到`group(0)`永远是原始字符串,`group(1)`、`group(2)`……表示第1、2、……个子串。 + + +最终结果 + +```kepython +Name: temperature, dtype: object + location temperature high low +0 香河 11/-5°C 11 -5 +1 涿州 14/-5°C 14 -5 +2 唐山 12/-6°C 12 -6 +3 沧州 12/-5°C 12 -5 +4 天津 11/-1°C 11 -1 +5 廊坊 11/-5°C 11 -5 +6 太原 8/-7°C 8 -7 +7 石家庄 13/-2°C 13 -2 +8 涿鹿 8/-6°C 8 -6 +9 张家口 5/-9°C 5 -9 +10 保定 14/-6°C 14 -6 +11 三河 11/-4°C 11 -4 +12 北京孔庙 13/-3°C 13 -3 +13 北京国子监 13/-3°C 13 -3 +14 中国地质博物馆 12/-3°C 12 -3 +15 月坛公园 12/-3°C 12 -3 +16 明城墙遗址公园 13/-3°C 13 -3 +17 北京市规划展览馆 12/-2°C 12 -2 +18 什刹海 12/-3°C 12 -3 +19 南锣鼓巷 13/-3°C 13 -3 +20 天坛公园 12/-2°C 12 -2 +21 北海公园 12/-2°C 12 -2 +22 景山公园 12/-2°C 12 -2 +23 北京海洋馆 12/-3°C 12 -3 +``` + +### 十、数据分析 + +本项目基于Kaggle电影影评数据集,通过这个系列,你将学到如何进行数据探索性分析(EDA),学会使用数据分析利器`pandas`,会用绘图包`pyecharts`,以及EDA时可能遇到的各种实际问题及一些处理技巧。 + + + +本项目需要导入的包: + +```python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from pyecharts.charts import Bar,Grid,Line +import pyecharts.options as opts +from pyecharts.globals import ThemeType +``` + +#### 1 创建DataFrame +pandas中一个dataFrame实例: +```python +Out[89]: + a val +0 apple1 1.0 +1 apple2 2.0 +2 apple3 3.0 +3 apple4 4.0 +4 apple5 5.0 +``` + +我们的**目标**是变为如下结构: +```python +a apple1 apple2 apple3 apple4 apple5 +0 1.0 2.0 3.0 4.0 5.0 +``` + +乍看可使用`pivot`,但很难一步到位。 + +所以另辟蹊径,提供一种简单且好理解的方法: + +```python +In [113]: pd.DataFrame(index=[0],columns=df.a,data=dict(zip(df.a,df.val))) +Out[113]: +a apple1 apple2 apple3 apple4 apple5 +0 1.0 2.0 3.0 4.0 5.0 +``` +以上方法是重新创建一个DataFrame,直接把`df.a`所有可能取值作为新dataframe的列,index调整为`[0]`,注意类型必须是数组类型(array-like 或者 Index),两个轴确定后,`data`填充数据域。 + +```python +In [116]: dict(zip(df.a,df.val)) +Out[116]: {'apple1': 1.0, 'apple2': 2.0, 'apple3': 3.0, 'apple4': 4.0, 'apple5': 5.0} +``` + + + +#### 2 导入数据 +数据来自kaggle,共包括三个文件: + +1. movies.dat +2. ratings.dat +3. users.dat + +`movies.dat`包括三个字段:['Movie ID', 'Movie Title', 'Genre'] + +使用pandas导入此文件: + +```python +import pandas as pd + +movies = pd.read_csv('./data/movietweetings/movies.dat', delimiter='::', engine='python', header=None, names = ['Movie ID', 'Movie Title', 'Genre']) +``` + +导入后,显示前5行: + +```python + Movie ID Movie Title \ +0 8 Edison Kinetoscopic Record of a Sneeze (1894) +1 10 La sortie des usines Lumi猫re (1895) +2 12 The Arrival of a Train (1896) +3 25 The Oxford and Cambridge University Boat Race ... +4 91 Le manoir du diable (1896) +5 131 Une nuit terrible (1896) +6 417 Le voyage dans la lune (1902) +7 439 The Great Train Robbery (1903) +8 443 Hiawatha, the Messiah of the Ojibway (1903) +9 628 The Adventures of Dollie (1908) + Genre +0 Documentary|Short +1 Documentary|Short +2 Documentary|Short +3 NaN +4 Short|Horror +5 Short|Comedy|Horror +6 Short|Action|Adventure|Comedy|Fantasy|Sci-Fi +7 Short|Action|Crime|Western +8 NaN +9 Action|Short +``` + + + +次导入其他两个数据文件 + +`users.dat`: + +```python +users = pd.read_csv('./data/movietweetings/users.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Twitter ID']) +print(users.head()) +``` + +结果: + +```python + User ID Twitter ID +0 1 397291295 +1 2 40501255 +2 3 417333257 +3 4 138805259 +4 5 2452094989 +5 6 391774225 +6 7 47317010 +7 8 84541461 +8 9 2445803544 +9 10 995885060 +``` + + + +`rating.data`: + +```python +ratings = pd.read_csv('./data/movietweetings/ratings.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Movie ID', 'Rating', 'Rating Timestamp']) +print(ratings.head()) +``` + +结果: + +```python + User ID Movie ID Rating Rating Timestamp +0 1 111161 10 1373234211 +1 1 117060 7 1373415231 +2 1 120755 6 1373424360 +3 1 317919 6 1373495763 +4 1 454876 10 1373621125 +5 1 790724 8 1374641320 +6 1 882977 8 1372898763 +7 1 1229238 9 1373506523 +8 1 1288558 5 1373154354 +9 1 1300854 8 1377165712 +``` + + **read_csv 使用说明** + +说明,本次导入`dat`文件使用`pandas.read_csv`函数。 + +第一个位置参数`./data/movietweetings/ratings.dat` 表示文件的相对路径 + +第二个关键字参数:`delimiter='::'`,表示文件分隔符使用`::` + +后面几个关键字参数分别代表使用的引擎,文件没有表头,所以`header`为`None;` + +导入后dataframe的列名使用`names`关键字设置,这个参数大家可以记住,比较有用。 + + + +Kaggle电影数据集第一节,我们使用数据处理利器 `pandas`, 函数`read_csv` 导入给定的三个数据文件。 + +```python +import pandas as pd + +movies = pd.read_csv('./data/movietweetings/movies.dat', delimiter='::', engine='python', header=None, names = ['Movie ID', 'Movie Title', 'Genre']) +users = pd.read_csv('./data/movietweetings/users.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Twitter ID']) +ratings = pd.read_csv('./data/movietweetings/ratings.dat', delimiter='::', engine='python', header=None, names = ['User ID', 'Movie ID', 'Rating', 'Rating Timestamp']) +``` + +用到的`read_csv`,某些重要的参数,如何使用在上一节也有所提到。下面开始数据探索分析(EDA) + +> 找出得分前10喜剧(comedy) + + + +#### 3 处理组合值 + +表`movies`字段`Genre`表示电影的类型,可能有多个值,分隔符为`|`,取值也可能为`None`. + +针对这类字段取值,可使用Pandas中Series提供的`str`做一步转化,**注意它是向量级的**,下一步,如Python原生的`str`类似,使用`contains`判断是否含有`comedy`字符串: + +```python +mask = movies.Genre.str.contains('comedy',case=False,na=False) +``` + +注意使用的两个参数:`case`, `na` + +case为 False,表示对大小写不敏感; +na Genre列某个单元格为`NaN`时,我们使用的充填值,此处填充为`False` + +返回的`mask`是一维的`Series`,结构与 movies.Genre相同,取值为True 或 False. + +观察结果: + +```python +0 False +1 False +2 False +3 False +4 False +5 True +6 True +7 False +8 False +9 False +Name: Genre, dtype: bool + +``` + + + #### 4 访问某列 + +得到掩码mask后,pandas非常方便地能提取出目标记录: + +```python +comedy = movies[mask] +comdey_ids = comedy['Movie ID'] + +``` + +以上,在pandas中被最频率使用,不再解释。看结果`comedy_ids.head()`: + +```python +5 131 +6 417 +15 2354 +18 3863 +19 4099 +20 4100 +21 4101 +22 4210 +23 4395 +25 4518 +Name: Movie ID, dtype: int64 + +``` + + + +1-4介绍`数据读入`,`处理组合值`,`索引数据`等, pandas中使用较多的函数,基于Kaggle真实电影影评数据集,最后得到所有`喜剧 ID`: + +```python +5 131 +6 417 +15 2354 +18 3863 +19 4099 +20 4100 +21 4101 +22 4210 +23 4395 +25 4518 +Name: Movie ID, dtype: int64 + +``` + +下面继续数据探索之旅~ + +#### 5 连接两个表 + +拿到所有喜剧的ID后,要想找出其中平均得分最高的前10喜剧,需要关联另一张表:`ratings`: + +再回顾下ratings表结构: + +```python + User ID Movie ID Rating Rating Timestamp +0 1 111161 10 1373234211 +1 1 117060 7 1373415231 +2 1 120755 6 1373424360 +3 1 317919 6 1373495763 +4 1 454876 10 1373621125 +5 1 790724 8 1374641320 +6 1 882977 8 1372898763 +7 1 1229238 9 1373506523 +8 1 1288558 5 1373154354 +9 1 1300854 8 1377165712 + +``` + + +pandas 中使用`join`关联两张表,连接字段是`Movie ID`,如果顺其自然这么使用`join`: + +```python +combine = ratings.join(comedy, on='Movie ID', rsuffix='2') + +``` + +左右滑动,查看完整代码 + +大家可验证这种写法,仔细一看,会发现结果非常诡异。 + +究其原因,这是pandas join函数使用的一个算是坑点,它在官档中介绍,连接右表时,此处右表是`comedy`,它的`index`要求是连接字段,也就是 `Movie ID`. + +左表的index不要求,但是要在参数 `on`中给定。 + +**以上是要注意的一点** + +修改为: + +```python +combine = ratings.join(comedy.set_index('Movie ID'), on='Movie ID') +print(combine.head(10)) + +``` + +以上是OK的写法 + +观察结果: + +```python + User ID Movie ID Rating Rating Timestamp Movie Title Genre +0 1 111161 10 1373234211 NaN NaN +1 1 117060 7 1373415231 NaN NaN +2 1 120755 6 1373424360 NaN NaN +3 1 317919 6 1373495763 NaN NaN +4 1 454876 10 1373621125 NaN NaN +5 1 790724 8 1374641320 NaN NaN +6 1 882977 8 1372898763 NaN NaN +7 1 1229238 9 1373506523 NaN NaN +8 1 1288558 5 1373154354 NaN NaN +9 1 1300854 8 1377165712 NaN NaN + +``` + +Genre列为`NaN`表明,这不是喜剧。需要筛选出此列不为`NaN` 的记录。 + +#### 6 按列筛选 + +pandas最方便的地方,就是向量化运算,尽可能减少了for循环的嵌套。 + +按列筛选这种常见需求,自然可以轻松应对。 + +为了照顾初次接触 pandas 的朋友,分两步去写: + +```python +mask = pd.notnull(combine['Genre']) + +``` + +结果是一列只含`True 或 False`的值 + +```python +result = combine[mask] +print(result.head()) + +``` + +结果中,Genre字段中至少含有一个Comedy字符串,表明验证了我们以上操作是OK的。 + +```python + User ID Movie ID Rating Rating Timestamp Movie Title \ +12 1 1588173 9 1372821281 Warm Bodies (2013) +13 1 1711425 3 1372604878 21 & Over (2013) +14 1 2024432 8 1372703553 Identity Thief (2013) +17 1 2101441 1 1372633473 Spring Breakers (2012) +28 2 1431045 7 1457733508 Deadpool (2016) + + Genre +12 Comedy|Horror|Romance +13 Comedy +14 Adventure|Comedy|Crime|Drama +17 Comedy|Crime|Drama +28 Action|Adventure|Comedy|Sci-Fi + + +``` + + + +截止目前已经求出所有喜剧电影`result`,前5行如下,Genre中都含有`Comedy`字符串: +```python + User ID Movie ID Rating Rating Timestamp Movie Title \ +12 1 1588173 9 1372821281 Warm Bodies (2013) +13 1 1711425 3 1372604878 21 & Over (2013) +14 1 2024432 8 1372703553 Identity Thief (2013) +17 1 2101441 1 1372633473 Spring Breakers (2012) +28 2 1431045 7 1457733508 Deadpool (2016) + + Genre +12 Comedy|Horror|Romance +13 Comedy +14 Adventure|Comedy|Crime|Drama +17 Comedy|Crime|Drama +28 Action|Adventure|Comedy|Sci-Fi +``` + + + +#### 7 按照Movie ID 分组 + +result中会有很多观众对同一部电影的打分,所以要求得分前10的喜剧,先按照`Movie ID`分组,然后求出平均值: +```python +score_as_movie = result.groupby('Movie ID').mean() +``` + +前5行显示如下: +```python + User ID Rating Rating Timestamp +Movie ID +131 34861.000000 7.0 1.540639e+09 +417 34121.409091 8.5 1.458680e+09 +2354 6264.000000 8.0 1.456343e+09 +3863 43803.000000 10.0 1.430439e+09 +4099 25084.500000 7.0 1.450323e+09 +``` + +#### 8 按照电影得分排序 + +```python +score_as_movie.sort_values(by='Rating', ascending = False,inplace=True) +score_as_movie +``` +前5行显示如下: +```python + User ID Rating Rating Timestamp +Movie ID +7134690 30110.0 10.0 1.524974e+09 +416889 1319.0 10.0 1.543320e+09 +57840 23589.0 10.0 1.396802e+09 +5693562 50266.0 10.0 1.511024e+09 +5074 43803.0 10.0 1.428352e+09 +``` +都是满分?这有点奇怪,会不会这些电影都只有几个人评分,甚至只有1个?评分样本个数太少,显然最终的平均分数不具有太强的说服力。 + +所以,下面要进行每部电影的评分人数统计 + +#### 9 分组后使用聚合函数 + +根据`Movie ID`分组后,使用`count`函数统计`每组个数`,只保留count列,最后得到`watchs2`: + +```python +watchs = result.groupby('Movie ID').agg(['count']) +watchs2 = watchs['Rating']['count'] +``` +打印前20行: +```python +print(watchs2.head(20)) +``` +结果: +```python +Movie ID +131 1 +417 22 +2354 1 +3863 1 +4099 2 +4100 1 +4101 1 +4210 1 +4395 1 +4518 1 +4546 2 +4936 2 +5074 1 +5571 1 +6177 1 +6414 3 +6684 1 +6689 1 +7145 1 +7162 2 +Name: count, dtype: int64 +``` +果然,竟然有这么多电影的评论数只有1次!样本个数太少,评论的平均值也就没有什么说服力。 + +查看`watchs2`一些重要统计量: +```python +watchs2.describe() +``` +结果: +```python +count 10740.000000 +mean 20.192086 +std 86.251411 +min 1.000000 +25% 1.000000 +50% 2.000000 +75% 7.000000 +max 1843.000000 +Name: count, dtype: float64 +``` +共有10740部**喜剧**电影被评分,平均打分次数20次,标准差86,75%的电影样本打分次数小于7次,最小1次,最多1843次。 + +#### 10 频率分布直方图 + +绘制评论数的频率分布直方图,便于更直观的观察电影被评论的分布情况。上面分析到,75%的电影打分次数小于7次,所以绘制打分次数小于20次的直方图: + +```python +fig = plt.figure(figsize=(12,8)) +histn = plt.hist(watchs2[watchs2 <=19],19,histtype='step') +plt.scatter([i+1 for i in range(len(histn[0]))],histn[0]) +``` + +![](./img/20200131094927.jpg) + +`histn`元祖表示个数和对应的被分割的区间,查看`histn[0]`: +```python +array([4383., 1507., 787., 541., 356., 279., 209., 163., 158., + 118., 114., 90., 104., 81., 80., 73., 62., 65., + 52.]) +``` +```python +sum(histn[0]) # 9222 +``` +看到电影评论次数1到19次的喜剧电影9222部,共有10740部喜剧电影,大约`86%`的喜剧电影评论次数`小于20次`,有`1518`部电影评论数不小于20次。 + +我们肯定希望挑选出被评论次数尽可能多的电影,因为难免会有水军和滥竽充数等`异常评论`行为。那么,如何准确的量化最小抽样量呢? + + + +#### 11 最小抽样量 + +根据统计学的知识,最小抽样量和Z值、样本方差和样本误差相关,下面给出具体的求解最小样本量的计算方法。 + +采用如下计算公式: + +$$ n = \frac{Z^2\sigma^2}{E^2} $$ + + +此处,$Z$ 值取为95%的置信度对应的Z值也就是1.96,样本误差取为均值的2.5%. + +根据以上公式,编写下面代码: + +```python +n3 = result.groupby('Movie ID').agg(['count','mean','std']) +n3r = n3[n3['Rating']['count']>=20]['Rating'] +``` +只计算影评超过20次,且满足最小样本量的电影。计算得到的`n3r`前5行: +```python + count mean std +Movie ID +417 22 8.500000 1.263027 +12349 68 8.485294 1.227698 +15324 20 8.350000 1.039990 +15864 51 8.431373 1.374844 +17925 44 8.636364 1.259216 +``` +进一步求出最小样本量: +```python +nmin = (1.96**2*n3r['std']**2) / ( (n3r['mean']*0.025)**2 ) +``` +`nmin`前5行: +```python +Movie ID +417 135.712480 +12349 128.671290 +15324 95.349276 +15864 163.434005 +17925 130.668350 +``` + +筛选出满足最小抽样量的喜剧电影: + +```python +n3s = n3r[ n3r['count'] >= nmin ] +``` +结果显示如下,因此共有`173`部电影满足最小样本抽样量。 + +```python + +count mean std +Movie ID +53604 129 8.635659 1.230714 +57012 207 8.449275 1.537899 +70735 224 8.839286 1.190799 +75686 209 8.095694 1.358885 +88763 296 8.945946 1.026984 +... ... ... ... +6320628 860 7.966279 1.469924 +6412452 276 7.510870 1.389529 +6662050 22 10.000000 0.000000 +6966692 907 8.673649 1.286455 +7131622 1102 7.851180 1.751500 +173 rows × 3 columns +``` + +#### 12 去重和连表 + +按照平均得分从大到小排序: +```python +n3s_sort = n3s.sort_values(by='mean',ascending=False) +``` +结果: +```python + count mean std +Movie ID +6662050 22 10.000000 0.000000 +4921860 48 10.000000 0.000000 +5262972 28 10.000000 0.000000 +5512872 353 9.985836 0.266123 +3863552 199 9.010050 1.163372 +... ... ... ... +1291150 647 6.327666 1.785968 +2557490 546 6.307692 1.858434 +1478839 120 6.200000 0.728761 +2177771 485 6.150515 1.523922 +1951261 1091 6.083410 1.736127 +173 rows × 3 columns +``` +仅靠`Movie ID`还是不知道哪些电影,连接`movies`表: +```python +ms = movies.drop_duplicates(subset=['Movie ID']) +ms = ms.set_index('Movie ID') +n3s_final = n3s_drops.join(ms,on='Movie ID') +``` + +#### 13 结果分析 + +喜剧榜单前50名: +```python +Movie Title +Five Minutes (2017) +MSG 2 the Messenger (2015) +Avengers: Age of Ultron Parody (2015) +Be Somebody (2016) +Bajrangi Bhaijaan (2015) +Back to the Future (1985) +La vita 鐚?bella (1997) +The Intouchables (2011) +The Sting (1973) +Coco (2017) +Toy Story 3 (2010) +3 Idiots (2009) +Green Book (2018) +Dead Poets Society (1989) +The Apartment (1960) +P.K. (2014) +The Truman Show (1998) +Am鑼卨ie (2001) +Inside Out (2015) +Toy Story 4 (2019) +Toy Story (1995) +Finding Nemo (2003) +Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb (1964) +Home Alone (1990) +Zootopia (2016) +Up (2009) +Monsters, Inc. (2001) +La La Land (2016) +Relatos salvajes (2014) +En man som heter Ove (2015) +Snatch (2000) +Lock, Stock and Two Smoking Barrels (1998) +How to Train Your Dragon 2 (2014) +As Good as It Gets (1997) +Guardians of the Galaxy (2014) +The Grand Budapest Hotel (2014) +Fantastic Mr. Fox (2009) +Silver Linings Playbook (2012) +Sing Street (2016) +Deadpool (2016) +Annie Hall (1977) +Pride (2014) +In Bruges (2008) +Big Hero 6 (2014) +Groundhog Day (1993) +The Breakfast Club (1985) +Little Miss Sunshine (2006) +Deadpool 2 (2018) +The Terminal (2004) +``` + +前10名评论数图: + +![](./img/2020013109495711.jpg) + +代码: +```python +x = n3s_final['Movie Title'][:10].tolist()[::-1] +y = n3s_final['count'][:10].tolist()[::-1] +bar = ( + Bar() + .add_xaxis(x) + .add_yaxis('评论数',y,category_gap='50%') + .reversal_axis() + .set_global_opts(title_opts=opts.TitleOpts(title="喜剧电影被评论次数"), + toolbox_opts=opts.ToolboxOpts(),) +) +grid = ( + Grid(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) + .add(bar, grid_opts=opts.GridOpts(pos_left="30%")) +) +grid.render_notebook() +``` + +前10名得分图: + +![](./img/2020013109500812.jpg) + +代码: +```python +x = n3s_final['Movie Title'][:10].tolist()[::-1] +y = n3s_final['mean'][:10].round(3).tolist()[::-1] +bar = ( + Bar() + .add_xaxis(x) + .add_yaxis('平均得分',y,category_gap='50%') + .reversal_axis() + .set_global_opts(title_opts=opts.TitleOpts(title="喜剧电影平均得分"), + xaxis_opts=opts.AxisOpts(min_=8.0,name='平均得分'), + toolbox_opts=opts.ToolboxOpts(),) +) +grid = ( + Grid(init_opts=opts.InitOpts(theme=ThemeType.MACARONS)) + .add(bar, grid_opts=opts.GridOpts(pos_left="30%")) +) +grid.render_notebook() +``` + + + +#### 14 生成哑变量 + +分类变量的数值化,是指将枚举类变量转化为indicator变量或称dummy变量。 + +那么什么是`indicator变量`,看看如下例子,A变量解析为:`[1,0,0]`, B解析为:`[0,1,0]`, C解析为:`[0,0,1]` +```python +In [8]: s = pd.Series(list('ABCA')) +In [9]: pd.get_dummies(s) +Out[9]: + A B C +0 1 0 0 +1 0 1 0 +2 0 0 1 +3 1 0 0 +``` + +如果输入的字符有4个唯一值,看到字符a被解析为[1,0,0,0],向量长度为4. + +```python +In [5]: s = pd.Series(list('abaccd')) +In [6]: pd.get_dummies(s) +Out[6]: + a b c d +0 1 0 0 0 +1 0 1 0 0 +2 1 0 0 0 +3 0 0 1 0 +4 0 0 1 0 +5 0 0 0 1 +``` + +也就是说dummy向量的长度等于输入字符串中,唯一字符的个数。 + +#### 15 讨厌的SettingWithCopyWarning!!! + +Pandas 处理数据,太好用了,谁用谁知道! + +使用过 Pandas 的,几乎都会遇到一个警告: + +*SettingWithCopyWarning* + +非常烦人! + +尤其是刚接触 Pandas 的,完全不理解为什么弹出这么一串: + +```python +d:\source\test\settingwithcopy.py:9: SettingWithCopyWarning: +A value is trying to be set on a copy of a slice from a DataFrame. +Try using .loc[row_indexer,col_indexer] = value instead + +See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy +``` + +归根结底,是因为代码中出现`链式操作`... + +有人就问了,什么是`链式操作`? + +这样的: + +```python +tmp = df[df.a<4] +tmp['c'] = 200 +``` + +先记住这个最典型的情况,即可! + +有的人就问了:出现这个 Warning, 需要理会它吗? + +如果结果不对,当然要理会;如果结果对,不care. + +举个例子~~ + +```python +import pandas as pd + +df = pd.DataFrame({'a':[1,3,5],'b':[4,2,7]},index=['a','b','c']) +df.loc[df.a<4,'c'] = 100 +print(df) +print('it\'s ok') + +tmp = df[df.a<4] +tmp['c'] = 200 +print('-----tmp------') +print(tmp) +print('-----df-------') +print(df) +``` + +输出结果: +```python + a b c +a 1 4 100.0 +b 3 2 100.0 +c 5 7 NaN +it's ok +d:\source\test\settingwithcopy.py:9: SettingWithCopyWarning: +A value is trying to be set on a copy of a slice from a DataFrame. +Try using .loc[row_indexer,col_indexer] = value instead + +See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy + tmp['c'] = 200 +-----tmp------ + a b c +a 1 4 200 +b 3 2 200 +-----df------- + a b c +a 1 4 100.0 +b 3 2 100.0 +c 5 7 NaN +``` + +it's ok 行后面的发生链式赋值,导致结果错误。因为 tmp 变了,df 没赋上值啊,所以必须理会。 + +it's ok 行前的是正解。 + +以上,链式操作尽量避免,如何避免?多使用 `.loc[row_indexer,col_indexer]`,提示告诉我们的~ + +#### 16 NumPy 数据归一化、分布可视化 + +仅使用 `NumPy`,下载数据,归一化,使用 `seaborn` 展示数据分布。 + +**下载数据** + +```python +import numpy as np + +url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data' +wid = np.genfromtxt(url, delimiter=',', dtype='float', usecols=[1]) +``` +仅提取 `iris` 数据集的第二列 `usecols = [1]` + +**展示数据** + +```python +array([3.5, 3. , 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3. , + 3. , 4. , 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3. , + 3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.1, 3. , + 3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3. , 3.8, 3.2, 3.7, 3.3, 3.2, 3.2, + 3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2. , 3. , 2.2, 2.9, 2.9, + 3.1, 3. , 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3. , 2.8, 3. , + 2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3. , 3.4, 3.1, 2.3, 3. , 2.5, 2.6, + 3. , 2.6, 2.3, 2.7, 3. , 2.9, 2.9, 2.5, 2.8, 3.3, 2.7, 3. , 2.9, + 3. , 3. , 2.5, 2.9, 2.5, 3.6, 3.2, 2.7, 3. , 2.5, 2.8, 3.2, 3. , + 3.8, 2.6, 2.2, 3.2, 2.8, 2.8, 2.7, 3.3, 3.2, 2.8, 3. , 2.8, 3. , + 2.8, 3.8, 2.8, 2.8, 2.6, 3. , 3.4, 3.1, 3. , 3.1, 3.1, 3.1, 2.7, + 3.2, 3.3, 3. , 2.5, 3. , 3.4, 3. ]) + +``` + +这是单变量(univariate)长度为 150 的一维 NumPy 数组。 + +**归一化** + +求出最大值、最小值 +```python +smax = np.max(wid) +smin = np.min(wid) + +In [51]: smax,smin +Out[51]: (4.4, 2.0) +```` +归一化公式: +```python +s = (wid - smin) / (smax - smin) +``` +只打印小数点后三位设置: +```python +np.set_printoptions(precision=3) +``` + +归一化结果: +```markdown +array([0.625, 0.417, 0.5 , 0.458, 0.667, 0.792, 0.583, 0.583, 0.375, + 0.458, 0.708, 0.583, 0.417, 0.417, 0.833, 1. , 0.792, 0.625, + 0.75 , 0.75 , 0.583, 0.708, 0.667, 0.542, 0.583, 0.417, 0.583, + 0.625, 0.583, 0.5 , 0.458, 0.583, 0.875, 0.917, 0.458, 0.5 , + 0.625, 0.458, 0.417, 0.583, 0.625, 0.125, 0.5 , 0.625, 0.75 , + 0.417, 0.75 , 0.5 , 0.708, 0.542, 0.5 , 0.5 , 0.458, 0.125, + 0.333, 0.333, 0.542, 0.167, 0.375, 0.292, 0. , 0.417, 0.083, + 0.375, 0.375, 0.458, 0.417, 0.292, 0.083, 0.208, 0.5 , 0.333, + 0.208, 0.333, 0.375, 0.417, 0.333, 0.417, 0.375, 0.25 , 0.167, + 0.167, 0.292, 0.292, 0.417, 0.583, 0.458, 0.125, 0.417, 0.208, + 0.25 , 0.417, 0.25 , 0.125, 0.292, 0.417, 0.375, 0.375, 0.208, + 0.333, 0.542, 0.292, 0.417, 0.375, 0.417, 0.417, 0.208, 0.375, + 0.208, 0.667, 0.5 , 0.292, 0.417, 0.208, 0.333, 0.5 , 0.417, + 0.75 , 0.25 , 0.083, 0.5 , 0.333, 0.333, 0.292, 0.542, 0.5 , + 0.333, 0.417, 0.333, 0.417, 0.333, 0.75 , 0.333, 0.333, 0.25 , + 0.417, 0.583, 0.458, 0.417, 0.458, 0.458, 0.458, 0.292, 0.5 , + 0.542, 0.417, 0.208, 0.417, 0.583, 0.417]) +``` + +**分布可视化** + +```python +import seaborn as sns +sns.distplot(s,kde=False,rug=True) +``` +频率分布直方图: + + +![](https://imgkr.cn-bj.ufileos.com/49bf5190-429c-4172-a53c-e3f6b66d4e64.png) + + +```python +sns.distplot(s,hist=True,kde=True,rug=True) +``` +带高斯密度核函数的直方图: + +![](https://imgkr.cn-bj.ufileos.com/4e4a72a5-8f59-4893-b435-e4b57e22a18e.png) + + + +**分布 fit 图** + +拿 `gamma` 分布去 fit : +```python +from scipy import stats +sns.distplot(s, kde=False, fit = stats.gamma) +``` + + + +![](https://imgkr.cn-bj.ufileos.com/89446755-7420-4f96-97fe-c4e45d0d3dec.png) + + +拿双 `gamma` 去 fit: +```python +from scipy import stats +sns.distplot(s, kde=False, fit = stats.dgamma) +``` + +![](https://imgkr.cn-bj.ufileos.com/f2c2a660-5433-4b4f-ad7b-d01da4121319.png) + +#### 17 Pandas 使用技巧 + +对于动辄就几十或几百个 G 的数据,在读取的这么大数据的时候,我们有没有办法随机选取一小部分数据,然后读入内存,快速了解数据和开展 EDA ? + +使用 Pandas 的 skiprows 和 概率知识,就能做到。 + +下面解释具体怎么做。 + +如下所示,读取某 100 G 大小的 big_data.csv 数据 + +1) 使用 skiprows 参数, + +2) x > 0 确保首行读入, + +3) np.random.rand() > 0.01 表示 99% 的数据都会被随机过滤掉 + +言外之意,只有全部数据的 1% 才有机会选入内存中。 + +```python +import pandas as pd +import numpy as np + +df = pd.read_csv("big_data.csv", +skiprows = +lambda x: x>0 and np.random.rand() > 0.01) + +print("The shape of the df is {}. +It has been reduced 100 times!".format(df.shape)) +``` + +使用这种方法,读取的数据量迅速缩减到原来的 1% ,对于迅速展开数据分析有一定的帮助。 + +### 十一、一步一步掌握Flask web开发 + +#### 1 Flask版 hello world + +Flask是Python轻量级web框架,容易上手,被广大Python开发者所喜爱。 + +今天我们先从hello world开始,一步一步掌握Flask web开发。例子君是Flask框架的小白,接下来与读者朋友们,一起学习这个对我而言的新框架,大家多多指导。 + +首先`pip install Flask`,安装Flask,然后import Flask,同时创建一个 `app` +```python +from flask import Flask + +App = Flask(__name__) +``` + +写一个index页的入口函数,返回hello world. + +通过装饰器:App.route('/')创建index页的路由或地址,一个`/`表示index页,也就是主页。 + +```python +@App.route('/') +def index(): + return "hello world" +``` + +调用 `index`函数: +```python +if __name__ == "__main__": + App.run(debug=True) +``` + +然后启动,会在console下看到如下启动信息,表明`服务启动成功`。 +```python +* Debug mode: on + * Restarting with stat + * Debugger is active! + * Debugger PIN: 663-788-611 + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) +``` + + 接下来,打开一个网页,相当于启动客户端,并在Url栏中输入:`http://127.0.0.1:5000/`,看到页面上答应出`hello world`,证明服务访问成功。 + + 同时在服务端后台看到如下信息,表示处理一次来自客户端的`get`请求。 + ```python + 27.0.0.1 - - [03/Feb/2020 21:26:50] "GET / HTTP/1.1" 200 - + ``` + + 以上就是flask的hello world 版 + +#### 2 Flask之数据入库操作 + +数据持久化就是将数据写入到数据库存储的过程。 + +本例子使用`sqlite3`数据库。 + +1)导入`sqlite3`,未安装前使用命令`pip install sqlite3` + +创建一个`py`文件:`sqlite3_started.py`,并写下第一行代码: +```python +import sqlite3 +``` +2)手动创建一个数据库实例`db`, 命名`test.db` + +3)创建与数据库实例`test.db`的连接: +```python +conn = sqlite3.connect("test.db") +``` + +4)拿到连接`conn`的cursor +```python +c = conn.cursor() +``` + +5)创建第一张表`books` + +共有四个字段:`id`,`sort`,`name`,`price`,类型分别为:`int`,`int`,`text`,`real`. 其中`id`为`primary key`. 主键的取值必须是唯一的(`unique`),否则会报错。 + + +```python +c.execute('''CREATE TABLE books + (id int primary key, + sort int, + name text, + price real)''') +``` +第一次执行上面语句,表`books`创建完成。当再次执行时,就会报`重复建表`的错误。需要优化脚本,检查表是否存在`IF NOT EXISTS books`,不存在再创建: +```python +c.execute('''CREATE TABLE IF NOT EXISTS books + (id int primary key, + sort int, + name text, + price real)''') +``` + +6)插入一行记录 + +共为4个字段赋值 + +```python +c.execute('''INSERT INTO books VALUES + (1, + 1, + 'computer science', + 39.0)''') +``` + +7)一次插入多行记录 + +先创建一个list:`books`,使用`executemany`一次插入多行。 +```python +books = [(2, 2, 'Cook book', 68), + (3, 2, 'Python intro', 89), + (4, 3, 'machine learning', 59), + ] + + +c.executemany('INSERT INTO books VALUES (?, ?, ?, ?)', books) +``` + +8)提交 + +提交后才会真正生效,写入到数据库 + +```python +conn.commit() +``` + +9)关闭期初建立的连接conn + +务必记住手动关闭,否则会出现内存泄漏 +```python +conn.close() +print('Done') +``` + +10)查看结果 +例子君使用`vs code`,在扩展库中选择:`SQLite`安装。 + +![image-20200208211721377](./img/image-20200208211721377.png) + +新建一个`sq`文件:`a.sql`,内容如下: + +```sql +SELECT * from books +``` +右键`run query`,得到表`books`插入的4行记录可视化图: + +![image-20200208211806853](./img/image-20200208211806853.png) + +以上十步就是sqlite3写入数据库的主要步骤,作为Flask系列的第二篇,为后面的前端讲解打下基础。 + +#### 3 Flask各层调用关系 + +这篇介绍Flask和B/S模式,即浏览器/服务器模式,是接下来快速理解Flask代码的关键理论篇:**理解Views、models和渲染模板层的调用关系**。 + +1) 发出请求 + +当我们在浏览器地址栏中输入某个地址,按回车后,完成第一步。 + +2) 视图层 views接收1)步发出的请求,Flask中使用解释器的方式处理这个求情,实例代码如下,它通常涉及到调用models层和模板文件层 + +```python +@main_blue.route('/', methods=['GET', 'POST']) +def index(): + form = TestForm() + print('test') +``` + +3) models层会负责创建数据模型,执行CRUD操作 + +4) 模板文件层处理html模板 + +5) 组合后返回html + +6) models层和html模板组合后返回给views层 + +7)最后views层响应并渲染到浏览器页面,我们就能看到请求的页面。 + +完整过程图如下所示: + +![image-20200211152007983](./img/web1.png) + +读者朋友们,如果你和例子君一样都是初学Flask编程,需要好好理解上面的过程。理解这些对于接下来的编程会有一定的理论指导,方向性指导价值。 + +### Python 问答 + +#### Python 如何生成二维码? + + + + + +## qrcode + +今天先来解答如何生成二维码。Python的`qrcode`包支持生成二维码。 + +用法也很简单: + +```python +import qrcode + +# 二维码内容 +data = "http://www.zglg.work/wp-content/uploads/2020/10/image-3.png" +# 生成二维码 +img = qrcode.make(data=data) +# 直接显示二维码 +img.show() +# 保存二维码为文件 +img.save("我的微信.jpg") +``` + +生成的二维码如下: + +![](https://imgkr2.cn-bj.ufileos.com/f0b08c53-0107-483b-bbe5-072bebc58e8d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=rVtaeBWhzLPPq%252BFCVtiOv6rS0tI%253D&Expires=1603544615) + + +大家微信扫描后,会出现我的二维码。 + +另外,还可以设置二维码的颜色等样式: + +```python +import qrcode + +# 实例化二维码生成类 +qr = qrcode.QRCode(border=2) +# 设置二维码数据 +data = "http://www.zglg.work/wp-content/uploads/2020/10/image-3.png" +qr.add_data(data=data) +# 启用二维码颜色设置 +qr.make(fit=True) +img = qr.make_image(fill_color="orange", back_color="white") + +# 显示二维码 +img.show() +``` + +生成一个orange的二维码: + +![](https://imgkr2.cn-bj.ufileos.com/cbd26fd8-27cf-4630-935f-6896822ce483.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=uy1r24x%252Fp5QpI5Wy10Ebdaz%252BpLM%253D&Expires=1603544681) + +更多样式,大家可以自己去玩耍。 + +## Python小项目:句子KWIC显示 + +上下文关键字(KWIC, Key Word In Context)是最常见的多行协调显示格式。 + +此小项目描述:输入一系列句子,给定一个给定单词,每个句子中至少会出现一次给定单词。目标输出,给定单词按照KWIC显示,KWIC显示的基本要求:待查询单词居中,前面`pre`序列右对齐,后面`post`序列左对齐,待查询单词前和后长度相等,若输入句子无法满足要求,用空格填充。 + +输入参数:输入句子sentences, 待查询单词selword, 滑动窗口长度`window_len` + +举例,输入如下六个句子,给定单词`secure`,输出如下字符串: + +```python + pre keyword post + + welfare , and secure the blessings of + nations , and secured immortal glory with + , and shall secure to you the + cherished . To secure us against these + defense as to secure our cities and + I can to secure economy and fidelity +``` + +请补充实现下面函数: + +```python +def kwic(sentences: List[str], selword: str, window_len: int) -> str: + """ + :type: sentences: input sentences + :type: selword: selected word + :type: window_len: window length + """ +``` + +更多KWIC显示参考如下: + +http://dep.chs.nihon-u.ac.jp/english_lang/tukamoto/kwic_e.html + +完整代码已经公布在:http://www.zglg.work/Python-20-topics/python-project1-kwic/ +![image](https://user-images.githubusercontent.com/20391209/123213609-c494dc00-d4f8-11eb-84d6-4d8caabb44f7.png) +![image](https://user-images.githubusercontent.com/20391209/123213901-26eddc80-d4f9-11eb-96cd-d3518005c4df.png) + diff --git a/md/1.md b/md/1.md index 38259505..817a25ea 100644 --- a/md/1.md +++ b/md/1.md @@ -25,6 +25,6 @@ relu(5) # 5 relu(-1) # 0 ``` - - -
[下一个例子](2.md)
+ + +
[下一个例子](2.md)
diff --git a/md/10.md b/md/10.md index 3cc76980..1fdec254 100644 --- a/md/10.md +++ b/md/10.md @@ -6,12 +6,39 @@ #### 10 转为整型   -int(x, base =10) , x可能为字符串或数值,将x 转换为一个普通整数。如果参数是字符串,那么它可能包含符号和小数点。如果超出了普通整数的表示范围,一个长整数被返回。 +int(x, base =10) , x可能为字符串或数值,将 x 转换为一个普通整数。 + +参数base指定x进制数,常见的2,8,10,16分别表示二进制、八进制、十进制、十六进制的数字 + +如果参数是字符串,必须为整数型字符串,如果是浮点数字符串会抛出异常。 + +如果x是浮点数,int后截去小数点,只保留整数部分。 ```python -In [1]: int('12',16) -Out[1]: 18 +In [2]: int('0110',2) + +Out[2]: 6 + +In [3]: int('0732',8) +Out[3]: 474 + +In [4]: int('12',16) +Out[4]: 18 + +In [5]: int('12',10) +Out[5]: 12 + +In [6]: int(1.45) +Out[6]: 1 + +In [7]: int('1.45') +--------------------------------------------------------------------------- +ValueError Traceback (most recent call last) + in +----> 1 int('1.45') + +ValueError: invalid literal for int() with base 10: '1.45' ``` -
[上一个例子](9.md) [下一个例子](11.md)
\ No newline at end of file +
[上一个例子](9.md) [下一个例子](11.md)
diff --git a/md/11.md b/md/11.md index 40874b42..31072399 100644 --- a/md/11.md +++ b/md/11.md @@ -9,9 +9,12 @@ base为底的exp次幂,如果mod给出,取余 ```python -In [1]: pow(3, 2, 4) +In [1]: pow(2,1.5) +Out[1]: 2.8284271247461903 + +In [1]: pow(3, 2, 4) # 3的2次方结果再对4取余数 Out[1]: 1 ``` -
[上一个例子](10.md) [下一个例子](12.md)
\ No newline at end of file +
[上一个例子](10.md) [下一个例子](12.md)
diff --git a/md/118.md b/md/118.md index ca14fe8d..8b3eaec1 100644 --- a/md/118.md +++ b/md/118.md @@ -23,7 +23,7 @@ print(t) # <_MainThread(MainThread, started 139908235814720)> 所以,验证了程序默认是在`MainThead`中执行。 -`t.getName()`获得这个线程的名字,其他常用方法,`getName()`获得线程`id`,`isAlive()`判断线程是否存活等。 +`t.getName()`获得这个线程的名字,其他常用方法,`t.ident`获得线程`id`,`isAlive()`判断线程是否存活等。 ```python print(t.getName()) # MainThread @@ -31,4 +31,4 @@ print(t.ident) # 139908235814720 print(t.isAlive()) # True ``` -
[上一个例子](117.md) [下一个例子](119.md)
\ No newline at end of file +
[上一个例子](117.md) [下一个例子](119.md)
diff --git a/md/123.md b/md/123.md index 20f70c47..ccff06f5 100644 --- a/md/123.md +++ b/md/123.md @@ -54,10 +54,10 @@ t8 adds a to 1: 9 t9 adds a to 1: 10 ``` -一起正常,其实这已经是单线程顺序执行了,就本例子而言,已经失去多线程的价值,并且还带来了因为线程创建开销,浪费时间的副作用。 +一切正常,其实这已经是单线程顺序执行了,就本例子而言,已经失去多线程的价值,并且还带来了因为线程创建开销,浪费时间的副作用。 程序中只有一把锁,通过 `try...finally`还能确保不发生死锁。但是,当程序中启用多把锁,还是很容易发生死锁。 注意使用场合,避免死锁,是我们在使用多线程开发时需要注意的一些问题。 -
[上一个例子](122.md) [下一个例子](124.md)
\ No newline at end of file +
[上一个例子](122.md) [下一个例子](124.md)
diff --git a/md/13.md b/md/13.md index 634ac883..3cc2f3c5 100644 --- a/md/13.md +++ b/md/13.md @@ -6,6 +6,8 @@ #### 13 链式比较 +Python支持这种连续不等比较,写起来更方便 + ```python i = 3 print(1 < i < 3) # False @@ -13,4 +15,4 @@ print(1 < i <= 3) # True ``` -
[上一个例子](12.md) [下一个例子](14.md)
\ No newline at end of file +
[上一个例子](12.md) [下一个例子](14.md)
diff --git a/md/14.md b/md/14.md index a66f7aca..061a1d8d 100644 --- a/md/14.md +++ b/md/14.md @@ -11,9 +11,20 @@ ```python In [12]: s = "apple" -In [13]: bytes(s,encoding='utf-8') -Out[13]: b'apple' +In [13]: a = bytes(s,encoding='utf-8') +In [14] a +Out[14]: b'apple' + +# 转化后a变为字节序列,bytes类型, +# 并且每个字符都被转化为数值,如下所示 +In [15]: for i in a: + ...: print(i) +97 +112 +112 +108 +101 ``` -
[上一个例子](13.md) [下一个例子](15.md)
\ No newline at end of file +
[上一个例子](13.md) [下一个例子](15.md)
diff --git a/md/15.md b/md/15.md index 93b443f5..b5127301 100644 --- a/md/15.md +++ b/md/15.md @@ -7,16 +7,30 @@ #### 15 任意对象转为字符串   ```python -In [14]: i = 100 + -In [15]: str(i) -Out[15]: '100' +In [1]: str(100) +Out[1]: '100' -In [16]: str([]) -Out[16]: '[]' +In [2]: str([3,2,10]) +Out[2]: '[3, 2, 10]' + +In [3]: str({'a':1, 'b':10}) +Out[3]: "{'a': 1, 'b': 10}" + +In [11]: from collections import defaultdict +In [12]: dd = defaultdict(int) + +In [14]: for i in [1,3,2,2,3,3]: + ...: dd[i] += 1 + ...: + +In [15]: dd +Out[15]: defaultdict(int, {1: 1, 3: 3, 2: 2}) + +In [16]: str(dd) +Out[16]: "defaultdict(, {1: 1, 3: 3, 2: 2})" -In [17]: str(tuple()) -Out[17]: '()' ``` -
[上一个例子](14.md) [下一个例子](16.md)
\ No newline at end of file +
[上一个例子](14.md) [下一个例子](16.md)
diff --git a/md/159.md b/md/159.md index 84bec8a9..98b7426f 100644 --- a/md/159.md +++ b/md/159.md @@ -13,7 +13,7 @@ ```python def del_item(lst,e): - return [lst.remove(i) for i in e if i==e] # NO! + return [lst.remove(i) for i in lst if i==e] # NO! ``` 考虑删除这个序列[1,3,3,3,5]中的元素3,结果发现只删除其中两个: @@ -31,4 +31,4 @@ def del_item(lst,e): ``` -
[上一个例子](158.md) [下一个例子](160.md)
\ No newline at end of file +
[上一个例子](158.md) [下一个例子](160.md)
diff --git a/md/16.md b/md/16.md index 45bccd41..5cc1a5a3 100644 --- a/md/16.md +++ b/md/16.md @@ -18,6 +18,18 @@ Out[3]: at 0x0000000005DE75D0, file "", line 1> In [4]: exec(r) helloworld + +s = """ +def f(): + a = 100 % 52 + print(a) +f() +""" +r = compile(s,"", "exec") +exec(r) ``` -
[上一个例子](15.md) [下一个例子](17.md)
\ No newline at end of file +输出 +48 + +
[上一个例子](15.md) [下一个例子](17.md)
diff --git a/md/17.md b/md/17.md index 33dbc7f5..3b3c360c 100644 --- a/md/17.md +++ b/md/17.md @@ -10,8 +10,24 @@ ```python In [1]: s = "1 + 3 +5" - ...: eval(s) -Out[1]: 9 + +In [2]: eval(s) +Out[2]: 9 + +s = ["{'小汽车':10, '面包车':8}", "{'面包车':5}"] +from collections import defaultdict +d = defaultdict(int) + +for item in s: + my_dict = eval(item) + print(type(my_dict)) + for key in my_dict: + d[key] += my_dict[key] +print(d) + + + +defaultdict(, {'小汽车': 10, '面包车': 13}) ``` -
[上一个例子](16.md) [下一个例子](18.md)
\ No newline at end of file +
[上一个例子](16.md) [下一个例子](18.md)
diff --git a/md/197.md b/md/197.md index 36935d52..3d166a53 100644 --- a/md/197.md +++ b/md/197.md @@ -6,6 +6,41 @@ @version @date 2020/04/04 ``` + +下面正则适用于提取正整数和大于0的浮点数,参看我的,若有疏漏欢迎补充。 + +```python +>>> import re +>>> pat_integ = '[1-9]+\d*' +>>> pat_float0 = '0\.\d+[1-9]' +>>> pat_float1 = '[1-9]\d*\.d+' +>>> pat = 'r%s|%s|%s'%(pat_float_0,pat_float_1,pat_integ) +>>> re.findall(pat, r) +['0.78', '3446.73', '0.91', '13642.95', '1.06', '2672.12', '3000'] +``` + +排除这些串: + +000 +000100 +0.00 +000.00 + + +解释:`*`表示前一个字符出现0次或多次,`+`表示前一个字符出现1次或多次,`\d`表示数字[0-9],`[1-9]`表示1,2,3,4,5,6,7,8,9,`\.`表示小数点 + +主要考虑:正整数最左侧一位大于0,大于1的浮点数必须以[1-9]开始,大于0小于1的浮点数小数点前只有1个0. + + + + +Day163:使用Python正则 提取出输入一段文字中的所有浮点数和整数 #Python拆书1# + +例如: 截至收盘,上证指数涨0.78%,报3446.73点,深证成指涨0.91%,报13642.95点,创业板指涨1.06%,报2672.12点。指数午后震荡走高,碳中和概念强者恒强,板块内上演涨停潮,环保、物业、特高压板块午后涨幅扩大,数字货币板块尾盘冲高,钢铁、煤炭、有色板块全天较为低迷,题材股午后整体回暖,两市上涨个股逾3000家,赚钱效益较好。 + +提取出所有浮点数和整数: 0.78, 3446.73, 0.91,13642.95 等 + + -
[上一个例子](196.md) [下一个例子](198.md)
\ No newline at end of file +
[上一个例子](196.md) [下一个例子](198.md)
diff --git a/md/198.md b/md/198.md index 566f6437..45709457 100644 --- a/md/198.md +++ b/md/198.md @@ -6,6 +6,23 @@ @version @date 2020/04/05 ``` - +## 1 常见算术运算 -
[上一个例子](197.md) [下一个例子](199.md)
\ No newline at end of file +```python +x, y = 3, 2 +print(x + y) # = 5 +print(x - y) # = 1 +print(x * y) # = 6 +print(x / y) # = 1.5 +print(x // y) # = 1 +print(x % y) # = 1 +print(-x) # = -3 +print(abs(-x)) # = 3 +print(int(3.9)) # = 3 +print(float(x)) # = 3.0 +print(x ** y) # = 9 +``` + +大多数操作符都是不言自明的。注意,`//`运算符执行整数除法。结果是一个向下舍入的整数值(例如,3//2==1) + +
[上一个例子](197.md) [下一个例子](199.md)
diff --git a/md/199.md b/md/199.md index 18c86a06..7df7ea58 100644 --- a/md/199.md +++ b/md/199.md @@ -1,11 +1,29 @@ ```markdown @author jackzhenguo -@desc +@desc 求两点球面距离 @tag @version @date 2020/04/06 ``` - + +```python +EARTH_RADIUS = 6378.137 + +import math +# 角度弧度计算公式 +def get_radian(degree): + return degree * 3.1415926 / 180.0 +# 根据经纬度计算两点之间的距离,得到的单位是 千米 +def get_distance(lat1,lng1,lat2,lng2): + radLat1 = get_radian(lat1) + radLat2 = get_radian(lat2) + a = radLat1 - radLat2 # 两点纬度差 + b = get_radian(lng1) - get_radian(lng2); # 两点的经度差 + s = 2 * math.asin(math.sqrt(math.pow(math.sin(a / 2), 2) + + math.cos(radLat1) * math.cos(radLat2) * math.pow(math.sin(b / 2), 2))); + s = s * EARTH_RADIUS + return s +```
[上一个例子](198.md) [下一个例子](200.md)
\ No newline at end of file diff --git a/md/2.md b/md/2.md index 22a5a27f..aa58feaa 100644 --- a/md/2.md +++ b/md/2.md @@ -8,21 +8,21 @@ 十进制转换为二进制: ```python -In [2]: bin(10) -Out[2]: '0b1010' +In [1]: bin(10) +Out[1]: '0b1010' ``` 十进制转换为八进制: ```python -In [3]: oct(9) -Out[3]: '0o11' +In [2]: oct(9) +Out[2]: '0o11' ``` 十进制转换为十六进制: ```python -In [4]: hex(15) -Out[4]: '0xf' +In [3]: hex(15) +Out[3]: '0xf' ``` - - + +
[上一个例子](1.md) [下一个例子](3.md)
\ No newline at end of file diff --git a/md/200.md b/md/200.md index 61f6eebc..ff873a60 100644 --- a/md/200.md +++ b/md/200.md @@ -1,11 +1,29 @@ ```markdown @author jackzhenguo -@desc +@desc 获取文件编码 @tag @version @date 2020/04/07 ``` - + +```python +import chardet +from chardet import UniversalDetector + +def get_encoding(file): + with open(file, "rb") as f: + cs = chardet.detect(f.read()) + return cs['encoding'] + + detector = UniversalDetector() + with open(file, "rb") as f: + for line in f.readlines(): + detector.feed(line) + if detector.done: + break + detector.close() + return detector.result +```
[上一个例子](199.md) [下一个例子](201.md)
\ No newline at end of file diff --git a/md/201.md b/md/201.md index c4e37277..7e323468 100644 --- a/md/201.md +++ b/md/201.md @@ -1,11 +1,25 @@ ```markdown @author jackzhenguo -@desc +@desc 格式化json串 @tag @version @date 2020/04/08 ``` +```python +import json + + +def format_json(json_str: str): + dic = json.loads(json_str) + + js = json.dumps(dic, + sort_keys=True, + ensure_ascii=False, + indent=4, + separators=(', ', ': ')) + return js +```
[上一个例子](200.md) [下一个例子](202.md)
\ No newline at end of file diff --git a/md/23.md b/md/23.md index 05fb188b..ac994a9e 100644 --- a/md/23.md +++ b/md/23.md @@ -6,6 +6,30 @@ #### 23 交换两元素 +理解交换两个元素,需要首先明白什么是pack,什么是unpack + +```python +In [1]: a=[3,1] + +# unpack +In [2]: a0,a1 = a + +In [3]: a0 +Out[3]: 3 + +In [4]: a1 +Out[4]: 1 + +# pack +In [5]: b = a0, a1 + +In [6]: b +Out[6]: (3, 1) + +``` + +所以下面 `b,a = a,b` 交换2个元素的过程,实际是先pack a,b为元组 (a,b),然后再unpack (a,b) 给 b, a的过程 + ```python def swap(a, b): return b, a @@ -14,4 +38,4 @@ def swap(a, b): print(swap(1, 0)) # (0,1) ``` -
[上一个例子](22.md) [下一个例子](24.md)
\ No newline at end of file +
[上一个例子](22.md) [下一个例子](24.md)
diff --git a/md/24.md b/md/24.md index 6aa000d2..22af8a0c 100644 --- a/md/24.md +++ b/md/24.md @@ -9,11 +9,9 @@ ```python In [31]: def f(): ...: print('i\'m f') - ...: In [32]: def g(): ...: print('i\'m g') - ...: In [33]: [f,g][1]() i'm g diff --git a/md/25.md b/md/25.md index 6a655eb4..f1e70f29 100644 --- a/md/25.md +++ b/md/25.md @@ -7,7 +7,8 @@ #### 25 生成逆序序列 ```python -list(range(10,-1,-1)) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] +list(range(10,-1,-1)) +# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] ``` 第三个参数为负时,表示从第一个参数开始递减,终止到第二个参数(不包括此边界) diff --git a/md/29.md b/md/29.md index a2282893..9998a648 100644 --- a/md/29.md +++ b/md/29.md @@ -9,17 +9,24 @@ 创建数据字典 ```python +# 方法1:使用dict In [1]: dict() Out[1]: {} - In [2]: dict(a='a',b='b') Out[2]: {'a': 'a', 'b': 'b'} +# 方法2:zip In [3]: dict(zip(['a','b'],[1,2])) Out[3]: {'a': 1, 'b': 2} +# 方法3:嵌入元组的列表 In [4]: dict([('a',1),('b',2)]) Out[4]: {'a': 1, 'b': 2} + +# 方法4:自典型字符串 +In [1]: s = "{'a':1, 'b':2}" +In [2]: eval(s) +Out[2]: {'a': 1, 'b': 2} ``` -
[上一个例子](28.md) [下一个例子](30.md)
\ No newline at end of file +
[上一个例子](28.md) [下一个例子](30.md)
diff --git a/md/31.md b/md/31.md index f2417ab3..c9a73545 100644 --- a/md/31.md +++ b/md/31.md @@ -9,10 +9,37 @@ 返回一个set对象,集合内不允许有重复元素: ```python -In [159]: a = [1,4,2,3,1] +In [1]: a = [1,4,2,3,1] -In [160]: set(a) -Out[160]: {1, 2, 3, 4} +In [2]: set(a) +Out[2]: {1, 2, 3, 4} + +In [3]: b = set(a) + +In [4]: b.add(5) + +In [5]: b +Out[5]: {1, 2, 3, 4, 5} + +In [6]: b.pop() +Out[6]: 1 + +In [7]: b +Out[7]: {2, 3, 4, 5} + +In [8]: b.pop() +Out[8]: 2 + +In [9]: b +Out[9]: {3, 4, 5} + +# 注意pop删除集合内任意一个元素 +In [10]: help(b.pop) +Help on built-in function pop: + +pop(...) method of builtins.set instance + Remove and return an arbitrary set element. + Raises KeyError if the set is empty. ``` -
[上一个例子](30.md) [下一个例子](32.md)
\ No newline at end of file +
[上一个例子](30.md) [下一个例子](32.md)
diff --git a/md/48.md b/md/48.md index 38073257..c62565fc 100644 --- a/md/48.md +++ b/md/48.md @@ -5,14 +5,22 @@ ``` #### 48 查看变量所占字节数 +`getsizeof`查看变量占用字节数 +看到:字典比列表占用更多空间 ```python In [1]: import sys -In [2]: a = {'a':1,'b':2.0} +In [3]: a = [('a',1),('b',2)] + +In [5]: sys.getsizeof(a) +Out[5]: 88 + + +In [6]: a = {'a':1,'b':2.0} +In [7]: sys.getsizeof(a) +Out[7]: 248 -In [3]: sys.getsizeof(a) # 占用240个字节 -Out[3]: 240 ``` -
[上一个例子](47.md) [下一个例子](49.md)
\ No newline at end of file +
[上一个例子](47.md) [下一个例子](49.md)
diff --git a/md/9.md b/md/9.md index b027f5ed..3cd61f78 100644 --- a/md/9.md +++ b/md/9.md @@ -1,6 +1,6 @@ ```markdown @author jackzhenguo -@desc +@desc 转为浮点类型  @date 2019/2/15 ``` #### 9 转为浮点类型  @@ -12,6 +12,25 @@ In [1]: float(3) Out[1]: 3.0 ``` +```python +In [1]: float('3') +Out[1]: 3.0 +``` + +浮点数最大值 +```python +import sys + +In[4]: sys.float_info.max +Out[4]: 1.7976931348623157e+308 +``` + +正无穷大、负无穷大 +```python +float('inf') # 正无穷大 +float('-inf') # 负无穷大 +``` + 如果不能转化为浮点数,则会报`ValueError`: ```python @@ -19,4 +38,4 @@ In [2]: float('a') # ValueError: could not convert string to float: 'a' ``` -
[上一个例子](8.md) [下一个例子](10.md)
\ No newline at end of file +
[上一个例子](8.md) [下一个例子](10.md)
diff --git a/script/.idea/.gitignore b/script/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/script/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/script/.idea/inspectionProfiles/Project_Default.xml b/script/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index e4919dcd..00000000 --- a/script/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - \ No newline at end of file diff --git a/script/.idea/inspectionProfiles/profiles_settings.xml b/script/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2da..00000000 --- a/script/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/script/.idea/misc.xml b/script/.idea/misc.xml deleted file mode 100644 index 8161a60d..00000000 --- a/script/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/script/.idea/modules.xml b/script/.idea/modules.xml deleted file mode 100644 index 7e8a5742..00000000 --- a/script/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/script/.idea/script.iml b/script/.idea/script.iml deleted file mode 100644 index 8dc09e54..00000000 --- a/script/.idea/script.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/script/.idea/vcs.xml b/script/.idea/vcs.xml deleted file mode 100644 index 6c0b8635..00000000 --- a/script/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/script/avatar.png b/script/avatar.png new file mode 100644 index 00000000..4455032c Binary files /dev/null and b/script/avatar.png differ