# 1 概述

## 1.1 安装

一个Python的常用工具类库。 本类库对python 3.10版本支持最好。因为依赖第三方库的问题，本工具下的部分函数对python 3.9之前和python
3.11版本有
一些兼容问题。

安装方法一：

    pip install yangke

使用该命令为最小化yangke安装，只会安装yangke库的必要依赖。因yangke库中包含多个模块，当使用具体模块时，
可能存在依赖不全的问题，此时需要根据提示，继续安装使用的模块的必要依赖。

安装方法二：

    pip install yangke[All]

使用该命令会安装yangke库中所有模块的依赖。yangke库中包含的模块可以使1.2节测试方法进行查询。

## 1.2 测试是否安装成功

    import yangke
    yangke.info()

如果安装成功，则提示如下图所示。

![图片无法显示](./document/figures/img_20210100.png)

# 2 小功能

## 2.1 日志输出

在test.py中输入以下代码：

    from yangke.common.config import logger

    logger.debug("debug from yangke logger")
    logger.info("info from yangke logger")
    logger.warning("warning from yangke logger")
    logger.error("error from yangke logger")
    logger.critical("critical from yangke logger")

运行结果如下图所示，且默认会输出日志到运行目录下的日志文件中，日志文件名为【runtime-运行时标.log】。

![图片无法显示](./document/figures/img_20210101.png)

### 高级日志配置

使用settings.yaml文件配置日志输出的格式。 在test.py同目录下创建settings.yaml文件，写入以下内容：

    logger:
      logFile: None # 关闭输出到日志文件，如果需要修改文件名，也可直接在此处指定文件名
      dateFormat: 'YYYY/MM/DD HH:mm:ss' # 日志中的时间格式
      format: '{time} - {level} - {module}:{function}:{line} - {message}'  # 日志格式
      level: 10  # 可取0，10，20，30，40，50，分别代表notset, debug, info, warn, error, fatal
      levelColor:
        DEBUG: yellow  # 小写表示前景色
        INFO: "GREEN"  # 大写表示后景色

则运行中日志按该定义输出，如下图所示：

![图片无法显示](./document/figures/img_20210104.png)

日志的格式定义参数含义可以参见loguru的官方说明文档，本类库提供了yaml配置日志的途径，方便用户自定义。

可以自定义的内容有：

* 不同的level级别使用不同的样式；
* 不同的field（即time, level, module）使用不同的样式，loguru的默认样式就是这种形式。
* 日志文字样式
    * 前景色，小写的颜色或"fg <rgb>"定义的颜色
    * 背景色，大写的颜色或"bg <rgb>"定义的颜色
    * 字体样式，下划线、斜体、加粗等，定义参见loguru说明。
* 日期格式，如 'YYYY/MM/DD HH:mm:ss'
* 日志格式，如 '{time} - {level} - {module}:{function}:{line} - {message}'
* field的对齐方式

## 2.2 给python方法添加日志环境提示

在程序运行进入某些关键函数或方法时，会生成进入该函数或方法的日志域。使用示例如下。

在test.py中输入以下代码：

    from yangke.common.config import logger, loggingTitleCall


    @loggingTitleCall(title="初始化mysql数据库连接")
    def init_mysql():
        logger.info("测试mysql是否可用")
        logger.info("连接mysql")
        logger.info("mysql连接成功")


    init_mysql()

运行结果如下图所示：

![图片无法显示](./document/figures/img_20200102.png)

也可以临时更改某个方法中的logger级别，定制不同的logger输出格式，详细用法参见项目源码。

## 2.3 windows系统运行命令

运行windows系统的命令，有两个方法。

    from yangke.core import *

    runAsAdmin('echo "1111" > ssssss.txt', popup=True)
    result = runCMD('echo "11111"', charset="GBK", wait_for_result=True, output_type="RETURN")
    print(result)

运行结果如下图所示：

![图片无法显示](./document/figures/img_20210103.png)
说明：

    runAsAdmin(cmd, cwd=None, charset="gbk", python=None, popup=True) 

该方法以管理员方式运行命令，在windows系统上会弹出确认窗口，询问是否以管理员方式运行，如果不需要
弹出确认窗口，可以设置参数popup=False，但这实际上利用了windows的漏洞，在win10上会被defender当做病毒拦截，在 win7上能正常运行。

    runCMD(command: str, charset: str = "utf8", wait_for_result: bool = True, cwd=None,
           output_type: str = "RETURN", timeout=None)

该方法可以返回第三方命令的执行结果给当前python主进程，如2.3节示例的运行结果，这在很多时候是很有用的。

## 2.4 判断是否安装某python库

    from yangke.core import existModule

    existModule("pandas")

## 2.5 读取csv或txt至pandas.DataFrame中

    from yangke.common.fileOperate import read_csv_ex

    read_csv_ex(file)

该方法可以处理双引号括起来的跨行的元素，解决各类编码问题，解决各行元素数量不同导致的读取报错问题。该方法可解决以下错误：

> UnicodeDecodeError 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

> pandas.errors.ParserError: Error tokenizing data. C error: Expected 21 fields in line 45, saw 41

该方法几乎可以读入任何形式的csv文件和txt文件内容，解决了数据读入的问题，后续的数据清洗就可以进行下去了。

该方法的其他参数有： read_csv_ex(file, sep=None, header="infer", skiprows=None, error_bad_lines=True, nrows=None,
index_col=None,
low_memory=None, na_values=" ")

## 2.6 yangke.base中的小工具

    from yangke.base import *

然后可以使用以下方法

* 读取yaml文件，返回对应的字典对象或列表对象

> read_from_yaml(file: str, encoding="utf8")

* 获取当前电脑的IP地址

> get_localhost_ip()

* 获取文本文件的编码

> get_encoding_of_file(file)

* 将图片转换为base64格式

输入图片可以是 url, ndarray或本地的图片文件路径
> pic2base64(pic)

* 将图片转换为ndarray格式

输入图片可以是 url, ndarray或本地的图片文件路径
> pic2ndarray(pic)

* 将xls格式的excel文件另存为xlsx或csv

该方法利用的是本地安装的Office或WPS，因此只要本地的Office或WPS能正常打开的xls文件都可以成功另存，具有极强的适应性。其他excel类库在某些情况下无法另存或存在编码错误的问题。
> save_as_xlsx(file, engine="WPS", ext="xlsx", visible=False)

* 开启新线程运行指定的目标函数

> start_threads(targets, args_list=())

* 遍历目录下的文件

> yield_all_file(folder, filter_=None, ignore_temp_file: bool = True)

* 装饰器方法
  > > @run_once 确保修饰的方法只被调用一次，运行中会忽略第一次以后的调用

  > > @auto_save_para

## 2.7 Qt多版本支持

通过以下导入语句，可以兼容PySide6、PyQt6和PyQt5这三个Python库的语法，开发的图形界面软件可以很方便的同时兼容Qt5和Qt6。

> from yangke.common.QtImporter import QtGui, Qt, QMessageBox, QLabel, QColor

任何需要从Qt中导入的组件，都可以从yangke.common.QtImporter中导入，无需关心当前运行环境安装的是PyQt5还是PySide6。
QtImporter模块会自动判断已经安装的Qt库，并使用正确的导入。且用户使用PyQt5和PySide6的语法均可正常运行。
如果是同名的Qt组件，则可以导入具体组件的模块，然后以[模块.组件名]的方式使用到特定的Qt组件。
例如：

> from yangke.common.QtImporter import QAction \
> from yangke.common.QtImporter import Qt3DInput

此处，QAction和Qt3DInput.QAction都可以使用，并且是两个不同的类。默认的QAction是QtGui.QAction（PyQt6）或QtWidgets.QAction（PyQt5）
。

# 3 神经网络

## 3.1 残差神经网络（ResNet）

残差神经网络是由微软研究院的和开明、张祥雨、任少卿、孙剑等人提出的。ResNet在2015年的ILSVRC（ImageNet Large Scale Visual
Recognition Challenge)中夺得了冠军。

残差神经网络的主要贡献是发现了“退化现象”，并针对退化现象发明了“快捷链接（Shortcut
connection）”，解决了深度过大的神经网络训练困难的问题。使得神经网络的深度可以大大增加。

残差神经网络适用于需要较多层数的神经网络架构中，如果神经网络层数不多，则不需要使用该网络结构。

### 3.1.1 网络深度问题

从理论上讲，越深的神经网络准确率越高，因为层数较多的神经网络，可以由较浅的神经网络和恒等变换网络拼接而成，如下图所示。
![图片无法显示](./document/figures/resnet_1.jpg)

### 3.1.2 退化现象与对策

通过实验，随着神经网络层数的不断增加，模型的准确率显示不断的提高，达到最大值，然后随着网络层数的继续增加，模型准确率毫无征兆的大幅度降低。ResNet团队吧这一现象成为“退化（Degradation）”。

退化现象的本质是随着网络层数的增加，神经网络训练过程难以获得有效的梯度，因此大量的神经网络权值更新都出现随机变化现象，即梯度消失的现象越来越明显。为了解决该问题，ResNet团队在ResNet模块中增加了快捷连接分支，使得梯度可以很好的传递到更深的神经网络层中。

## 3.2 激活函数/激励函数(Activation Function)

### 3.2.1 relu函数

卷积神经网络中常用

### 3.2.2 sigmoid函数

### 3.2.3 tanh函数

### 3.2.4 softplus函数

![图片无法显示](./document/figures/激活函数.png)

## 3.3 分类函数

### 3.3.1 sigmoid

二分类问题时，神经网络隐藏层的最后会接一个sigmoid函数，用于将神经网络输出结果变换为两个概率值。当多分类问题时，需要使用softmax函数。

### 3.3.2 softmax

首先，我们知道概率有两个性质：1）预测的概率为非负数；2）各种预测结果概率之和等于1。

softmax就是将在负无穷到正无穷上的预测结果按照这两步转换为概率的。

## 3.4 损失函数

### 3.4.1 cross entropy

首先是信息量。假设我们听到了两件事，分别如下：

事件A：巴西队进入了2018世界杯决赛圈。

事件B：中国队进入了2018世界杯决赛圈。

仅凭直觉来说，显而易见事件B的信息量比事件A的信息量要大。究其原因，是因为事件A发生的概率很大，事件B发生的概率很小。所以当越不可能的事件发生了，我们获取到的信息量就越大。越可能发生的事件发生了，我们获取到的信息量就越小。那么信息量应该和事件发生的概率有关。

信息熵=求和(事件概率×事件信息量)

具体理论参见：https://www.jianshu.com/p/47172eb86b39

总之，该损失函数适用于“类别排斥多分类问题”。

### 3.4.2 mean average error(MSE)

适用于回归任务

## 3.5 神经网络类库

### 3.5.1 pytorch

* Variable对象的类型是<class 'torch.Tensor'>，这和torch.FloatTensor类的对象是同样的类型。

  虽然类型相同，但是Variable计算时，他在后台一步步默默地搭建着一个庞大的系统，叫做计算图（computational
  graph）。这个图将所有的计算步骤（节点）都连接起来，最后进行误差反向传递的时候，一次性将所有variable里面的修改幅度（梯度）都计算出来，而tensor没有这个能力。
* numpy和pytorch数据类型转换

> torch_data = torch.from_numpy(np_data)
>
> np_data = torch_data.numpy()
>

# 4 双碳目标

## 4.1 碳汇

碳汇（carbon sink），是指通过植树造林、植被恢复等措施，吸收大气中的二氧化碳，从而减少温室气体在大气中浓度的过程、活动或机制。

可分为：

* 森林碳汇
* 草地碳汇
* 耕地碳汇
* 土壤碳汇
* 海洋碳汇

## 4.2 温室气体

《京都议定书》规定的六中温室气体包括：二氧化碳（CO<sub>2</sub>）、甲烷（CH<sub>4</sub>）、氧化亚氮（N<sub>2</sub>
O）、氢氟碳化物（HFCs）、全氟化碳（PFCs）和六氟化硫（SF<sub>6</sub>）。

《GB/T 32151.1-2005 温室气体排放核算与报告要求 第1部分：发电企业》中核算的温室气体只包括二氧化碳。

## 4.3 发电企业碳排放核算方法

发电企业的全部排放包括化石燃料燃烧产生的二氧化碳排放、脱硫过程的二氧化碳排放、企业购入电力产生的二氧化碳排放。对于生物质混合燃料发电企业，其燃料燃烧产生的二氧化碳排放仅统计混合燃料中化石燃料的二氧化碳排放；对于垃圾焚烧发电企业，其燃料燃烧的二氧化碳排放仅统计化石燃料的二氧化碳排放。

# 5 快速创建桌面应用程序

如下代码将使用YkWindow快速创建桌面应用程序：

    from yangke.common.qt import YkWindow, run_app

    class MainWindow(YkWindow):
        def __init__(self):
            super(MainWindow, self).__init__()    
    
    run_app(MainWindow)

运行后，应用程序初始化界面如下：
![图片无法显示](./document/figures/桌面应用1.png)

![img_1.png](document/figures/img_20220709.png)

![img.png](document/figures/img_202207091746.png)

![img_2.png](document/figures/img_202207091744.png)

点击设置菜单项下的【开启表格】，则可在主面板中打开一个表格内容区域。
![img_3.png](document/figures/img_202207091745.png)

点击设置菜单项下的【开启输入面板】，则进一步打开一个输入面板。如下图所示：

![img_1.png](document/figures/img_1.png)

软件界面中的菜单由ui_menu.yaml文件定义，默认加载程序运行目录下的ui_menu.yaml或ui/ui_menu.yaml。示例如下：

~~~
menu:
  - # 第一个菜单ribbon
    name: "文件"
    items:
      - action_name: "窗口居中"
        short_cut: "Alt+C"
        connect: "self.center"  # 菜单项的点击时间，用户可以自定义
      - action_name: "退出"
        connect: "self.closeEvent"
      - name: "设置"
        items:
          - action_name: "开启表格"
            connect: "self.enable_table"
          - action_name: "开启输入面板"
            connect: "self.enable_input_panel"
          - action_name: "字体设置"
            connect: "self.set_font"
          - action_name: "数据小数点位数"
            connect: "self.set_digits"
      - name: "帮助"
        items:
          - action_name: "关于"
            connect: "self.about"
          - action_name: "帮助"
            connect: "self.help"
~~~

软件界面中的表格格式也可以由ui_table.yaml文件定义，默认加载程序运行目录下的ui_table.yaml或ui/ui_table.yaml。示例如下：

~~~
width: "[200,80,100]"  # 每一列的宽度，如果多个区域设置同一列，则以最后一个为准

data:
  - title: "示例计算"
    range: "(0,0,3)"
    background: "#dfc2ea"
    items_text_align: center
    items_unit_align: center
    items:
      - label: "苹果单价"
        value: "4.5"
        unit: "元/kg"
      - label: "苹果数量"
        value: "1.5"
        unit: "kg"
      - label: "香蕉单价"
        value: "3.5"
        unit: "元/kg"
      - label: "香蕉数量"
        value: "2"
        unit: "kg"
  - title: "计算结果"
    range: "(0, 7, 3)"
    background: "lightgreen"
    foreground: "blue"
    items:
      - label: "苹果总价"
        value: ""
        unit: "元"
      - label: "香蕉总价"
        value: ""
        unit: "元"
      - label: "-------------------------------------------------------------------"
        merge_label_row_col: "(1,3)"
      - label: "总价"
        value: ""
        unit: "元"

button:
  - name: "计算"
    range: "(2,5)"  # 按钮位于表格的第三列四行
    connect: "root._test_cal_" # 调用应用程序类的about方法
~~~

软件界面中的输入面板格式也可以由ui_panel.yaml文件定义，默认加载程序运行目录下的ui_panel.yaml或ui/ui_panel.yaml。示例如下：

~~~
# ===========================定义一些yaml变量，用于公共使用的部分，一般为单位列表或者尺寸描述=============================
unit:
  massflowrate: &unit_massflowrate
    - t/h
    - kg/s
  power: &unit_power
    - W
    - kW
    - MW

size:
  width:
    - 120  # <label>的宽度
    - 140  # <textField>的宽度
    - 50  # <unit>的宽度
# ===========================定义一些yaml变量，用于公共使用的部分，一般为单位列表或者尺寸描述=============================

# ===========================定义界面信息=============================
calculate: # 计算对应的输入面板配置
  inputArea:  # 输入面板中的输入区域，是一个列表，每一个列表对应一项输入
    - # 第一个设置项
      label: "#1 中压供汽量"
      value: 240
      unit: *unit_massflowrate
      unit_selected: "t/h"
    - # 第二个设置项
      label: "低压供汽需求"
      value: 200
      unit: *unit_massflowrate
      unit_selected: "t/h"
    - # 第三个设置项
      label: "#2 功率"
      value: 350
      value_validator: "int, 210, 350"  # value只能是210~350之间的整数
      unit: *unit_power
      unit_selected: "MW"
  buttons:
    - # 第一个按钮
      text: "应用"
      on_click: "root.btn_clicked"  # 按钮点击触发的事件
    - # 第二个按钮
      text: "寻优"
      on_click: "root.btn_clicked"  # 按钮点击触发的事件
~~~

用户可以在MainFrame类下自定义类方法，并将类方法绑定到软件中的按钮上。菜单中的connect取值为self.<自定义方法>
即可完成绑定，面板和表格中的按钮connect取值应为root.<自定义方法>。

例如上述示例中菜单栏【帮助】下的【关于】菜单项绑定了self.about事件，则可以如下定义_test_cal_方法：

~~~
def _test_cal_(self):
    a = self.table_widget.get_value("示例计算.苹果单价")
    b = self.table_widget.get_value("示例计算.香蕉单价")
    x = self.table_widget.get_value("示例计算.苹果数量")
    y = self.table_widget.get_value("示例计算.香蕉数量")
    a_t = a * x
    b_t = b * y
    result = a_t + b_t
    self.table_widget.set_value("计算结果.苹果总价", a_t)
    self.table_widget.set_value("计算结果.香蕉总价", b_t)
    self.table_widget.set_value("计算结果.总价", result)
~~~

单击计算按钮后，则在计算结果框中显示出当前的计算结果。如下图所示：
![img_2.png](document/figures/img_2.png)

sqlalchemy中的类型

![sqlalchemy类型.png](document/figures/sqlalchemy类型.png)

常用的SQLAlchemy列选项
![img.png](document/img.png)

常用的SQLAlchemy关系选项
![img_1.png](document/img_1.png)

pandas使用to_sql()语句存储时，使用dtype参数指定dataframe到数据库的对应类型，需要特别注意日期类型的处理，否则可能出现各种
类型转换问题。只需要使用dtype参数将二者类型对应起来，则不存在任何查询格式匹配问题。

~~~
holiday_data[["calendarDate", "isOpen"]].to_sql('Holiday', con=self.engine, if_exists='replace',
                                                index=False,
                                                dtype={"calendarDate": DATE, "isOpen": BOOLEAN})
~~~

# 6 股票
股票软件的入口文件为yangke.stock.main，运行后即可显示量化软件主界面。如下图所示。
![img_3.png](document/img_3.png)
该软件默认使用sqlite数据库存储股票数据。股票数据库包含四个数据库表。分别为：
表1，股票信息表（AllStocksBasicInfo），记录了目前上证、深证的所有股票基本信息。
表2~N，daily{股票代码}记录了每只股票的开盘价、收盘价等日线信息。
表3，节假日表（Holiday），用于判断A股股市是否开盘。
表4，修改日期表，记录每只股票的最后更新时间。

![img_2.png](document/img_2.png)

# 7 自动化操作
步骤定义：

![步骤.png](./document/figures/步骤.png)

op target = 步骤的操作类型有：
  * click key: 单击某个键盘键位；
  * press key: 按下某个键盘键位；
  * release key: 释放某个键盘键位；
  * left click: 鼠标单击；
  * right click： 鼠标右击；
  * double click: 鼠标双击；

judge = 条件等待类型：
 * 重复： 当条件不满足时，重复进行步骤的操作，直到条件满足
 * 等待： 只进行一次操作，当条件不满足时，无限期等待条件满足
 * 无： 只进行一次操作，则当前步骤即完成

condition = 条件类型有：
  * 存在文字： 画面中存在指定的文字
  * 存在图片： 画面中存在指定的图片
  * 不存在文字： 画面中指定文字消失，则条件满足
  * 不存在图片： 画面中指定图片小时，则条件满足
  * 无： 当judge为重复时，该条件永远不满足，当judge为等待时，该条件永远满足


[judge=等待 and condition=无] 的效果与[judge=无]相同
