pyxllib 0.3.96__py3-none-any.whl → 0.3.200__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. pyxllib/__init__.py +21 -21
  2. pyxllib/algo/__init__.py +8 -8
  3. pyxllib/algo/disjoint.py +54 -54
  4. pyxllib/algo/geo.py +541 -529
  5. pyxllib/algo/intervals.py +964 -964
  6. pyxllib/algo/matcher.py +389 -311
  7. pyxllib/algo/newbie.py +166 -166
  8. pyxllib/algo/pupil.py +629 -461
  9. pyxllib/algo/shapelylib.py +67 -67
  10. pyxllib/algo/specialist.py +241 -240
  11. pyxllib/algo/stat.py +494 -458
  12. pyxllib/algo/treelib.py +149 -149
  13. pyxllib/algo/unitlib.py +66 -66
  14. {pyxlpr → pyxllib/autogui}/__init__.py +5 -5
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/{ext/autogui → autogui}/autogui.py +852 -823
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/{ext/autogui → autogui}/virtualkey.py +102 -102
  20. pyxllib/autogui/wechat.py +827 -0
  21. pyxllib/autogui/wechat_msg.py +421 -0
  22. pyxllib/autogui/wxautolib.py +84 -0
  23. pyxllib/cv/__init__.py +5 -5
  24. pyxllib/cv/expert.py +267 -267
  25. pyxllib/cv/imfile.py +159 -159
  26. pyxllib/cv/imhash.py +39 -39
  27. pyxllib/cv/pupil.py +9 -9
  28. pyxllib/cv/rgbfmt.py +1525 -1525
  29. pyxllib/cv/slidercaptcha.py +137 -0
  30. pyxllib/cv/trackbartools.py +251 -251
  31. pyxllib/cv/xlcvlib.py +1040 -1040
  32. pyxllib/cv/xlpillib.py +423 -423
  33. pyxllib/data/echarts.py +240 -129
  34. pyxllib/data/jsonlib.py +89 -0
  35. pyxllib/data/oss.py +72 -72
  36. pyxllib/data/pglib.py +1127 -643
  37. pyxllib/data/sqlite.py +568 -341
  38. pyxllib/data/sqllib.py +297 -297
  39. pyxllib/ext/JLineViewer.py +505 -492
  40. pyxllib/ext/__init__.py +6 -6
  41. pyxllib/ext/demolib.py +246 -246
  42. pyxllib/ext/drissionlib.py +277 -0
  43. pyxllib/ext/kq5034lib.py +12 -1606
  44. pyxllib/ext/old.py +663 -663
  45. pyxllib/ext/qt.py +449 -449
  46. pyxllib/ext/robustprocfile.py +497 -0
  47. pyxllib/ext/seleniumlib.py +76 -76
  48. pyxllib/ext/tk.py +173 -173
  49. pyxllib/ext/unixlib.py +827 -826
  50. pyxllib/ext/utools.py +351 -338
  51. pyxllib/ext/webhook.py +124 -101
  52. pyxllib/ext/win32lib.py +40 -40
  53. pyxllib/ext/wjxlib.py +88 -0
  54. pyxllib/ext/wpsapi.py +124 -0
  55. pyxllib/ext/xlwork.py +9 -0
  56. pyxllib/ext/yuquelib.py +1105 -173
  57. pyxllib/file/__init__.py +17 -17
  58. pyxllib/file/docxlib.py +761 -761
  59. pyxllib/file/gitlib.py +309 -309
  60. pyxllib/file/libreoffice.py +165 -0
  61. pyxllib/file/movielib.py +148 -139
  62. pyxllib/file/newbie.py +10 -10
  63. pyxllib/file/onenotelib.py +1469 -1469
  64. pyxllib/file/packlib/__init__.py +330 -293
  65. pyxllib/file/packlib/zipfile.py +2441 -2441
  66. pyxllib/file/pdflib.py +426 -426
  67. pyxllib/file/pupil.py +185 -185
  68. pyxllib/file/specialist/__init__.py +685 -685
  69. pyxllib/file/specialist/dirlib.py +799 -799
  70. pyxllib/file/specialist/download.py +193 -186
  71. pyxllib/file/specialist/filelib.py +2829 -2618
  72. pyxllib/file/xlsxlib.py +3131 -2976
  73. pyxllib/file/xlsyncfile.py +341 -0
  74. pyxllib/prog/__init__.py +5 -5
  75. pyxllib/prog/cachetools.py +64 -0
  76. pyxllib/prog/deprecatedlib.py +233 -233
  77. pyxllib/prog/filelock.py +42 -0
  78. pyxllib/prog/ipyexec.py +253 -253
  79. pyxllib/prog/multiprogs.py +940 -0
  80. pyxllib/prog/newbie.py +451 -444
  81. pyxllib/prog/pupil.py +1197 -1128
  82. pyxllib/prog/sitepackages.py +33 -33
  83. pyxllib/prog/specialist/__init__.py +391 -217
  84. pyxllib/prog/specialist/bc.py +203 -200
  85. pyxllib/prog/specialist/browser.py +497 -488
  86. pyxllib/prog/specialist/common.py +347 -347
  87. pyxllib/prog/specialist/datetime.py +199 -131
  88. pyxllib/prog/specialist/tictoc.py +240 -241
  89. pyxllib/prog/specialist/xllog.py +180 -180
  90. pyxllib/prog/xlosenv.py +108 -101
  91. pyxllib/stdlib/__init__.py +17 -17
  92. pyxllib/stdlib/tablepyxl/__init__.py +10 -10
  93. pyxllib/stdlib/tablepyxl/style.py +303 -303
  94. pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
  95. pyxllib/text/__init__.py +8 -8
  96. pyxllib/text/ahocorasick.py +39 -39
  97. pyxllib/text/airscript.js +744 -0
  98. pyxllib/text/charclasslib.py +121 -109
  99. pyxllib/text/jiebalib.py +267 -264
  100. pyxllib/text/jinjalib.py +32 -0
  101. pyxllib/text/jsa_ai_prompt.md +271 -0
  102. pyxllib/text/jscode.py +922 -767
  103. pyxllib/text/latex/__init__.py +158 -158
  104. pyxllib/text/levenshtein.py +303 -303
  105. pyxllib/text/nestenv.py +1215 -1215
  106. pyxllib/text/newbie.py +300 -288
  107. pyxllib/text/pupil/__init__.py +8 -8
  108. pyxllib/text/pupil/common.py +1121 -1095
  109. pyxllib/text/pupil/xlalign.py +326 -326
  110. pyxllib/text/pycode.py +47 -47
  111. pyxllib/text/specialist/__init__.py +8 -8
  112. pyxllib/text/specialist/common.py +112 -112
  113. pyxllib/text/specialist/ptag.py +186 -186
  114. pyxllib/text/spellchecker.py +172 -172
  115. pyxllib/text/templates/echart_base.html +11 -0
  116. pyxllib/text/templates/highlight_code.html +17 -0
  117. pyxllib/text/templates/latex_editor.html +103 -0
  118. pyxllib/text/vbacode.py +17 -17
  119. pyxllib/text/xmllib.py +747 -685
  120. pyxllib/xl.py +42 -38
  121. pyxllib/xlcv.py +17 -17
  122. pyxllib-0.3.200.dist-info/METADATA +48 -0
  123. pyxllib-0.3.200.dist-info/RECORD +126 -0
  124. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +1 -2
  125. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info/licenses}/LICENSE +190 -190
  126. pyxllib/ext/autogui/__init__.py +0 -8
  127. pyxllib-0.3.96.dist-info/METADATA +0 -51
  128. pyxllib-0.3.96.dist-info/RECORD +0 -333
  129. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  130. pyxlpr/ai/__init__.py +0 -5
  131. pyxlpr/ai/clientlib.py +0 -1281
  132. pyxlpr/ai/specialist.py +0 -286
  133. pyxlpr/ai/torch_app.py +0 -172
  134. pyxlpr/ai/xlpaddle.py +0 -655
  135. pyxlpr/ai/xltorch.py +0 -705
  136. pyxlpr/data/__init__.py +0 -11
  137. pyxlpr/data/coco.py +0 -1325
  138. pyxlpr/data/datacls.py +0 -365
  139. pyxlpr/data/datasets.py +0 -200
  140. pyxlpr/data/gptlib.py +0 -1291
  141. pyxlpr/data/icdar/__init__.py +0 -96
  142. pyxlpr/data/icdar/deteval.py +0 -377
  143. pyxlpr/data/icdar/icdar2013.py +0 -341
  144. pyxlpr/data/icdar/iou.py +0 -340
  145. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  146. pyxlpr/data/imtextline.py +0 -473
  147. pyxlpr/data/labelme.py +0 -866
  148. pyxlpr/data/removeline.py +0 -179
  149. pyxlpr/data/specialist.py +0 -57
  150. pyxlpr/eval/__init__.py +0 -85
  151. pyxlpr/paddleocr.py +0 -776
  152. pyxlpr/ppocr/__init__.py +0 -15
  153. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  154. pyxlpr/ppocr/data/__init__.py +0 -135
  155. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  156. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  157. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  158. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  159. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  160. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  161. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  162. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  163. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  164. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  165. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  166. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  167. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  168. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  169. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  170. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  171. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  172. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  173. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  174. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  175. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  176. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  177. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  178. pyxlpr/ppocr/losses/__init__.py +0 -61
  179. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  180. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  181. pyxlpr/ppocr/losses/center_loss.py +0 -88
  182. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  183. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  184. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  185. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  186. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  187. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  188. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  189. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  190. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  191. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  192. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  193. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  194. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  195. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  196. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  197. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  198. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  199. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  200. pyxlpr/ppocr/metrics/__init__.py +0 -44
  201. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  202. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  203. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  204. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  205. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  206. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  207. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  208. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  209. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  210. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  211. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  212. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  213. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  214. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  215. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  216. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  217. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  218. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  219. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  220. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  221. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  222. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  223. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  224. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  225. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  226. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  227. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  228. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  229. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  230. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  231. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  232. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  233. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  234. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  235. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  236. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  237. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  238. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  239. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  240. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  241. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  242. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  243. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  244. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  245. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  246. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  247. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  248. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  249. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  250. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  251. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  252. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  253. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  254. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  255. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  256. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  257. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  258. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  259. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  260. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  261. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  262. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  263. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  264. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  265. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  266. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  267. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  268. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  269. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  270. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  271. pyxlpr/ppocr/tools/__init__.py +0 -14
  272. pyxlpr/ppocr/tools/eval.py +0 -83
  273. pyxlpr/ppocr/tools/export_center.py +0 -77
  274. pyxlpr/ppocr/tools/export_model.py +0 -129
  275. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  276. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  277. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  278. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  279. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  280. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  281. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  282. pyxlpr/ppocr/tools/infer_det.py +0 -134
  283. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  284. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  285. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  286. pyxlpr/ppocr/tools/infer_table.py +0 -107
  287. pyxlpr/ppocr/tools/program.py +0 -596
  288. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  289. pyxlpr/ppocr/tools/train.py +0 -163
  290. pyxlpr/ppocr/tools/xlprog.py +0 -748
  291. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  292. pyxlpr/ppocr/utils/__init__.py +0 -24
  293. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  294. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  295. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  296. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  297. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  298. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  299. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  300. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  301. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  302. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  303. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  304. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  305. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  306. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  307. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  308. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  309. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  310. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  311. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  312. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  313. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  314. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  315. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  316. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  317. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  318. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  319. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  320. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  321. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  322. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  323. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  324. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  325. pyxlpr/ppocr/utils/dict90.txt +0 -90
  326. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  327. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  328. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  329. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  330. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  331. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  332. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  333. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  334. pyxlpr/ppocr/utils/gen_label.py +0 -81
  335. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  336. pyxlpr/ppocr/utils/iou.py +0 -54
  337. pyxlpr/ppocr/utils/logging.py +0 -69
  338. pyxlpr/ppocr/utils/network.py +0 -84
  339. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  340. pyxlpr/ppocr/utils/profiler.py +0 -110
  341. pyxlpr/ppocr/utils/save_load.py +0 -150
  342. pyxlpr/ppocr/utils/stats.py +0 -72
  343. pyxlpr/ppocr/utils/utility.py +0 -80
  344. pyxlpr/ppstructure/__init__.py +0 -13
  345. pyxlpr/ppstructure/predict_system.py +0 -187
  346. pyxlpr/ppstructure/table/__init__.py +0 -13
  347. pyxlpr/ppstructure/table/eval_table.py +0 -72
  348. pyxlpr/ppstructure/table/matcher.py +0 -192
  349. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  350. pyxlpr/ppstructure/table/predict_table.py +0 -221
  351. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  352. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  353. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  354. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  355. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  356. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  357. pyxlpr/ppstructure/utility.py +0 -71
  358. pyxlpr/xlai.py +0 -10
pyxllib/algo/pupil.py CHANGED
@@ -1,461 +1,629 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2021/06/03 14:22
6
-
7
- import textwrap
8
- from collections import defaultdict, Counter
9
- import math
10
- import re
11
- import sys
12
-
13
- from pyxllib.prog.newbie import typename, human_readable_number
14
- from pyxllib.text.pupil import listalign, int2myalphaenum
15
-
16
-
17
- def natural_sort_key(key):
18
- """
19
- >>> natural_sort_key('0.0.43') < natural_sort_key('0.0.43.1')
20
- True
21
-
22
- >>> natural_sort_key('0.0.2') < natural_sort_key('0.0.12')
23
- True
24
- """
25
-
26
- def convert(text):
27
- return int(text) if text.isdigit() else text.lower()
28
-
29
- return [convert(c) for c in re.split('([0-9]+)', str(key))]
30
-
31
-
32
- def natural_sort(ls, only_use_digits=False):
33
- """ 自然排序
34
-
35
- :param only_use_digits: 正常会用数字作为分隔,切割每一部分进行比较
36
- 如果只想比较数值部分,可以only_use_digits=True
37
-
38
- >>> natural_sort(['0.1.12', '0.0.10', '0.0.23'])
39
- ['0.0.10', '0.0.23', '0.1.12']
40
- """
41
- if only_use_digits:
42
- def func(key):
43
- return [int(c) for c in re.split('([0-9]+)', str(key)) if c.isdigit()]
44
- else:
45
- func = natural_sort_key
46
- return sorted(ls, key=func)
47
-
48
-
49
- def argsort(seq):
50
- # http://stackoverflow.com/questions/3071415/efficient-method-to-calculate-the-rank-vector-of-a-list-in-python
51
- return sorted(range(len(seq)), key=seq.__getitem__)
52
-
53
-
54
- def make_index_function(li, *, start=0, nan=None):
55
- """ 返回一个函数,输入值,返回对应下标,找不到时返回 not_found
56
-
57
- :param li: 列表数据
58
- :param start: 起始下标
59
- :param nan: 找不到对应元素时的返回值
60
- 注意这里找不到默认不是-1,而是li的长度,这样用于排序时,找不到的默认会排在尾巴
61
-
62
- >>> func = make_index_function(['少儿', '小学', '初中', '高中'])
63
- >>> sorted(['初中', '小学', '高中'], key=func)
64
- ['小学', '初中', '高中']
65
-
66
- # 不在枚举项目里的,会统一列在最后面
67
- >>> sorted(['初中', '小学', '高中', '幼儿'], key=func)
68
- ['小学', '初中', '高中', '幼儿']
69
- """
70
- data = {x: i for i, x in enumerate(li, start=start)}
71
- if nan is None:
72
- nan = len(li)
73
-
74
- def warpper(x, default=None):
75
- if default is None:
76
- default = nan
77
- return data.get(x, default)
78
-
79
- return warpper
80
-
81
-
82
- class ValuesStat:
83
- """ 一串数值的相关统计分析 """
84
-
85
- def __init__(self, values):
86
- from statistics import pstdev, mean
87
- self.values = values
88
- self.n = len(values)
89
- self.sum = sum(values)
90
- if self.n:
91
- self.mean = mean(self.values)
92
- self.std = pstdev(self.values)
93
- self.min, self.max = min(values), max(values)
94
- else:
95
- self.mean = self.std = self.min = self.max = float('nan')
96
-
97
- def __len__(self):
98
- return self.n
99
-
100
- def summary(self, valfmt=lambda x: human_readable_number(x, '万', 4)):
101
- """ 输出性能分析报告,data是每次运行得到的时间数组
102
-
103
- :param valfmt: 数值显示的格式
104
- g是比较智能的一种模式
105
- 也可以用 '.3f'表示保留3位小数
106
- 可以是一个函数,该函数接收一个数值作为输入,返回格式化后的字符串
107
- 注意可以写None表示删除特定位的显示
108
-
109
- 也可以传入长度5的格式清单,表示 [和、均值、标准差、最小值、最大值] 一次展示的格式
110
- """
111
- if isinstance(valfmt, str) or callable(valfmt):
112
- valfmt = [valfmt] * 6
113
-
114
- if len(valfmt) == 5: # 兼容旧版格式化,默认是不填充"总数"的格式化的
115
- valfmt = [lambda x: x] + valfmt
116
- assert len(valfmt) == 6, f'valfmt长度必须是6,现在是{len(valfmt)}'
117
-
118
- ls = []
119
-
120
- def format_value(value, fmt_id):
121
- """ 根据指定的格式来格式化值 """
122
- format_spec = valfmt[fmt_id]
123
- if format_spec is None:
124
- return ''
125
-
126
- if callable(format_spec):
127
- return format_spec(value)
128
- else:
129
- return f"{value:{format_spec}}"
130
-
131
- if self.n > 1:
132
- ls.append(f'总数: {format_value(self.n, 0)}') # 注意输出其实完整是6个值,还有个总数不用控制格式
133
- if valfmt[1]:
134
- ls.append(f'总和: {format_value(self.sum, 1)}')
135
- if valfmt[2] or valfmt[3]:
136
- mean_str = format_value(self.mean, 2)
137
- std_str = format_value(self.std, 3)
138
- if mean_str and std_str:
139
- ls.append(f'均值标准差: {mean_str}±{std_str}')
140
- elif mean_str:
141
- ls.append(f'均值: {mean_str}')
142
- elif std_str:
143
- ls.append(f'标准差: {std_str}')
144
- if valfmt[4]:
145
- ls.append(f'最小值: {format_value(self.min, 4)}')
146
- if valfmt[5]:
147
- ls.append(f'最大值: {format_value(self.max, 5)}')
148
- return '\t'.join(ls)
149
- elif self.n == 1:
150
- return format_value(self.sum, 1)
151
- else:
152
- raise ValueError("无效的数据数量")
153
-
154
-
155
- class Groups:
156
- def __init__(self, data):
157
- """ 分组
158
-
159
- :param data: 输入字典结构直接赋值
160
- 或者其他结构,会自动按相同项聚合
161
-
162
- TODO 显示一些数值统计信息,甚至图表
163
- TODO 转文本表达,方便bc比较
164
- """
165
- if not isinstance(data, dict):
166
- new_data = dict()
167
- # 否要要转字典类型,自动从1~n编组
168
- for k, v in enumerate(data, start=1):
169
- new_data[k] = v
170
- data = new_data
171
- self.data = data # 字典存原数据
172
- self.ctr = Counter({k: len(x) for k, x in self.data.items()}) # 计数
173
- self.stat = ValuesStat(self.ctr.values()) # 综合统计数据
174
-
175
- def __repr__(self):
176
- ls = []
177
- for i, (k, v) in enumerate(self.data.items(), start=1):
178
- ls.append(f'{i}, {k}:{v}')
179
- return '\n'.join(ls)
180
-
181
- @classmethod
182
- def groupby(cls, ls, key, ykey=None):
183
- """
184
- :param ls: 可迭代等数组类型
185
- :param key: 映射规则,ls中每个元素都会被归到映射的key组上
186
- Callable[Any, 不可变类型]
187
- None,未输入时,默认输入的ls已经是分好组的数据
188
- :param ykey: 是否对分组后存储的内容y,也做一个函数映射
189
- :return: dict
190
- """
191
- data = defaultdict(list)
192
- for x in ls:
193
- k = key(x)
194
- if ykey:
195
- x = ykey(x)
196
- data[k].append(x)
197
- return cls(data)
198
-
199
-
200
- def intersection_split(a, b):
201
- """ 输入两个对象a,b,可以是dict或set类型,list等
202
-
203
- 会分析出二者共有的元素值关系
204
- 返回值是 ls1, ls2, ls3, ls4,大部分是list类型,但也有可能遵循原始情况是set类型
205
- ls1:a中,与b共有key的元素值
206
- ls2:a中,独有key的元素值
207
- ls3:b中,与a共有key的元素值
208
- ls4:b中,独有key的元素值
209
- """
210
- # 1 获得集合的key关系
211
- keys1 = set(a)
212
- keys2 = set(b)
213
- keys0 = keys1 & keys2 # 两个集合共有的元素
214
-
215
- # TODO 如果是字典,希望能保序
216
-
217
- # 2 组合出ls1、ls2、ls3、ls4
218
-
219
- def split(t, s, ks):
220
- """原始元素为t,集合化的值为s,共有key是ks"""
221
- if isinstance(t, (set, list, tuple)):
222
- return ks, s - ks
223
- elif isinstance(t, dict):
224
- ls1 = sorted(map(lambda x: (x, t[x]), ks), key=lambda x: natural_sort_key(x[0]))
225
- ls2 = sorted(map(lambda x: (x, t[x]), s - ks), key=lambda x: natural_sort_key(x[0]))
226
- return ls1, ls2
227
- else:
228
- # dprint(type(s)) # s不是可以用来进行集合规律分析的类型
229
- raise ValueError(f'{type(s)}不是可以用来进行集合规律分析的类型')
230
-
231
- ls1, ls2 = split(a, keys1, keys0)
232
- ls3, ls4 = split(b, keys2, keys0)
233
- return ls1, ls2, ls3, ls4
234
-
235
-
236
- def matchpairs(xs, ys, cmp_func, least_score=sys.float_info.epsilon, *,
237
- key=None, index=False):
238
- r""" 匹配两组数据
239
-
240
- :param xs: 第一组数据
241
- :param ys: 第二组数据
242
- :param cmp_func: 所用的比较函数,值越大表示两个对象相似度越高
243
- :param least_score: 允许匹配的最低分,默认必须要大于0
244
- :param key: 是否需要对xs, ys进行映射后再传入 cmp_func 操作
245
- :param index: 返回的不是原值,而是下标
246
- :return: 返回结构[(x1, y1, score1), (x2, y2, score2), ...],注意长度肯定不会超过min(len(xs), len(ys))
247
-
248
- 注意:这里的功能①不支持重复匹配,②任何一个x,y都有可能没有匹配到
249
- 如果每个x必须都要有一个匹配,或者支持重复配对,请到隔壁使用 MatchPairs
250
-
251
- TODO 这里很多中间步骤结果都是很有分析价值的,能改成类,然后支持分析中间结果?
252
- TODO 这样全量两两比较是很耗性能的,可以加个参数草算,不用精确计算的功能?
253
-
254
- >>> xs, ys = [4, 6, 1, 2, 9, 4, 5], [1, 5, 8, 9, 2]
255
- >>> cmp_func = lambda x,y: 1-abs(x-y)/max(x,y)
256
- >>> matchpairs(xs, ys, cmp_func)
257
- [(1, 1, 1.0), (2, 2, 1.0), (9, 9, 1.0), (5, 5, 1.0), (6, 8, 0.75)]
258
- >>> matchpairs(ys, xs, cmp_func)
259
- [(1, 1, 1.0), (5, 5, 1.0), (9, 9, 1.0), (2, 2, 1.0), (8, 6, 0.75)]
260
- >>> matchpairs(xs, ys, cmp_func, 0.9)
261
- [(1, 1, 1.0), (2, 2, 1.0), (9, 9, 1.0), (5, 5, 1.0)]
262
- >>> matchpairs(xs, ys, cmp_func, 0.9, index=True)
263
- [(2, 0, 1.0), (3, 4, 1.0), (4, 3, 1.0), (6, 1, 1.0)]
264
- """
265
- # 0 实际计算使用的是 xs_, ys_
266
- if key:
267
- xs_ = [key(x) for x in xs]
268
- ys_ = [key(y) for y in ys]
269
- else:
270
- xs_, ys_ = xs, ys
271
-
272
- # 1 计算所有两两相似度
273
- n, m = len(xs), len(ys)
274
- all_pairs = []
275
- for i in range(n):
276
- for j in range(m):
277
- score = cmp_func(xs_[i], ys_[j])
278
- if score >= least_score:
279
- all_pairs.append([i, j, score])
280
- # 按分数权重排序,如果分数有很多相似并列,就只能按先来后到排序啦
281
- all_pairs = sorted(all_pairs, key=lambda v: (-v[2], v[0], v[1]))
282
-
283
- # 2 过滤出最终结果
284
- pairs = []
285
- x_used, y_used = set(), set()
286
- for p in all_pairs:
287
- i, j, score = p
288
- if i not in x_used and j not in y_used:
289
- if index:
290
- pairs.append((i, j, score))
291
- else:
292
- pairs.append((xs[i], ys[j], score))
293
- x_used.add(i)
294
- y_used.add(j)
295
-
296
- return pairs
297
-
298
-
299
- def get_number_width(n):
300
- """ 判断数值n的长度
301
-
302
- >>> get_number_width(0)
303
- Traceback (most recent call last):
304
- AssertionError
305
- >>> get_number_width(9)
306
- 1
307
- >>> get_number_width(10)
308
- 2
309
- >>> get_number_width(97)
310
- 2
311
- """
312
- assert n > 0
313
- return math.ceil(math.log10(n + 1))
314
-
315
-
316
- class SearchBase:
317
- """ 一个dfs、bfs模板类 """
318
-
319
- def __init__(self, root):
320
- """
321
- Args:
322
- root: 根节点
323
- """
324
- self.root = root
325
-
326
- def get_neighbors(self, node):
327
- """ 获得邻接节点,必须要用yield实现,方便同时支持dfs、bfs的使用
328
-
329
- 对于树结构而言,相当于获取直接子结点
330
-
331
- 这里默认是bs4中Tag规则;不同业务需求,可以重定义该函数
332
- 例如对图结构、board类型,可以在self存储图访问状态,在这里实现遍历四周的功能
333
- """
334
- try:
335
- for node in node.children:
336
- yield node
337
- except AttributeError:
338
- pass
339
-
340
- def dfs_nodes(self, node=None, depth=0):
341
- """ 返回深度优先搜索得到的结点清单
342
-
343
- :param node: 起始结点,默认是root根节点
344
- :param depth: 当前node深度
345
- :return: list,[(node1, depth1), (node2, depth2), ...]
346
- """
347
- if not node:
348
- node = self.root
349
-
350
- ls = [(node, depth)]
351
- for t in self.get_neighbors(node):
352
- ls += self.dfs_nodes(t, depth + 1)
353
- return ls
354
-
355
- def bfs_nodes(self, node=None, depth=0):
356
- if not node:
357
- node = self.root
358
-
359
- ls = [(node, depth)]
360
- i = 0
361
-
362
- while i < len(ls):
363
- x, d = ls[i]
364
- nodes = self.get_neighbors(x)
365
- ls += [(t, d + 1) for t in nodes]
366
- i += 1
367
-
368
- return ls
369
-
370
- def fmt_node(self, node, depth, *, prefix=' ', show_node_type=False):
371
- """ node格式化显示 """
372
- s1 = prefix * depth
373
- s2 = typename(node) + ',' if show_node_type else ''
374
- s3 = textwrap.shorten(str(node), 200)
375
- return s1 + s2 + s3
376
-
377
- def fmt_nodes(self, *, nodes=None, select_depth=None, linenum=False,
378
- msghead=True, show_node_type=False, prefix=' '):
379
- """ 结点清单格式化输出
380
-
381
- :param nodes: 默认用dfs获得结点,也可以手动指定结点
382
- :param prefix: 缩进格式,默认用4个空格
383
- :param select_depth: 要显示的深度
384
- 单个数字:获得指定层
385
- Sequences: 两个整数,取出这个闭区间内的层级内容
386
- :param linenum:节点从1开始编号
387
- 行号后面,默认会跟一个类似Excel列名的字母,表示层级深度
388
- :param msghead: 第1行输出一些统计信息
389
- :param show_node_type:
390
-
391
- Requires
392
- textwrap:用到shorten
393
- align.listalign:生成列编号时对齐
394
- """
395
- # 1 生成结点清单
396
- ls = nodes if nodes else self.dfs_nodes()
397
- total_node = len(ls)
398
- total_depth = max(map(lambda x: x[1], ls))
399
- head = f'总节点数:1~{total_node},总深度:0~{total_depth}'
400
-
401
- # 2 过滤与重新整理ls(select_depth)
402
- logo = True
403
- cnt = 0
404
- tree_num = 0
405
- if isinstance(select_depth, int):
406
-
407
- for i in range(total_node):
408
- if ls[i][1] == select_depth:
409
- ls[i][1] = 0
410
- cnt += 1
411
- logo = True
412
- elif ls[i][1] < select_depth and logo: # 遇到第1个父节点添加一个空行
413
- ls[i] = ''
414
- tree_num += 1
415
- logo = False
416
- else: # 删除该节点,不做任何显示
417
- ls[i] = None
418
- head += f';挑选出的节点数:{cnt},所选深度:{select_depth},树数量:{tree_num}'
419
-
420
- elif hasattr(select_depth, '__getitem__'):
421
- for i in range(total_node):
422
- if select_depth[0] <= ls[i][1] <= select_depth[1]:
423
- ls[i][1] -= select_depth[0]
424
- cnt += 1
425
- logo = True
426
- elif ls[i][1] < select_depth[0] and logo: # 遇到第1个父节点添加一个空行
427
- ls[i] = ''
428
- tree_num += 1
429
- logo = False
430
- else: # 删除该节点,不做任何显示
431
- ls[i] = None
432
- head += f';挑选出的节点数:{cnt},所选深度:{select_depth[0]}~{select_depth[1]},树数量:{tree_num}'
433
- """注意此时ls[i]的状态,有3种类型
434
- (node, depth):tuple类型,第0个元素是node对象,第1个元素是该元素所处层级
435
- None:已删除元素,但为了后续编号方便,没有真正的移出,而是用None作为标记
436
- '':已删除元素,但这里涉及父节点的删除,建议此处留一个空行
437
- """
438
-
439
- # 3 格式处理
440
- def mystr(item):
441
- return self.fmt_node(item[0], item[1], prefix=prefix, show_node_type=show_node_type)
442
-
443
- line_num = listalign(range(1, total_node + 1))
444
- res = []
445
- for i in range(total_node):
446
- if ls[i] is not None:
447
- if isinstance(ls[i], str): # 已经指定该行要显示什么
448
- res.append(ls[i])
449
- else:
450
- if linenum: # 增加了一个能显示层级的int2excel_col_name
451
- res.append(line_num[i] + int2myalphaenum(ls[i][1]) + ' ' + mystr(ls[i]))
452
- else:
453
- res.append(mystr(ls[i]))
454
-
455
- s = '\n'.join(res)
456
-
457
- # 是否要添加信息头
458
- if msghead:
459
- s = head + '\n' + s
460
-
461
- return s
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/06/03 14:22
6
+
7
+ from bisect import bisect_right
8
+ from collections import defaultdict, Counter
9
+ import datetime
10
+ import re
11
+ from statistics import quantiles
12
+ import sys
13
+ import textwrap
14
+
15
+ from pyxllib.prog.newbie import typename, human_readable_number
16
+ from pyxllib.text.pupil import listalign, int2myalphaenum
17
+
18
+
19
+ def natural_sort_key(key):
20
+ """
21
+ >>> natural_sort_key('0.0.43') < natural_sort_key('0.0.43.1')
22
+ True
23
+
24
+ >>> natural_sort_key('0.0.2') < natural_sort_key('0.0.12')
25
+ True
26
+ """
27
+
28
+ def convert(text):
29
+ return int(text) if text.isdigit() else text.lower()
30
+
31
+ return [convert(c) for c in re.split('([0-9]+)', str(key))]
32
+
33
+
34
+ def natural_sort(ls, only_use_digits=False):
35
+ """ 自然排序
36
+
37
+ :param only_use_digits: 正常会用数字作为分隔,切割每一部分进行比较
38
+ 如果只想比较数值部分,可以only_use_digits=True
39
+
40
+ >>> natural_sort(['0.1.12', '0.0.10', '0.0.23'])
41
+ ['0.0.10', '0.0.23', '0.1.12']
42
+ """
43
+ if only_use_digits:
44
+ def func(key):
45
+ return [int(c) for c in re.split('([0-9]+)', str(key)) if c.isdigit()]
46
+ else:
47
+ func = natural_sort_key
48
+ return sorted(ls, key=func)
49
+
50
+
51
+ def argsort(seq):
52
+ # http://stackoverflow.com/questions/3071415/efficient-method-to-calculate-the-rank-vector-of-a-list-in-python
53
+ return sorted(range(len(seq)), key=seq.__getitem__)
54
+
55
+
56
+ def make_index_function(li, *, start=0, nan=None):
57
+ """ 返回一个函数,输入值,返回对应下标,找不到时返回 not_found
58
+
59
+ :param li: 列表数据
60
+ :param start: 起始下标
61
+ :param nan: 找不到对应元素时的返回值
62
+ 注意这里找不到默认不是-1,而是li的长度,这样用于排序时,找不到的默认会排在尾巴
63
+
64
+ >>> func = make_index_function(['少儿', '小学', '初中', '高中'])
65
+ >>> sorted(['初中', '小学', '高中'], key=func)
66
+ ['小学', '初中', '高中']
67
+
68
+ # 不在枚举项目里的,会统一列在最后面
69
+ >>> sorted(['初中', '小学', '高中', '幼儿'], key=func)
70
+ ['小学', '初中', '高中', '幼儿']
71
+ """
72
+ data = {x: i for i, x in enumerate(li, start=start)}
73
+ if nan is None:
74
+ nan = len(li)
75
+
76
+ def warpper(x, default=None):
77
+ if default is None:
78
+ default = nan
79
+ return data.get(x, default)
80
+
81
+ return warpper
82
+
83
+
84
+ class ValuesStat:
85
+ """ 一串数值的相关统计分析 """
86
+
87
+ def __init__(self, values):
88
+ from statistics import pstdev, mean
89
+ self.values = values
90
+ self.n = len(values)
91
+ self.sum = sum(values)
92
+ if self.n:
93
+ self.mean = mean(self.values)
94
+ self.std = pstdev(self.values)
95
+ self.min, self.max = min(values), max(values)
96
+ else:
97
+ self.mean = self.std = self.min = self.max = float('nan')
98
+
99
+ def __len__(self):
100
+ return self.n
101
+
102
+ def summary(self, valfmt=lambda x: human_readable_number(x, '万', 4)):
103
+ """ 输出性能分析报告,data是每次运行得到的时间数组
104
+
105
+ :param valfmt: 数值显示的格式
106
+ g是比较智能的一种模式
107
+ 也可以用 '.3f'表示保留3位小数
108
+ 可以是一个函数,该函数接收一个数值作为输入,返回格式化后的字符串
109
+ 注意可以写None表示删除特定位的显示
110
+
111
+ 也可以传入长度5的格式清单,表示 [和、均值、标准差、最小值、最大值] 一次展示的格式
112
+ """
113
+ if isinstance(valfmt, str) or callable(valfmt):
114
+ valfmt = [valfmt] * 6
115
+
116
+ if len(valfmt) == 5: # 兼容旧版格式化,默认是不填充"总数"的格式化的
117
+ valfmt = [lambda x: x] + valfmt
118
+ assert len(valfmt) == 6, f'valfmt长度必须是6,现在是{len(valfmt)}'
119
+
120
+ ls = []
121
+
122
+ def format_value(value, fmt_id):
123
+ """ 根据指定的格式来格式化值 """
124
+ format_spec = valfmt[fmt_id]
125
+ if format_spec is None:
126
+ return ''
127
+
128
+ if callable(format_spec):
129
+ return format_spec(value)
130
+ else:
131
+ return f"{value:{format_spec}}"
132
+
133
+ if self.n > 1:
134
+ ls.append(f'总数: {format_value(self.n, 0)}') # 注意输出其实完整是6个值,还有个总数不用控制格式
135
+ if valfmt[1]:
136
+ ls.append(f'总和: {format_value(self.sum, 1)}')
137
+ if valfmt[2] or valfmt[3]:
138
+ mean_str = format_value(self.mean, 2)
139
+ std_str = format_value(self.std, 3)
140
+ if mean_str and std_str:
141
+ ls.append(f'均值标准差: {mean_str}±{std_str}')
142
+ elif mean_str:
143
+ ls.append(f'均值: {mean_str}')
144
+ elif std_str:
145
+ ls.append(f'标准差: {std_str}')
146
+ if valfmt[4]:
147
+ ls.append(f'最小值: {format_value(self.min, 4)}')
148
+ if valfmt[5]:
149
+ ls.append(f'最大值: {format_value(self.max, 5)}')
150
+ return '\t'.join(ls)
151
+ elif self.n == 1:
152
+ return format_value(self.sum, 1)
153
+ else:
154
+ raise ValueError("无效的数据数量")
155
+
156
+
157
+ class ValuesStat2:
158
+ """ 240509周四17:33,第2代统计器
159
+
160
+ 240628周五14:05 todo 关于各种特殊格式数据,怎么计算是个问题
161
+ 这问题可能有些复杂,近期估计没空折腾,留以后有空折腾的一个大坑了
162
+ """
163
+
164
+ def __init__(self, values=None, raw_values=None, data_type=None):
165
+ from statistics import pstdev, mean
166
+
167
+ # 支持输入可能带有非数值类型的raw_values
168
+ data_type = data_type or ''
169
+ if raw_values:
170
+ if 'timestamp' in data_type:
171
+ values = [x.timestamp() for x in raw_values if hasattr(x, 'timestamp')]
172
+ else:
173
+ values = [x for x in raw_values if isinstance(x, (int, float))] # todo 可能需要更泛用的判断数值的方法
174
+
175
+ self.date_type = data_type
176
+ self.raw_values = raw_values
177
+ values = values or []
178
+ self.values = sorted(values)
179
+ if self.raw_values:
180
+ self.raw_n = len(self.raw_values)
181
+ else:
182
+ self.raw_n = 0
183
+ self.n = len(values)
184
+
185
+ if 'timestamp' in data_type:
186
+ self.sum = None
187
+ else:
188
+ self.sum = sum(values)
189
+
190
+ if self.n:
191
+ self.mean = mean(self.values)
192
+ self.std = pstdev(self.values)
193
+ self.min, self.max = self.values[0], self.values[-1]
194
+ else:
195
+ self.mean = self.std = self.min = self.max = None
196
+
197
+ self.dist = None
198
+
199
+ def __len__(self):
200
+ return self.n
201
+
202
+ def _summary(self, unit=None, precision=4, percentile_count=5):
203
+ """ 返回字典结构的总结 """
204
+ """ 文本汇总性的报告
205
+
206
+ :param percentile_count: 包括两个极值端点的切分点数,
207
+ 设置2,就是不设置分位数,就是只展示最小、最大值
208
+ 如果设置了3,就表示"中位数、二分位数",在展示的时候,会显示50%位置的分位数值
209
+ 如果设置了5,就相当于"四分位数",会显示25%、50%、75%位置的分位数值
210
+ :param unit: 展示数值时使用的单位
211
+ :param precision: 展示数值时的精度
212
+ """
213
+
214
+ # 1 各种细分的格式化方法
215
+ def fmt0(v):
216
+ # 数量类整数的格式
217
+ return human_readable_number(v, '万')
218
+
219
+ def fmt1(v):
220
+ if isinstance(v, str):
221
+ return v
222
+ return human_readable_number(v, unit or 'K', precision)
223
+
224
+ def fmt2(v):
225
+ # 日期类数据的格式化
226
+ # todo 这个应该数据的具体格式来设置的,但是这个现在有点难写,先写死
227
+ if isinstance(v, str):
228
+ return v
229
+ elif isinstance(v, (int, float)):
230
+ v = datetime.datetime.fromtimestamp(v)
231
+
232
+ return v.strftime(unit or '%Y-%m-%d %H:%M:%S')
233
+
234
+ def fmt2b(v):
235
+ # 时间长度类数据的格式化
236
+ return human_readable_number(v, '秒')
237
+
238
+ if 'timestamp' in self.date_type:
239
+ fmt = fmt2
240
+ fmtb = fmt2b
241
+ else:
242
+ fmt = fmtb = fmt1
243
+
244
+ # 2 生成统计报告
245
+ desc = {}
246
+ if self.raw_n and self.raw_n > self.n:
247
+ desc["总数"] = f"{fmt0(self.n)}/{fmt0(self.raw_n)}≈{self.n / self.raw_n:.2%}"
248
+ else:
249
+ desc["总数"] = f"{fmt0(self.n)}"
250
+
251
+ if self.sum is not None:
252
+ desc["总和"] = f"{fmt(self.sum)}"
253
+ if self.mean is not None and self.std is not None:
254
+ desc["均值±标准差"] = f"{fmt(self.mean)}±{fmtb(self.std)}"
255
+ elif self.mean is not None:
256
+ desc["均值"] = f"{fmt(self.mean)}"
257
+ elif self.std is not None:
258
+ desc["标准差"] = f"{fmtb(self.std)}"
259
+
260
+ if self.values:
261
+ dist = [self.values[0]]
262
+ if percentile_count > 2:
263
+ quartiles = quantiles(self.values, n=percentile_count - 1)
264
+ dist += quartiles
265
+ dist.append(self.values[-1])
266
+
267
+ desc["分布"] = '/'.join([fmt(v) for v in dist])
268
+ elif self.dist:
269
+ desc["分布"] = '/'.join([fmt(v) for v in self.dist])
270
+
271
+ return desc
272
+
273
+ def summary(self, unit=None, precision=4, percentile_count=5):
274
+ """ 文本汇总性的报告
275
+
276
+ :param unit: 展示数值时使用的单位
277
+ :param precision: 展示数值时的精度
278
+ :param percentile_count: 包括两个极值端点的切分点数,
279
+ 设置2,就是不设置分位数,就是只展示最小、最大值
280
+ 如果设置了3,就表示"中位数、二分位数",在展示的时候,会显示50%位置的分位数值
281
+ 如果设置了5,就相当于"四分位数",会显示25%、50%、75%位置的分位数值
282
+ """
283
+ desc = self._summary(unit, precision, percentile_count)
284
+ return '\t'.join([f"{key}: {value}" for key, value in desc.items()])
285
+
286
+ def calculate_ratios(self, x_values, fmt=False, unit=False):
287
+ """ 计算并返回一个字典,其中包含每个 x_values 中的值与其小于等于该值的元素的比例
288
+
289
+ :param x_values: 一个数值列表,用来计算每个数值小于等于它的元素的比例
290
+ :param fmt: 直接将值格式化好
291
+ :return: 一个字典,键为输入的数值,值为对应的比例(百分比)
292
+ """
293
+ ratio_dict = {}
294
+ for x in x_values:
295
+ position = bisect_right(self.values, x)
296
+ if self.n > 0:
297
+ ratio = (position / self.n)
298
+ else:
299
+ ratio = 0
300
+ ratio_dict[x] = ratio
301
+
302
+ def unit_func(x):
303
+ if unit:
304
+ return human_readable_number(x, unit, 4)
305
+ return x
306
+
307
+ if fmt:
308
+ ratio_dict = {unit_func(x): f'{ratio:.2%}' for x, ratio in ratio_dict.items()}
309
+
310
+ return ratio_dict
311
+
312
+ def group_count(self, max_entries=None, min_count=None):
313
+ """ 统计每种取值出现的次数,并根据条件过滤结果
314
+
315
+ :param max_entries: 最多显示的条目数
316
+ :param min_count: 显示的条目至少出现的次数
317
+ """
318
+ from collections import Counter
319
+
320
+ # 使用Counter来计数每个值出现的次数
321
+ counts = Counter(self.values or self.raw_values)
322
+
323
+ # 根据min_count过滤计数结果
324
+ if min_count is not None:
325
+ counts = {k: v for k, v in counts.items() if v >= min_count}
326
+
327
+ # 根据max_entries限制结果数量
328
+ if max_entries is not None:
329
+ # 按出现次数降序排列,然后选取前max_entries项
330
+ most_common = counts.most_common(max_entries)
331
+ # 转换回字典形式
332
+ counts = dict(most_common)
333
+ else:
334
+ # 如果没有指定max_entries,则保持所有满足min_count的结果
335
+ counts = dict(sorted(counts.items(), key=lambda item: item[1], reverse=True))
336
+
337
+ return counts
338
+
339
+
340
+ class Groups:
341
+ def __init__(self, data):
342
+ """ 分组
343
+
344
+ :param data: 输入字典结构直接赋值
345
+ 或者其他结构,会自动按相同项聚合
346
+
347
+ TODO 显示一些数值统计信息,甚至图表
348
+ TODO 转文本表达,方便bc比较
349
+ """
350
+ if not isinstance(data, dict):
351
+ new_data = dict()
352
+ # 否要要转字典类型,自动从1~n编组
353
+ for k, v in enumerate(data, start=1):
354
+ new_data[k] = v
355
+ data = new_data
356
+ self.data = data # 字典存原数据
357
+ self.ctr = Counter({k: len(x) for k, x in self.data.items()}) # 计数
358
+ self.stat = ValuesStat(self.ctr.values()) # 综合统计数据
359
+
360
+ def __repr__(self):
361
+ ls = []
362
+ for i, (k, v) in enumerate(self.data.items(), start=1):
363
+ ls.append(f'{i}, {k}:{v}')
364
+ return '\n'.join(ls)
365
+
366
+ @classmethod
367
+ def groupby(cls, ls, key, ykey=None):
368
+ """
369
+ :param ls: 可迭代等数组类型
370
+ :param key: 映射规则,ls中每个元素都会被归到映射的key组上
371
+ Callable[Any, 不可变类型]
372
+ None,未输入时,默认输入的ls已经是分好组的数据
373
+ :param ykey: 是否对分组后存储的内容y,也做一个函数映射
374
+ :return: dict
375
+ """
376
+ data = defaultdict(list)
377
+ for x in ls:
378
+ k = key(x)
379
+ if ykey:
380
+ x = ykey(x)
381
+ data[k].append(x)
382
+ return cls(data)
383
+
384
+
385
+ def intersection_split(a, b):
386
+ """ 输入两个对象a,b,可以是dict或set类型,list等
387
+
388
+ 会分析出二者共有的元素值关系
389
+ 返回值是 ls1, ls2, ls3, ls4,大部分是list类型,但也有可能遵循原始情况是set类型
390
+ ls1:a中,与b共有key的元素值
391
+ ls2:a中,独有key的元素值
392
+ ls3:b中,与a共有key的元素值
393
+ ls4:b中,独有key的元素值
394
+ """
395
+ # 1 获得集合的key关系
396
+ keys1 = set(a)
397
+ keys2 = set(b)
398
+ keys0 = keys1 & keys2 # 两个集合共有的元素
399
+
400
+ # TODO 如果是字典,希望能保序
401
+
402
+ # 2 组合出ls1、ls2、ls3、ls4
403
+
404
+ def split(t, s, ks):
405
+ """原始元素为t,集合化的值为s,共有key是ks"""
406
+ if isinstance(t, (set, list, tuple)):
407
+ return ks, s - ks
408
+ elif isinstance(t, dict):
409
+ ls1 = sorted(map(lambda x: (x, t[x]), ks), key=lambda x: natural_sort_key(x[0]))
410
+ ls2 = sorted(map(lambda x: (x, t[x]), s - ks), key=lambda x: natural_sort_key(x[0]))
411
+ return ls1, ls2
412
+ else:
413
+ # dprint(type(s)) # s不是可以用来进行集合规律分析的类型
414
+ raise ValueError(f'{type(s)}不是可以用来进行集合规律分析的类型')
415
+
416
+ ls1, ls2 = split(a, keys1, keys0)
417
+ ls3, ls4 = split(b, keys2, keys0)
418
+ return ls1, ls2, ls3, ls4
419
+
420
+
421
+ def matchpairs(xs, ys, cmp_func, least_score=sys.float_info.epsilon, *,
422
+ key=None, index=False):
423
+ r""" 匹配两组数据
424
+
425
+ :param xs: 第一组数据
426
+ :param ys: 第二组数据
427
+ :param cmp_func: 所用的比较函数,值越大表示两个对象相似度越高
428
+ :param least_score: 允许匹配的最低分,默认必须要大于0
429
+ :param key: 是否需要对xs, ys进行映射后再传入 cmp_func 操作
430
+ :param index: 返回的不是原值,而是下标
431
+ :return: 返回结构[(x1, y1, score1), (x2, y2, score2), ...],注意长度肯定不会超过min(len(xs), len(ys))
432
+
433
+ 注意:这里的功能①不支持重复匹配,②任何一个x,y都有可能没有匹配到
434
+ 如果每个x必须都要有一个匹配,或者支持重复配对,请到隔壁使用 MatchPairs
435
+
436
+ TODO 这里很多中间步骤结果都是很有分析价值的,能改成类,然后支持分析中间结果?
437
+ TODO 这样全量两两比较是很耗性能的,可以加个参数草算,不用精确计算的功能?
438
+
439
+ >>> xs, ys = [4, 6, 1, 2, 9, 4, 5], [1, 5, 8, 9, 2]
440
+ >>> cmp_func = lambda x,y: 1-abs(x-y)/max(x,y)
441
+ >>> matchpairs(xs, ys, cmp_func)
442
+ [(1, 1, 1.0), (2, 2, 1.0), (9, 9, 1.0), (5, 5, 1.0), (6, 8, 0.75)]
443
+ >>> matchpairs(ys, xs, cmp_func)
444
+ [(1, 1, 1.0), (5, 5, 1.0), (9, 9, 1.0), (2, 2, 1.0), (8, 6, 0.75)]
445
+ >>> matchpairs(xs, ys, cmp_func, 0.9)
446
+ [(1, 1, 1.0), (2, 2, 1.0), (9, 9, 1.0), (5, 5, 1.0)]
447
+ >>> matchpairs(xs, ys, cmp_func, 0.9, index=True)
448
+ [(2, 0, 1.0), (3, 4, 1.0), (4, 3, 1.0), (6, 1, 1.0)]
449
+ """
450
+ # 0 实际计算使用的是 xs_, ys_
451
+ if key:
452
+ xs_ = [key(x) for x in xs]
453
+ ys_ = [key(y) for y in ys]
454
+ else:
455
+ xs_, ys_ = xs, ys
456
+
457
+ # 1 计算所有两两相似度
458
+ n, m = len(xs), len(ys)
459
+ all_pairs = []
460
+ for i in range(n):
461
+ for j in range(m):
462
+ score = cmp_func(xs_[i], ys_[j])
463
+ if score >= least_score:
464
+ all_pairs.append([i, j, score])
465
+ # 按分数权重排序,如果分数有很多相似并列,就只能按先来后到排序啦
466
+ all_pairs = sorted(all_pairs, key=lambda v: (-v[2], v[0], v[1]))
467
+
468
+ # 2 过滤出最终结果
469
+ pairs = []
470
+ x_used, y_used = set(), set()
471
+ for p in all_pairs:
472
+ i, j, score = p
473
+ if i not in x_used and j not in y_used:
474
+ if index:
475
+ pairs.append((i, j, score))
476
+ else:
477
+ pairs.append((xs[i], ys[j], score))
478
+ x_used.add(i)
479
+ y_used.add(j)
480
+
481
+ return pairs
482
+
483
+
484
+ class SearchBase:
485
+ """ 一个dfs、bfs模板类 """
486
+
487
+ def __init__(self, root):
488
+ """
489
+ Args:
490
+ root: 根节点
491
+ """
492
+ self.root = root
493
+
494
+ def get_neighbors(self, node):
495
+ """ 获得邻接节点,必须要用yield实现,方便同时支持dfs、bfs的使用
496
+
497
+ 对于树结构而言,相当于获取直接子结点
498
+
499
+ 这里默认是bs4中Tag规则;不同业务需求,可以重定义该函数
500
+ 例如对图结构、board类型,可以在self存储图访问状态,在这里实现遍历四周的功能
501
+ """
502
+ try:
503
+ for node in node.children:
504
+ yield node
505
+ except AttributeError:
506
+ pass
507
+
508
+ def dfs_nodes(self, node=None, depth=0):
509
+ """ 返回深度优先搜索得到的结点清单
510
+
511
+ :param node: 起始结点,默认是root根节点
512
+ :param depth: 当前node深度
513
+ :return: list,[(node1, depth1), (node2, depth2), ...]
514
+ """
515
+ if not node:
516
+ node = self.root
517
+
518
+ ls = [(node, depth)]
519
+ for t in self.get_neighbors(node):
520
+ ls += self.dfs_nodes(t, depth + 1)
521
+ return ls
522
+
523
+ def bfs_nodes(self, node=None, depth=0):
524
+ if not node:
525
+ node = self.root
526
+
527
+ ls = [(node, depth)]
528
+ i = 0
529
+
530
+ while i < len(ls):
531
+ x, d = ls[i]
532
+ nodes = self.get_neighbors(x)
533
+ ls += [(t, d + 1) for t in nodes]
534
+ i += 1
535
+
536
+ return ls
537
+
538
+ def fmt_node(self, node, depth, *, prefix=' ', show_node_type=False):
539
+ """ node格式化显示 """
540
+ s1 = prefix * depth
541
+ s2 = typename(node) + ',' if show_node_type else ''
542
+ s3 = textwrap.shorten(str(node), 200)
543
+ return s1 + s2 + s3
544
+
545
+ def fmt_nodes(self, *, nodes=None, select_depth=None, linenum=False,
546
+ msghead=True, show_node_type=False, prefix=' '):
547
+ """ 结点清单格式化输出
548
+
549
+ :param nodes: 默认用dfs获得结点,也可以手动指定结点
550
+ :param prefix: 缩进格式,默认用4个空格
551
+ :param select_depth: 要显示的深度
552
+ 单个数字:获得指定层
553
+ Sequences: 两个整数,取出这个闭区间内的层级内容
554
+ :param linenum:节点从1开始编号
555
+ 行号后面,默认会跟一个类似Excel列名的字母,表示层级深度
556
+ :param msghead: 第1行输出一些统计信息
557
+ :param show_node_type:
558
+
559
+ Requires
560
+ textwrap:用到shorten
561
+ align.listalign:生成列编号时对齐
562
+ """
563
+ # 1 生成结点清单
564
+ ls = nodes if nodes else self.dfs_nodes()
565
+ total_node = len(ls)
566
+ total_depth = max(map(lambda x: x[1], ls))
567
+ head = f'总节点数:1~{total_node},总深度:0~{total_depth}'
568
+
569
+ # 2 过滤与重新整理ls(select_depth)
570
+ logo = True
571
+ cnt = 0
572
+ tree_num = 0
573
+ if isinstance(select_depth, int):
574
+
575
+ for i in range(total_node):
576
+ if ls[i][1] == select_depth:
577
+ ls[i][1] = 0
578
+ cnt += 1
579
+ logo = True
580
+ elif ls[i][1] < select_depth and logo: # 遇到第1个父节点添加一个空行
581
+ ls[i] = ''
582
+ tree_num += 1
583
+ logo = False
584
+ else: # 删除该节点,不做任何显示
585
+ ls[i] = None
586
+ head += f';挑选出的节点数:{cnt},所选深度:{select_depth},树数量:{tree_num}'
587
+
588
+ elif hasattr(select_depth, '__getitem__'):
589
+ for i in range(total_node):
590
+ if select_depth[0] <= ls[i][1] <= select_depth[1]:
591
+ ls[i][1] -= select_depth[0]
592
+ cnt += 1
593
+ logo = True
594
+ elif ls[i][1] < select_depth[0] and logo: # 遇到第1个父节点添加一个空行
595
+ ls[i] = ''
596
+ tree_num += 1
597
+ logo = False
598
+ else: # 删除该节点,不做任何显示
599
+ ls[i] = None
600
+ head += f';挑选出的节点数:{cnt},所选深度:{select_depth[0]}~{select_depth[1]},树数量:{tree_num}'
601
+ """注意此时ls[i]的状态,有3种类型
602
+ (node, depth):tuple类型,第0个元素是node对象,第1个元素是该元素所处层级
603
+ None:已删除元素,但为了后续编号方便,没有真正的移出,而是用None作为标记
604
+ '':已删除元素,但这里涉及父节点的删除,建议此处留一个空行
605
+ """
606
+
607
+ # 3 格式处理
608
+ def mystr(item):
609
+ return self.fmt_node(item[0], item[1], prefix=prefix, show_node_type=show_node_type)
610
+
611
+ line_num = listalign(range(1, total_node + 1))
612
+ res = []
613
+ for i in range(total_node):
614
+ if ls[i] is not None:
615
+ if isinstance(ls[i], str): # 已经指定该行要显示什么
616
+ res.append(ls[i])
617
+ else:
618
+ if linenum: # 增加了一个能显示层级的int2excel_col_name
619
+ res.append(line_num[i] + int2myalphaenum(ls[i][1]) + ' ' + mystr(ls[i]))
620
+ else:
621
+ res.append(mystr(ls[i]))
622
+
623
+ s = '\n'.join(res)
624
+
625
+ # 是否要添加信息头
626
+ if msghead:
627
+ s = head + '\n' + s
628
+
629
+ return s