<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>zzZ5的博客</title>
  
  <subtitle>多说无益</subtitle>
  <link href="https://zzz5.github.io/atom.xml" rel="self"/>
  
  <link href="https://zzz5.github.io/"/>
  <updated>2026-08-26T09:18:21.240Z</updated>
  <id>https://zzz5.github.io/</id>
  
  <author>
    <name>zzZ5</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>搭建CP500</title>
    <link href="https://zzz5.github.io/2025/11/14/lot/arduino-cp500/"/>
    <id>https://zzz5.github.io/2025/11/14/lot/arduino-cp500/</id>
    <published>2025-11-14T14:00:00.000Z</published>
    <updated>2026-08-26T09:18:21.240Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>本篇文章介绍如何使用3D打印件、ESP32、温度传感器和执行器搭建微型堆肥反应器 CP500。系统通过水浴维持反应温度，并控制循环泵和曝气泵。本文重点记录结构、接线和调试流程，具体控制参数需要根据实际容积、加热功率和散热条件调整。</p><h2 id="硬件清单"><a href="#硬件清单" class="headerlink" title="硬件清单"></a>硬件清单</h2><table><thead><tr><th>部件</th><th align="right">数量</th><th>用途</th></tr></thead><tbody><tr><td>ESP32 开发板</td><td align="right">1</td><td>采集温度并执行控制逻辑</td></tr><tr><td>DS18B20 防水温度探头</td><td align="right">5</td><td>水浴 3 个、物料 1 个、水箱 1 个</td></tr><tr><td>两路继电器模块</td><td align="right">1</td><td>控制加热棒和循环水泵</td></tr><tr><td>MOSFET 驱动模块</td><td align="right">1</td><td>控制低压曝气泵</td></tr><tr><td>加热棒、循环水泵、曝气泵</td><td align="right">各 1</td><td>加热、循环和供氧</td></tr><tr><td>12V&#x2F;5V&#x2F;3V 电源模块</td><td align="right">按负载选择</td><td>为执行器和控制板供电</td></tr></tbody></table><p>继电器、MOSFET 和电源的额定电压、电流必须覆盖实际负载，并保留余量。加热、水泵等大功率负载不能由 ESP32 引脚直接供电。</p><h2 id="准备3D模型"><a href="#准备3D模型" class="headerlink" title="准备3D模型"></a>准备3D模型</h2><p>3D模型包括：<br>水浴结构、物料内筒、筛网、物料盖、水箱、水箱盖，底部主板、底部支撑柱4个、主板盖<br>模型地址通过网盘分享的文件：3D打印模型<br>链接: <a href="https://pan.baidu.com/s/121zwOXlOseUdFzWseTfPzA?pwd=8888">提取码: 8888</a>  </p><h3 id="3D打印"><a href="#3D打印" class="headerlink" title="3D打印"></a>3D打印</h3><p>采用拓竹3D打印机打印3D部件，其中水箱和水箱盖采用ASA材料打印(也可以采用耐热性能更高的其他耗材)，其他部件采用ABS打印</p><p>具体打印方法如下：<br>下载拓竹Bambu Studio软件，连接打印机，打开下载好的3D模型，设置均已提前设置好，直接切盘打印即可</p><p>打印前建议根据设备用途再检查以下项目：</p><ul><li>水箱和水浴部件的壁厚、层间结合与耐温性能；</li><li>过线孔、传感器孔和水管接口是否需要预留密封余量；</li><li>与水接触的打印件先单独做 24 小时静置漏水测试；</li><li>打印完成后清理支撑和毛刺，避免水管被锐边割伤。</li></ul><h2 id="装配顺序"><a href="#装配顺序" class="headerlink" title="装配顺序"></a>装配顺序</h2><ol><li>先安装底部主板、支撑柱和电源模块，低压控制线与负载电源线分开走线；</li><li>安装水箱、循环水泵和水管，在不通电的情况下完成漏水测试；</li><li>放入水浴筒和物料内筒，确保内筒可以正常取出；</li><li>固定 DS18B20 探头，避免探头紧贴加热棒或容器壁造成局部温度偏差；</li><li>最后连接继电器、MOSFET 和 ESP32，并为接头做好绝缘与应力释放。</li></ol><h2 id="接线"><a href="#接线" class="headerlink" title="接线"></a>接线</h2><p>所有3D模型打印完毕后，需要结合准备的设备（esp32单片机、电源板、水泵、曝气泵、加热棒、温度传感器、2路继电器、场效应管）进行接线，接线方法如下：<br>Esp32接2路继电器（分别控制加热和水箱）、接MOS（场效应管，控制曝气）<br>Esp32接温度传感器Ds18b20（分别为3个监测水浴温度，1个物料温度、1个水箱温度，物料和水箱温度共线）<br>电源板接esp32（5V）接加热棒、水泵（12V），接曝气泵（3V）</p><h3 id="详细连线"><a href="#详细连线" class="headerlink" title="详细连线"></a>详细连线</h3><p>esp32 pin4接物料和水箱温度传感器信号线，pin5接接水浴温度传感器信号线，温度传感器采用esp32 3V3和GND供电<br>pin25接继电器加热棒控制信号线，pin26接继电器水泵控制信号线，pin27接气泵MOS控制信号线，2路继电器和MOS采用esp32 5V和GND供电</p><p>整理后的引脚表如下：</p><table><thead><tr><th align="right">GPIO</th><th>连接对象</th><th>功能</th></tr></thead><tbody><tr><td align="right">4</td><td>物料、水箱 DS18B20 数据线</td><td>读取物料和水箱温度</td></tr><tr><td align="right">5</td><td>3 个水浴 DS18B20 数据线</td><td>读取水浴温度</td></tr><tr><td align="right">25</td><td>继电器通道 1</td><td>控制加热棒</td></tr><tr><td align="right">26</td><td>继电器通道 2</td><td>控制循环水泵</td></tr><tr><td align="right">27</td><td>MOSFET 模块</td><td>控制曝气泵</td></tr></tbody></table><p>DS18B20 数据线通常需要接上拉电阻，常用值为 <code>4.7 kΩ</code>，连接在数据线和 3V3 之间。多个探头共用一条 OneWire 总线时，应尽量缩短总线长度并保证接头可靠。</p><blockquote><p>上电前务必确认继电器模块是高电平触发还是低电平触发。程序中的开关逻辑与模块触发方式相反时，设备可能在 ESP32 启动阶段意外开启。</p></blockquote><h2 id="程序"><a href="#程序" class="headerlink" title="程序"></a>程序</h2><p>代码地址：<a href="https://github.com/zzZ5/arduino-esp32-example/tree/main/esp32-cp500">esp32-cp500</a>。程序通过 <code>initSensors(4, 5, 25, 26, 27)</code> 初始化上述引脚，主要完成以下工作：</p><ul><li>读取物料、水浴和水箱温度，并过滤明显异常值；</li><li>根据温差或设定值控制加热棒和循环水泵；</li><li>保证加热和循环泵按照控制逻辑互斥，避免错误组合；</li><li>定时或远程控制曝气；</li><li>通过 MQTT 上报测量结果和设备状态。</li></ul><p>首次烧录前需要根据仓库说明填写 Wi-Fi、MQTT、温度上限、测量周期和曝气周期等配置。建议先断开加热棒与水泵，只观察串口输出和 GPIO 状态，确认逻辑无误后再逐个接入负载。</p><h2 id="上电检查"><a href="#上电检查" class="headerlink" title="上电检查"></a>上电检查</h2><ol><li><strong>只给 ESP32 供电</strong>：检查所有 DS18B20 是否都能返回合理温度；</li><li><strong>测试执行器空载输出</strong>：观察继电器和 MOSFET 指示灯，确认 GPIO 与实际通道对应；</li><li><strong>单独测试水泵</strong>：水路中加水并排尽空气，确认方向和流量正常；</li><li><strong>低温测试加热</strong>：设置较低目标温度，验证加热能在达到阈值后停止；</li><li><strong>测试异常保护</strong>：拔掉一个温度探头或模拟超温，确认程序不会继续加热；</li><li><strong>连续运行测试</strong>：正式加入物料前至少运行一个完整控制周期，检查漏水、接头温升和网络重连。</li></ol><h2 id="安全说明"><a href="#安全说明" class="headerlink" title="安全说明"></a>安全说明</h2><ul><li>水、电和加热部件必须可靠隔离，所有接头做好防水和绝缘；</li><li>加热棒必须在允许的液位和散热条件下工作，避免干烧；</li><li>ESP32 与继电器只负责控制，负载电流应走独立电源回路；</li><li>感性负载应使用合适的驱动和续流保护；</li><li>软件温控不能代替独立的温度保险、过流保护和漏电保护。</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://github.com/zzZ5/arduino-esp32-example/tree/main/esp32-cp500">项目完整代码：esp32-cp500</a></li><li><a href="https://docs.espressif.com/projects/arduino-esp32/en/latest/">Arduino ESP32 官方文档</a></li></ul>]]></content>
    
    
    <summary type="html">简单介绍如何使用3D打印+一些列配件+esp32+arduino搭建微型堆肥反应器CP500.</summary>
    
    
    
    <category term="物联网" scheme="https://zzz5.github.io/categories/%E7%89%A9%E8%81%94%E7%BD%91/"/>
    
    
    <category term="lot" scheme="https://zzz5.github.io/tags/lot/"/>
    
    <category term="arduino" scheme="https://zzz5.github.io/tags/arduino/"/>
    
    <category term="esp32" scheme="https://zzz5.github.io/tags/esp32/"/>
    
  </entry>
  
  <entry>
    <title>esp32使用arduino的at指令和移远ec800k进行mqtt通信</title>
    <link href="https://zzz5.github.io/2025/03/18/lot/adruino-ec800k/"/>
    <id>https://zzz5.github.io/2025/03/18/lot/adruino-ec800k/</id>
    <published>2025-03-18T09:37:00.000Z</published>
    <updated>2026-08-26T09:18:32.378Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="硬件准备"><a href="#硬件准备" class="headerlink" title="硬件准备"></a>硬件准备</h2><p>硬件列表：</p><ul><li>ESP32 开发板；</li><li>移远 EC800K 通信模块或带稳压、SIM 卡座和天线座的开发板；</li><li>可提供足够瞬时电流的独立电源；</li><li>4G 天线、可用的 SIM 卡和串口连接线；</li><li>如果两块板的 UART 电平不一致，还需要双向电平转换模块。</li></ul><blockquote><p>EC800K 核心模块与不同厂商开发板的供电电压可能不同，接线前必须查看手中开发板的原理图。蜂窝模块发射时会产生较大的瞬时电流，不建议直接从 ESP32 的 3V3 引脚取电，否则很容易出现反复重启、掉网或 MQTT 莫名断开的情况。</p></blockquote><p>本文配套代码使用 ESP32 的 UART1，默认引脚如下：</p><table><thead><tr><th>ESP32</th><th>EC800K</th><th>说明</th></tr></thead><tbody><tr><td>GPIO17（TX）</td><td>RX</td><td>ESP32 发送、EC800K 接收</td></tr><tr><td>GPIO16（RX）</td><td>TX</td><td>ESP32 接收、EC800K 发送</td></tr><tr><td>GND</td><td>GND</td><td>两块板必须共地</td></tr></tbody></table><p>串口参数为 <code>115200, 8N1</code>。如果你的 EC800K 固件使用其他波特率，需要同步修改代码中的 <code>ec800kSerial.begin()</code>。</p><h2 id="PlatformIO-项目"><a href="#PlatformIO-项目" class="headerlink" title="PlatformIO 项目"></a>PlatformIO 项目</h2><p>完整代码位于 <a href="https://github.com/zzZ5/arduino-esp32-example/tree/main/esp32-ec800k-mqtt">esp32-ec800k-mqtt</a>。项目的 <code>platformio.ini</code> 可以保持为：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[env:esp32dev]</span></span><br><span class="line"><span class="attr">platform</span> = espressif32</span><br><span class="line"><span class="attr">board</span> = esp32dev</span><br><span class="line"><span class="attr">framework</span> = ardui<span class="literal">no</span></span><br><span class="line"><span class="attr">monitor_speed</span> = <span class="number">115200</span></span><br></pre></td></tr></table></figure><p>核心代码的作用是建立一个串口透传终端：电脑串口监视器输入 AT 指令，ESP32 转发给 EC800K；模块返回的内容再打印到电脑。这样可以先把网络注册和 MQTT 指令逐条调通，再考虑封装成自动状态机。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Arduino.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;HardwareSerial.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">HardwareSerial <span class="title">ec800kSerial</span><span class="params">(<span class="number">1</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setup</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  Serial.<span class="built_in">begin</span>(<span class="number">115200</span>);</span><br><span class="line">  ec800kSerial.<span class="built_in">begin</span>(<span class="number">115200</span>, SERIAL_8N1, <span class="number">16</span>, <span class="number">17</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="keyword">while</span> (Serial.<span class="built_in">available</span>()) &#123;</span><br><span class="line">    ec800kSerial.<span class="built_in">write</span>(Serial.<span class="built_in">read</span>());</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">while</span> (ec800kSerial.<span class="built_in">available</span>()) &#123;</span><br><span class="line">    Serial.<span class="built_in">write</span>(ec800kSerial.<span class="built_in">read</span>());</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>仓库中的版本使用 FreeRTOS 任务异步读取串口，并增加了缓冲区和输出过滤，更适合继续扩展。</p><h2 id="联网前检查"><a href="#联网前检查" class="headerlink" title="联网前检查"></a>联网前检查</h2><p>插入 SIM 卡并连接天线后，先在串口监视器依次检查：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">AT</span><br><span class="line">AT+CPIN?</span><br><span class="line">AT+CSQ</span><br><span class="line">AT+CEREG?</span><br><span class="line">AT+CGATT?</span><br></pre></td></tr></table></figure><p>正常情况下，<code>AT</code> 返回 <code>OK</code>，<code>CPIN</code> 显示 SIM 卡已就绪，<code>CEREG</code> 表明已经注册到分组网络，<code>CGATT</code> 返回已附着。<code>CSQ</code> 用于查看信号质量；如果长时间无法注册，应先检查天线、SIM 卡、供电和当地运营商网络，而不是直接排查 MQTT。</p><h2 id="MQTT-AT-指令流程"><a href="#MQTT-AT-指令流程" class="headerlink" title="MQTT AT 指令流程"></a>MQTT AT 指令流程</h2><p>EC800K 不同固件的 MQTT 指令参数可能略有区别，应以模块对应版本的 AT 手册为准。常见的调试顺序如下，其中地址、端口、客户端 ID 和主题都需要替换成自己的值：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">AT+QMTOPEN=0,&quot;broker.example.com&quot;,1883</span><br><span class="line">AT+QMTCONN=0,&quot;esp32-ec800k-client&quot;</span><br><span class="line">AT+QMTSUB=0,1,&quot;device/test/down&quot;,0</span><br></pre></td></tr></table></figure><p>发布消息通常使用固件支持的 <code>QMTPUB</code> 或 <code>QMTPUBEX</code> 指令，例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">AT+QMTPUBEX=0,0,0,0,&quot;device/test/up&quot;,18</span><br><span class="line">&#123;&quot;temperature&quot;:25&#125;</span><br></pre></td></tr></table></figure><p>每一步都要等待模块的异步结果再继续，尤其是 <code>QMTOPEN</code>。仅收到命令行的 <code>OK</code> 不代表 MQTT 已经连接成功，还需要检查随后出现的 <code>+QMTOPEN</code>、<code>+QMTCONN</code>、<code>+QMTSUB</code> 或 <code>+QMTPUBEX</code> 结果码。</p><p>断开连接时可以按固件手册依次关闭 MQTT 客户端和网络连接。程序化控制时不要简单地连续发送一串 AT 指令，建议将流程拆分为“等待网络注册 → 打开连接 → 登录 → 订阅&#x2F;发布 → 断线重连”几个状态，并为每个状态设置超时。</p><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="串口完全没有响应"><a href="#串口完全没有响应" class="headerlink" title="串口完全没有响应"></a>串口完全没有响应</h3><p>先交换 RX&#x2F;TX，确认共地，再检查波特率和模块是否已经正常开机。有些开发板还需要拉低 PWRKEY 一段时间才能启动。</p><h3 id="模块不断重启或一联网就掉线"><a href="#模块不断重启或一联网就掉线" class="headerlink" title="模块不断重启或一联网就掉线"></a>模块不断重启或一联网就掉线</h3><p>优先检查供电能力和电源线压降。蜂窝模块的瞬时电流明显高于空闲状态，万用表看到的平均电压正常并不能排除电源问题。</p><h3 id="MQTT-打不开"><a href="#MQTT-打不开" class="headerlink" title="MQTT 打不开"></a>MQTT 打不开</h3><p>先确认 <code>CEREG</code> 和 <code>CGATT</code> 正常，再检查 APN、Broker 地址、端口以及服务器是否允许匿名连接。如果使用 TLS，还需要额外配置证书和 SSL 上下文。</p><h3 id="收到-OK-但没有消息"><a href="#收到-OK-但没有消息" class="headerlink" title="收到 OK 但没有消息"></a>收到 <code>OK</code> 但没有消息</h3><p><code>OK</code> 只表示 AT 命令格式被接受。继续查看 <code>+QMT...</code> 异步返回码，并确认发布和订阅使用的是同一个 Broker、主题及 QoS 设置。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://github.com/zzZ5/arduino-esp32-example/tree/main/esp32-ec800k-mqtt">项目完整代码：esp32-ec800k-mqtt</a></li><li><a href="https://www.quectel.com/download-zone/">移远通信下载中心</a></li><li><a href="https://docs.platformio.org/en/latest/boards/espressif32/esp32dev.html">PlatformIO：Espressif ESP32 Dev Module</a></li></ul>]]></content>
    
    
    <summary type="html">使用 ESP32 和 移远 EC800K 模块进行 MQTT 通信, 从而实现4G物联网通信.</summary>
    
    
    
    <category term="物联网" scheme="https://zzz5.github.io/categories/%E7%89%A9%E8%81%94%E7%BD%91/"/>
    
    
    <category term="lot" scheme="https://zzz5.github.io/tags/lot/"/>
    
    <category term="arduino" scheme="https://zzz5.github.io/tags/arduino/"/>
    
    <category term="esp32" scheme="https://zzz5.github.io/tags/esp32/"/>
    
  </entry>
  
  <entry>
    <title>在esp32上使用arduino</title>
    <link href="https://zzz5.github.io/2024/10/16/lot/arduino-esp32/"/>
    <id>https://zzz5.github.io/2024/10/16/lot/arduino-esp32/</id>
    <published>2024-10-16T14:00:00.000Z</published>
    <updated>2026-08-26T09:18:21.241Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>Arduino是一个能够用来感应和控制现实物理世界的一套工具。 它由一个基于单片机并且开放源码的硬件平台，和一套为Arduino板编写程序的开发环境组成。<br>ESP32是一种由Espressif乐鑫信息科技推出的WiFi芯片，专为移动设备、可穿戴电子产品和物联网应用而设计。它具有低功耗性能、双核32位MCU、2.4GHz双模Wi-Fi和蓝牙芯片等特点，适用于物联网和嵌入式系统开发.</p><h2 id="为什么要使用Arduino？"><a href="#为什么要使用Arduino？" class="headerlink" title="为什么要使用Arduino？"></a>为什么要使用Arduino？</h2><p>有很多的单片机和单片机平台都适合用做交互式系统的设计。例如：Parallax Basic Stamp， Netmedia’s BX-24，Phidgets，MIT’s Handyboard 和其它等等提供类似功能的。 所有这些工具，你都不需要去关心单片机编程繁琐的细节，提供给你的是一套容易使用的工具包。 Arduino同样也简化了同单片机工作的流程，但同其它系统相比Arduino在很多地方更具有优越性， 特别适合老师，学生和一些业余爱好者们使用：</p><ul><li>便宜 － 和其它平台相比，Arduino板算是相当便宜了。最便宜的Arduino版本可以自己动手制作，即使是组装好的成品，其价格也不会超过200元。</li><li>跨平台 － Arduino软件可以运行在Windows，Macintosh OSX，和Linux操作系统。大部分其它的单片机系统都只能运行在Windows上。</li><li>简易的编程环境 － 初学者很容易就能学会使用Arduino编程环境，同时它又能为高级用户提供足够多的高级应用。对于老师们来说，一般都能很方便的使用Processing 编程环境，所以如果学生学习过使用Processing 编程环境的话，那他们在使用Arduino开发环境的时候就会觉得很相似很熟悉。</li><li>软件开源并可扩展 － Arduino软件是开源的，对于有经验的程序员可以对其进行扩展。Arduino编程语言可以通过C++库进行扩展，如果有人想去了解技术上的细节，可以跳过Arduino语言而直接使用AVR C 编程语言（因为Arduino语言实际上是基于AVR C的）。类似的，如果你需要的话，你也可以直接往你的Arduino程序中添加AVR-C 代码。</li><li>硬件开源并可扩展 － Arduino板基于 Atmel 的ATMEGA8 和ATMEGA168&#x2F;328 单片机。Arduino基于Creative Commons 许可协议，所以有经验的电路设计师能够根据需求设计自己的模块，可以对其扩展或改进。甚至是对于一些相对没有什么经验的用户，也可以通过制作试验板来理解Arduino是怎么工作的，省钱又省事。</li><li>Arduino基于AVR平台，对AVR库进行了二次编译封装，把端口都打包好了，寄存器啦、地址指针之类的基本不用管。大大降低了软件开发难度，适宜非专业爱好者使用。优点和缺点并存，因为是二次编译封装，代码不如直接使用AVR代码编写精练，代码执行效率与代码体积都弱于AVR直接编译。</li></ul><h2 id="为什么要使用esp32"><a href="#为什么要使用esp32" class="headerlink" title="为什么要使用esp32"></a>为什么要使用esp32</h2><ol><li>功能强大</li></ol><p>自带wifi+蓝牙是最大亮点！一起来看看ESP32的规格，就知道它强在哪里了。</p><ul><li>内置WiFi和蓝牙，就不需要额外的以太网模块或wifi模块了，集成度高；</li><li>双核 CPU，可以主频为 80、160 或 240MHz。对于一个体积小巧的芯片中是相当多的计算了；</li><li>高级外设接口：I2C, SPI, CAN等，可以实现很多应用，不仅仅是个Wi-Fi MCU；</li><li>大量内存：ESP32包括 512KB 的片上 SRAM 存储器，还支持外部存储器，具体取决于主板，可能高达 4 到 8MB。这意味着ESP32 适用于一些较重的任务，例如连接摄像头、识别语音、从互联网流式传输数据等。</li></ul><ol start="2"><li>价格亲民</li></ol><ul><li>ESP32性价比非常高, 不仅功能强大，而且价格便宜。</li></ul><ol start="3"><li>IoT上近乎无敌</li></ol><p>stm32是通用芯片，应用更全面；而ESP 32是物联网专用芯片，其算力性能，启动速度，稳定性，耐久度，可操作io数量及类型，啥都够，实在是太好用了。<br>首先，ESP32的片上资源和引脚接口特别丰富，打造出各种物联网场景智能设备绰绰有余了。现在物联网产品协议使用的最多的还是Wi-Fi，这两年BLE和BLE Mesh也发展的很快，Zigbee反而日渐势微，而ESP32同时支持Wi-Fi和蓝牙，加上其超高的性价比，前景不可限量。<br>其次，在嵌入式开发者中，ESP系列芯片深入人心，在国内某宝&#x2F;B站以及国外youtube等上面都能找到大量有意思的开发者作品。ESP8266资源太少，而且只支持Wi-Fi，加上现在ESP32的价格也已经非常便宜了，如果想打造一个智能设备，首选ESP32完全OK。<br>再次，现在已经是物联网的时代了，开发出的智能设备也不能只停留在开发台上，希望能远程控制，访问。但是想要同时做好智能设备开发、连接云端服务、手机设备程序等全链路开发难度还是不小的。而ESP32有云端一体的全链路开发框架，开发远程硬件应用就很方便，比如做个家里燃气&#x2F;火焰&#x2F;甲醛检测设备，发现危险就推送消息到手机。</p><p><img src="https://s2.loli.net/2024/10/17/EsfWxGmZeD5a3pF.png" alt="image.png"></p><h2 id="使用arduino自带IDE"><a href="#使用arduino自带IDE" class="headerlink" title="使用arduino自带IDE"></a>使用arduino自带IDE</h2><p>可以在 <a href="https://www.arduino.cc/en/software">Arduino 官网</a> 下载安装 Arduino IDE。安装后打开设置，在“附加开发板管理器网址”中加入 ESP32 稳定版索引：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://espressif.github.io/arduino-esp32/package_esp32_index.json</span><br></pre></td></tr></table></figure><p>然后打开开发板管理器，搜索并安装 <code>esp32</code>，再从“工具 → 开发板”选择自己的型号。国内网络环境也可以使用 Espressif 官方文档中提供的镜像索引。</p><p>上传前需要确认两个选项：</p><ul><li>端口选择了 ESP32 实际对应的串口；</li><li>开发板型号、Flash 大小和分区方案与手中硬件一致。</li></ul><p>下面是一个最小串口示例，上传后打开 <code>115200</code> 波特率的串口监视器即可验证开发环境：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Arduino.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setup</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  Serial.<span class="built_in">begin</span>(<span class="number">115200</span>);</span><br><span class="line">  <span class="built_in">delay</span>(<span class="number">500</span>);</span><br><span class="line">  Serial.<span class="built_in">println</span>(<span class="string">&quot;ESP32 Arduino is ready&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  Serial.<span class="built_in">printf</span>(<span class="string">&quot;millis: %lu\n&quot;</span>, <span class="built_in">millis</span>());</span><br><span class="line">  <span class="built_in">delay</span>(<span class="number">1000</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="在VScode上使用PlatformIO-IDE用Arduino开发Esp32"><a href="#在VScode上使用PlatformIO-IDE用Arduino开发Esp32" class="headerlink" title="在VScode上使用PlatformIO IDE用Arduino开发Esp32"></a>在VScode上使用PlatformIO IDE用Arduino开发Esp32</h2><p>强烈推荐这个方式，使用起来非常方便。<br>在VScode上安装PlatformIO拓展<br>使用PlatformIO创建新的project，Board选择Espressif esp32 Dev Module，然后选择Arduino框架（建议使用自己的目录而不是默认目录）。</p><p>这时候就可以在VScode上编写代码，编译，上传了。常用的 <code>platformio.ini</code> 如下：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[env:esp32dev]</span></span><br><span class="line"><span class="attr">platform</span> = espressif32</span><br><span class="line"><span class="attr">board</span> = esp32dev</span><br><span class="line"><span class="attr">framework</span> = ardui<span class="literal">no</span></span><br><span class="line"><span class="attr">monitor_speed</span> = <span class="number">115200</span></span><br></pre></td></tr></table></figure><p>PlatformIO 项目的主要目录包括：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">项目目录/</span><br><span class="line">├─ include/        # 头文件</span><br><span class="line">├─ lib/            # 项目私有库</span><br><span class="line">├─ src/main.cpp    # 程序入口</span><br><span class="line">└─ platformio.ini  # 开发板、框架和依赖配置</span><br></pre></td></tr></table></figure><p>需要第三方库时，可以在 PlatformIO 的 Library 页面安装，也可以写入 <code>lib_deps</code>，这样换一台电脑后仍能自动还原依赖：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">lib_deps</span> =</span><br><span class="line">  bblanchon/ArduinoJson</span><br></pre></td></tr></table></figure><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="找不到串口"><a href="#找不到串口" class="headerlink" title="找不到串口"></a>找不到串口</h3><p>先检查 USB 线是否支持数据传输，再检查 CP210x、CH340 等 USB 转串口驱动。Windows 下也可以在设备管理器中确认端口号。</p><h3 id="一直停在-Connecting"><a href="#一直停在-Connecting" class="headerlink" title="一直停在 Connecting..."></a>一直停在 <code>Connecting...</code></h3><p>部分开发板无法自动进入下载模式。可以在出现 <code>Connecting...</code> 时按住 <code>BOOT</code>，开始写入后松开；必要时按一下 <code>EN/RESET</code> 再重试。</p><h3 id="程序上传成功但没有串口输出"><a href="#程序上传成功但没有串口输出" class="headerlink" title="程序上传成功但没有串口输出"></a>程序上传成功但没有串口输出</h3><p>确认 <code>Serial.begin()</code> 与串口监视器的波特率一致，并检查程序是否在初始化阶段阻塞或反复重启。ESP32 启动日志通常使用 <code>115200</code> 波特率。</p><h3 id="引脚输出异常"><a href="#引脚输出异常" class="headerlink" title="引脚输出异常"></a>引脚输出异常</h3><p>不同 ESP32、ESP32-S2、ESP32-C3 和 ESP32-S3 开发板的可用 GPIO 不完全相同。使用启动配置脚、仅输入引脚或板载 Flash 占用引脚前，应先查看对应开发板的引脚图。</p><h2 id="下一步"><a href="#下一步" class="headerlink" title="下一步"></a>下一步</h2><p>完成最小程序后，可以依次练习 GPIO、PWM、ADC、I2C、SPI、UART、Wi-Fi 和蓝牙。建议每增加一个模块先写独立测试程序，确认硬件和库工作正常，再合并到完整项目中，排错会简单很多。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><em>[1]<a href="https://blog.csdn.net/Mark_md/article/details/121326060">ESP32使用Arduino环境，安装、编译、下载、安装库_arduino esp32-CSDN博客</a></em><br><em>[2]<a href="https://blog.csdn.net/qq_40288383/article/details/113116201">使用PlatformIO IDE在VScode上开发Arduino Esp32_platformio 删除库-CSDN博客</a></em><br><em>[3]<a href="https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html">Arduino ESP32：Installing</a></em><br><em>[4]<a href="https://docs.platformio.org/en/latest/boards/espressif32/esp32dev.html">PlatformIO：Espressif ESP32 Dev Module</a></em></p>]]></content>
    
    
    <summary type="html">简单介绍如何在esp32上使用arduino.</summary>
    
    
    
    <category term="物联网" scheme="https://zzz5.github.io/categories/%E7%89%A9%E8%81%94%E7%BD%91/"/>
    
    
    <category term="lot" scheme="https://zzz5.github.io/tags/lot/"/>
    
    <category term="arduino" scheme="https://zzz5.github.io/tags/arduino/"/>
    
    <category term="esp32" scheme="https://zzz5.github.io/tags/esp32/"/>
    
  </entry>
  
  <entry>
    <title>Docker使用说明</title>
    <link href="https://zzz5.github.io/2023/09/13/others/docker/"/>
    <id>https://zzz5.github.io/2023/09/13/others/docker/</id>
    <published>2023-09-13T01:49:00.000Z</published>
    <updated>2026-08-26T09:27:47.265Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="Docker是什么"><a href="#Docker是什么" class="headerlink" title="Docker是什么"></a>Docker是什么</h2><p>Docker 是一个用于开发、交付和运行应用程序的容器平台。它可以把程序、运行时和依赖打包成镜像，再从镜像启动相对隔离的容器，从而减少“在我电脑上明明可以运行”的环境差异。</p><p>容器不是简化版虚拟机。虚拟机通常包含完整操作系统和独立内核，而容器更接近一个被隔离的进程：它有自己的文件系统、网络和进程空间，但通常与其他容器共享宿主机内核。因此容器启动更快、占用资源更少，不过隔离边界和虚拟机也不完全相同。</p><p>Docker 主要解决以下问题：</p><ul><li>固定应用及依赖版本，让开发、测试和生产环境尽量一致；</li><li>快速创建和销毁隔离环境；</li><li>使用镜像仓库分发应用；</li><li>通过 Docker Compose 管理由多个容器组成的服务。</li></ul><h2 id="Docker的架构"><a href="#Docker的架构" class="headerlink" title="Docker的架构"></a>Docker的架构</h2><p><img src="https://s2.loli.net/2023/09/13/DxHLUBWY4f8pyCF.png" alt="docker-architecture.png"></p><p>Docker 使用客户端—服务端架构：</p><ul><li><strong>Docker Client</strong>：也就是平时使用的 <code>docker</code> 命令；</li><li><strong>Docker Daemon</strong>：后台的 <code>dockerd</code>，负责构建镜像、运行容器以及管理网络和存储；</li><li><strong>Registry</strong>：保存镜像的仓库，例如 Docker Hub；</li><li><strong>Docker Compose</strong>：使用 YAML 文件描述和管理一组相关容器。</li></ul><p>执行 <code>docker run nginx</code> 时，客户端会向 Daemon 发出请求。如果本地没有 <code>nginx</code> 镜像，Daemon 先从镜像仓库拉取它，再创建并启动容器。</p><h2 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h2><h3 id="镜像与容器"><a href="#镜像与容器" class="headerlink" title="镜像与容器"></a>镜像与容器</h3><p>镜像（Image）是创建容器的只读模板，容器（Container）是镜像的运行实例。同一个镜像可以启动多个互不影响的容器。</p><p>可以将镜像理解为程序安装包加运行环境，但容器运行时会额外增加一个可写层。删除容器时，这个可写层中的数据也会消失，因此数据库和用户文件需要放在 Volume 或挂载目录中。</p><h3 id="Volume-与-Bind-Mount"><a href="#Volume-与-Bind-Mount" class="headerlink" title="Volume 与 Bind Mount"></a>Volume 与 Bind Mount</h3><ul><li><strong>Volume</strong> 由 Docker 管理，适合数据库等需要长期保存的数据；</li><li><strong>Bind Mount</strong> 把宿主机指定目录映射到容器，适合开发时同步源码和配置文件。</li></ul><h3 id="Network"><a href="#Network" class="headerlink" title="Network"></a>Network</h3><p>容器默认运行在隔离网络中。端口映射让宿主机能够访问容器服务，自定义网络则允许多个容器通过容器名互相访问。</p><h2 id="安装与验证"><a href="#安装与验证" class="headerlink" title="安装与验证"></a>安装与验证</h2><p>Windows 和 macOS 可以安装 <a href="https://docs.docker.com/get-started/get-docker/">Docker Desktop</a>，Linux 建议按照对应发行版的 <a href="https://docs.docker.com/engine/install/">Docker Engine 官方安装说明</a> 操作。不要长期沿用来源不明的一键安装脚本。</p><p>安装完成后检查版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker version</span><br><span class="line">docker compose version</span><br></pre></td></tr></table></figure><p>运行官方测试容器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run --<span class="built_in">rm</span> hello-world</span><br></pre></td></tr></table></figure><p><code>--rm</code> 表示容器停止后自动删除。能够看到 Hello from Docker 的输出，就说明客户端、Daemon 和镜像拉取均正常。</p><h2 id="运行第一个服务"><a href="#运行第一个服务" class="headerlink" title="运行第一个服务"></a>运行第一个服务</h2><p>下面使用 Nginx 启动一个 Web 服务：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">  --name my-nginx \</span><br><span class="line">  -p 8080:80 \</span><br><span class="line">  nginx:alpine</span><br></pre></td></tr></table></figure><p>参数含义：</p><ul><li><code>-d</code>：后台运行；</li><li><code>--name my-nginx</code>：指定容器名称；</li><li><code>-p 8080:80</code>：把宿主机的 8080 端口映射到容器的 80 端口；</li><li><code>nginx:alpine</code>：使用 Nginx 的 Alpine 镜像标签。</li></ul><p>打开 <code>http://localhost:8080</code> 即可看到 Nginx 页面。查看容器和日志：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker ps</span><br><span class="line">docker logs -f my-nginx</span><br></pre></td></tr></table></figure><p>停止、再次启动和删除容器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">docker stop my-nginx</span><br><span class="line">docker start my-nginx</span><br><span class="line">docker <span class="built_in">rm</span> -f my-nginx</span><br></pre></td></tr></table></figure><h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><h3 id="镜像"><a href="#镜像" class="headerlink" title="镜像"></a>镜像</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker pull nginx:alpine       <span class="comment"># 拉取镜像</span></span><br><span class="line">docker images                  <span class="comment"># 查看本地镜像</span></span><br><span class="line">docker image inspect nginx:alpine</span><br><span class="line">docker rmi nginx:alpine        <span class="comment"># 删除未使用的镜像</span></span><br></pre></td></tr></table></figure><h3 id="容器"><a href="#容器" class="headerlink" title="容器"></a>容器</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">docker ps                      <span class="comment"># 查看运行中的容器</span></span><br><span class="line">docker ps -a                   <span class="comment"># 查看全部容器</span></span><br><span class="line">docker inspect my-nginx        <span class="comment"># 查看容器详细信息</span></span><br><span class="line">docker stats                   <span class="comment"># 实时查看资源占用</span></span><br><span class="line">docker <span class="built_in">exec</span> -it my-nginx sh    <span class="comment"># 进入运行中的容器</span></span><br><span class="line">docker <span class="built_in">cp</span> my-nginx:/etc/nginx/nginx.conf ./nginx.conf</span><br></pre></td></tr></table></figure><p>容器的主进程结束后，容器也会停止。如果容器一启动就退出，可以先使用 <code>docker ps -a</code> 找到它，再用 <code>docker logs 容器名</code> 查看原因。</p><h2 id="环境变量与端口"><a href="#环境变量与端口" class="headerlink" title="环境变量与端口"></a>环境变量与端口</h2><p>很多镜像使用环境变量接收配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker run --<span class="built_in">rm</span> \</span><br><span class="line">  -e APP_ENV=production \</span><br><span class="line">  -e TZ=Asia/Shanghai \</span><br><span class="line">  my-app:latest</span><br></pre></td></tr></table></figure><p>端口写法是 <code>宿主机端口:容器端口</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d -p 127.0.0.1:8080:80 nginx:alpine</span><br></pre></td></tr></table></figure><p>上面的服务只监听宿主机回环地址。如果写成 <code>-p 8080:80</code>，可能会向局域网或公网暴露端口，具体取决于宿主机防火墙和网络环境。</p><h2 id="数据持久化"><a href="#数据持久化" class="headerlink" title="数据持久化"></a>数据持久化</h2><h3 id="使用-Volume"><a href="#使用-Volume" class="headerlink" title="使用 Volume"></a>使用 Volume</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">docker volume create app-data</span><br><span class="line"></span><br><span class="line">docker run -d \</span><br><span class="line">  --name app \</span><br><span class="line">  -v app-data:/var/lib/app \</span><br><span class="line">  my-app:latest</span><br><span class="line"></span><br><span class="line">docker volume <span class="built_in">ls</span></span><br><span class="line">docker volume inspect app-data</span><br></pre></td></tr></table></figure><p>删除容器不会自动删除命名 Volume，因此重新创建容器后仍可以挂载原来的数据。</p><h3 id="挂载宿主机目录"><a href="#挂载宿主机目录" class="headerlink" title="挂载宿主机目录"></a>挂载宿主机目录</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">  --name static-site \</span><br><span class="line">  -p 8080:80 \</span><br><span class="line">  --mount <span class="built_in">type</span>=<span class="built_in">bind</span>,<span class="built_in">source</span>=<span class="string">&quot;<span class="subst">$(pwd)</span>/html&quot;</span>,target=/usr/share/nginx/html,<span class="built_in">readonly</span> \</span><br><span class="line">  nginx:alpine</span><br></pre></td></tr></table></figure><p>Windows PowerShell 需要把 <code>$(pwd)/html</code> 换成实际目录，例如 <code>D:/Web/html</code>。只读配置或静态文件建议加上 <code>readonly</code>，避免容器意外修改宿主机文件。</p><h2 id="使用-Dockerfile-构建镜像"><a href="#使用-Dockerfile-构建镜像" class="headerlink" title="使用 Dockerfile 构建镜像"></a>使用 Dockerfile 构建镜像</h2><p>假设当前目录有一个 Hexo 生成的 <code>public</code> 文件夹，可以编写以下 Dockerfile：</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> nginx:alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> public/ /usr/share/nginx/html/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><p>构建并运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker build -t my-blog:1.0 .</span><br><span class="line">docker run -d --name my-blog -p 8080:80 my-blog:1.0</span><br></pre></td></tr></table></figure><p>建议同时创建 <code>.dockerignore</code>，避免把无关内容发送给构建器：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">.git</span><br><span class="line">node_modules</span><br><span class="line">.deploy_git</span><br><span class="line">*.log</span><br></pre></td></tr></table></figure><p>镜像标签最好使用明确版本，例如 <code>my-blog:1.0</code>，不要在生产环境只依赖会变化的 <code>latest</code>。</p><h2 id="使用-Docker-Compose"><a href="#使用-Docker-Compose" class="headerlink" title="使用 Docker Compose"></a>使用 Docker Compose</h2><p>当项目包含 Web、数据库、缓存等多个服务时，逐个执行 <code>docker run</code> 会比较麻烦。可以使用 <code>compose.yaml</code> 统一描述：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">web:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">nginx:alpine</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;8080:80&quot;</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">./html:/usr/share/nginx/html:ro</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">redis:7-alpine</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">redis-data:/data</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line"></span><br><span class="line"><span class="attr">volumes:</span></span><br><span class="line">  <span class="attr">redis-data:</span></span><br></pre></td></tr></table></figure><p>常用 Compose 命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">docker compose up -d              <span class="comment"># 创建并启动服务</span></span><br><span class="line">docker compose ps                 <span class="comment"># 查看服务状态</span></span><br><span class="line">docker compose logs -f web        <span class="comment"># 跟踪指定服务日志</span></span><br><span class="line">docker compose pull               <span class="comment"># 拉取更新后的镜像</span></span><br><span class="line">docker compose up -d              <span class="comment"># 按新配置重建发生变化的服务</span></span><br><span class="line">docker compose down               <span class="comment"># 停止并删除容器和默认网络</span></span><br></pre></td></tr></table></figure><p><code>docker compose down</code> 默认不会删除命名 Volume；使用 <code>docker compose down -v</code> 会同时删除数据卷，执行前必须确认数据已经备份。</p><h2 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h2><p>创建自定义网络并启动两个容器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">docker network create app-net</span><br><span class="line">docker run -d --name redis --network app-net redis:7-alpine</span><br><span class="line">docker run -it --<span class="built_in">rm</span> --network app-net redis:7-alpine redis-cli -h redis ping</span><br></pre></td></tr></table></figure><p>同一自定义网络中的容器可以通过容器名 <code>redis</code> 访问，不需要固定容器 IP。Compose 会为项目自动创建网络，服务之间直接使用服务名通信。</p><h2 id="清理资源"><a href="#清理资源" class="headerlink" title="清理资源"></a>清理资源</h2><p>先查看磁盘占用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker system <span class="built_in">df</span></span><br></pre></td></tr></table></figure><p>按类型清理未使用资源：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker container prune</span><br><span class="line">docker image prune</span><br><span class="line">docker network prune</span><br><span class="line">docker volume prune</span><br></pre></td></tr></table></figure><p><code>docker system prune</code> 会一次清理多种未使用资源；带 <code>--volumes</code> 时还会清理未使用的数据卷。Volume 可能保存数据库数据，不要在不了解影响时使用 <code>docker system prune --volumes</code>。</p><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="容器名称冲突"><a href="#容器名称冲突" class="headerlink" title="容器名称冲突"></a>容器名称冲突</h3><p>如果出现 <code>container name is already in use</code>，说明同名容器仍然存在。使用 <code>docker ps -a</code> 找到它，然后选择继续使用、改名或删除。</p><h3 id="端口已被占用"><a href="#端口已被占用" class="headerlink" title="端口已被占用"></a>端口已被占用</h3><p>修改端口映射左侧的宿主机端口，例如把 <code>8080:80</code> 改为 <code>8081:80</code>，或者先停止占用该端口的程序。</p><h3 id="Linux-下没有权限访问-Docker"><a href="#Linux-下没有权限访问-Docker" class="headerlink" title="Linux 下没有权限访问 Docker"></a>Linux 下没有权限访问 Docker</h3><p>默认情况下 Docker Socket 需要 root 权限。可以暂时使用 <code>sudo docker ...</code>，或按照官方安装后的说明配置用户组。加入 <code>docker</code> 用户组相当于授予较高的系统权限，不应把不可信用户加入该组。</p><h3 id="镜像架构不匹配"><a href="#镜像架构不匹配" class="headerlink" title="镜像架构不匹配"></a>镜像架构不匹配</h3><p>Apple Silicon、ARM 服务器和部分开发板可能遇到 <code>platform does not match</code>。优先选择支持当前架构的多平台镜像；模拟其他架构虽然可行，但性能会下降。</p><h3 id="容器里修改的内容重建后消失"><a href="#容器里修改的内容重建后消失" class="headerlink" title="容器里修改的内容重建后消失"></a>容器里修改的内容重建后消失</h3><p>容器可写层不是可靠的数据保存位置。需要长期保存的内容应使用 Volume、Bind Mount，或者写入 Dockerfile 重新构建镜像。</p><h2 id="使用建议"><a href="#使用建议" class="headerlink" title="使用建议"></a>使用建议</h2><ul><li>为基础镜像指定版本，并定期重新构建以获取安全修复；</li><li>不要把密码、Token 和私钥直接写进 Dockerfile 或提交到仓库；</li><li>尽量让容器使用非 root 用户运行；</li><li>一个容器只承担清晰的职责，多个服务使用 Compose 组织；</li><li>为重要数据卷制定备份和恢复方案；</li><li>通过日志、健康检查和资源限制观察容器状态。</li></ul><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><ol><li><a href="https://docs.docker.com/">Docker Docs</a></li><li><a href="https://docs.docker.com/get-started/docker-overview/">Docker overview</a></li><li><a href="https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/">What is a container?</a></li><li><a href="https://docs.docker.com/compose/gettingstarted/">Docker Compose Quickstart</a></li></ol>]]></content>
    
    
    <summary type="html">Docker是一个用于开发、交付和运行应用程序的开放平台。Docker能够将应用程序与基础设施分离，以便快速交付软件。</summary>
    
    
    
    <category term="杂七杂八" scheme="https://zzz5.github.io/categories/%E6%9D%82%E4%B8%83%E6%9D%82%E5%85%AB/"/>
    
    
    <category term="Docker" scheme="https://zzz5.github.io/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>开始更新</title>
    <link href="https://zzz5.github.io/2023/09/01/diary/diary-2023-0901/"/>
    <id>https://zzz5.github.io/2023/09/01/diary/diary-2023-0901/</id>
    <published>2023-09-01T07:32:00.000Z</published>
    <updated>2023-09-01T07:52:07.530Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><p>残血归来</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="日记" scheme="https://zzz5.github.io/categories/%E6%97%A5%E8%AE%B0/"/>
    
    
    <category term="公告" scheme="https://zzz5.github.io/tags/%E5%85%AC%E5%91%8A/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu上使用Mosqitto部署Mqtt服务器</title>
    <link href="https://zzz5.github.io/2021/03/16/lot/mosquitto/"/>
    <id>https://zzz5.github.io/2021/03/16/lot/mosquitto/</id>
    <published>2021-03-16T13:11:00.000Z</published>
    <updated>2023-09-04T00:43:03.825Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>Mosqitto 是一款实现了消息推送协议 MQTT v3.1 的开源消息代理软件, 提供轻量级的, 支持可发布&#x2F;可订阅的的消息推送模式, 使设备对设备之间的短消息通信变得简单, 比如现在应用广泛的低功耗传感器, 手机, 嵌入式计算机, 微型控制器等移动设备.</p><p>而消息推送协议 MQTT 是物联网(loT)常用的一种协议, 它的特点有:</p><ol><li>使用发布&#x2F;订阅消息模式, 提供一对多的消息发布, 解除应用程序耦合.</li><li>小型传输, 开销很小(固定长度的头部是2字节), 协议交换最小化, 以降低网络流量. 这也是它在物联网领域广泛应用的主要原因之一.</li><li>支持三种QoS(Quality of Service，服务质量)：<ul><li>“至多一次”, 消息发布完全依赖底层TCP&#x2F;IP网络. 会发生消息丢失或重复. 这一级别可用于如下情况, 环境传感器数据, 丢失一次读记录无所谓, 因为不久后还会有第二次发送. 这一种方式主要普通APP的推送, 倘若你的智能设备在消息推送时未联网, 推送过去没收到, 再次联网也就收不到了.</li><li>“至少一次”, 确保消息到达, 但消息重复可能会发生.</li><li>“只有一次”, 确保消息到达一次. 在一些要求比较严格的计费系统中, 可以使用此级别. 在计费系统中, 消息重复或丢失会导致不正确的结果. 这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送, 确保用户收到且只会收到一次.</li></ul></li></ol><h2 id="下载安装"><a href="#下载安装" class="headerlink" title="下载安装"></a>下载安装</h2><p>这里我用的Ubuntu系统, 首先将mosquitto添加到储存库列表</p><p><code>sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa</code></p><p>再更新软件包</p><p><code>sudo apt-get update</code></p><p>然后安装mosquitto:</p><p><code>sudo apt-get install mosquitto</code></p><p>根据需要确定是否安装命令行客户端(因为mqtt已经有非常多的gui客户端了, 命令行客户端反而显得没那么好用了, 这里我推荐一个 <a href="https://mqttx.app/cn/">MATTX</a> , 各个平台都能用, 倒是挺方便的):</p><p><code>sudo apt-get install mosquitto-clients</code></p><h2 id="mosquitto详解"><a href="#mosquitto详解" class="headerlink" title="mosquitto详解"></a>mosquitto详解</h2><p>mosquitto主要分为几个部分, 分别为 mosquitto(运行服务器), mosquitto.conf(配置文件), mosquitto_passwd(用户配置), aclfile(用户权限配置).&#96;&#96;</p><h3 id="mosquitto"><a href="#mosquitto" class="headerlink" title="mosquitto"></a>mosquitto</h3><h4 id="命令"><a href="#命令" class="headerlink" title="命令"></a>命令</h4><blockquote><p>mosquitto [-c config file] [ -d | –daemon ] [-p port number] [-v]</p></blockquote><p>参数: </p><ul><li>-c config file: 从config文件加载配置. 如果没有给出, 则使用配置文件的默认值.</li><li>-d 或者 –daemon: 将mosquitto在后台以<a href="https://baike.baidu.com/item/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B">守护进程</a>的身份运行.</li><li>-p port number: 指定监听的端口, 默认的端口是1883. 也可以指定监听多个端口.</li><li>-v: 使用详细日志记录, 也可以在配置文件中设置.</li></ul><h4 id="关于重启mosquitto"><a href="#关于重启mosquitto" class="headerlink" title="关于重启mosquitto"></a>关于重启mosquitto</h4><p>一般来说, mosquitto是默认开机自动启动的, 可以通过删除<code>/etc/init.d/mosquitto</code>来关闭开机自启.</p><p>通常可以用 <code>ps -aux | grep mosquitto</code> 查找 mosquitto 运行的进程, 再用 <code>kill -9 进程号</code> 来关闭mosquitto, 但这很多时候会出现各种问题, 以至于无法关闭. 这时可以用 ‘&#x2F;etc&#x2F;init.d&#x2F;mosquitto stop’来关闭, 或者用’&#x2F;etc&#x2F;init.d&#x2F;mosquitto restart’来实现重启.</p><h3 id="mosquitto-conf"><a href="#mosquitto-conf" class="headerlink" title="mosquitto.conf"></a>mosquitto.conf</h3><h4 id="主要的设置"><a href="#主要的设置" class="headerlink" title="主要的设置"></a>主要的设置</h4><ul><li><p>acl_file file path: 设置访问控制列表文件的路径. 如果定义了该文件，则使用该文件的内容来控制客户端对代理上的主题的访问. 如果定义了此参数，则只有列出的主题才能访问.主题访问权限添加的行格式为：</p><blockquote><p>topic [read|write|readwrite] <topic><br>  访问类型由”Read”, “Write” 或 “ReadWrite” 控制. 此参数是可选的, 如果未指定, 则访问权限为读&#x2F;写. 与订阅中一样, <topic> 可以包含 + 或 # 通配符.<br>  如果允许匿名的话, 第一组主题将应用于匿名客户端.</p></blockquote><blockquote><p>user <username><br>  指定要控制访问的用户名</p></blockquote><blockquote><p>pattern [read|write|readwrite] <topic><br>  可以使用正则来控制访问的话题, 其中 %c 是匹配客户端的 client id, %u 是匹配客户端的 username.</p></blockquote></li></ul><p>例如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 第一行是匿名客户端可以访问的topic, 所有匿名用户都可以订阅话题 topic/anonymous.</span><br><span class="line">topic read topic/anonymous</span><br><span class="line"># 下面是用户admin可以访问的topic, admin可以订阅和发布 topic/ 下所有话题的信息.</span><br><span class="line">user admin</span><br><span class="line">topic readwirte topic/#</span><br><span class="line"># 正则限制, 所有用户都可以在话题 topic/username/data 发布消息.</span><br><span class="line">pattern write topic/%u/data</span><br></pre></td></tr></table></figure><ul><li><p>password_file: 用户&#x2F;密码文件, 默认格式: username:password</p></li><li><p>include_dir &#x2F;etc&#x2F;mosquitto&#x2F;conf.d: 自己的配置可以放到以下目录中</p></li><li><p>allow_anonymous [ true | false ]: 是否允许匿名, 默认是true.</p></li><li><p>allow_duplicate_messages [ true | false ]: 是否允许重复&#x2F;重叠订阅, 默认是true.</p></li><li><p>allow_zero_length_clientid [ true | false ]: 是否允许零长度客户端id, 默认为true.</p></li><li><p>use_username_as_clientid [ true | false ]: 使用用户名为客户端id, 默认为false.</p></li><li><p>pid_file &#x2F;var&#x2F;run&#x2F;mosquitto.pid: 服务进程的PID.</p></li><li><p>user username: 服务进程的系统用户.</p></li><li><p>port port number: 服务绑定的端口, 默认的是1883</p></li><li><p>retry_interval: 当QoS为1或2的消息已经被发送后, mosquitto在一段时间内仍未接收到客户端的反馈消息, 将重新发送消息. 默认为20秒.</p></li><li><p>max_connections -1: 最大连接数, -1代表不限制.</p></li><li><p>require_certificate false: 必须提供证书以保证数据安全性.</p></li><li><p>cafile: CA证书文件.</p></li><li><p>capath: CA证书目录.</p></li><li><p>certfile: PEM证书文件.</p></li><li><p>keyfile: PEM密钥文件.</p></li><li><p>autosave_interval 1800: 消息自动保存的间隔时间</p></li><li><p>autosave_on_changes false: 消息自动保存功能的开关</p></li><li><p>persistence true: 持久化功能的开关</p></li><li><p>persistence_file mosquitto.db: 持久化DB文件</p></li><li><p>persistence_location &#x2F;var&#x2F;lib&#x2F;mosquitto&#x2F;: 持久化DB文件目录</p></li><li><p>log_dest none: 4种日志模式: stdout, stderr, syslog, topic, none 则表示不记日志, 此配置可以提升些许性能.</p></li><li><p>log_type error: 选择日志的级别(可设置多项), 包括error, warning, notice, information.</p></li><li><p>connection_messages true: 是否记录客户端连接信息.</p></li><li><p>log_timestamp true: 是否记录日志时间</p></li></ul><h4 id="推荐的配置"><a href="#推荐的配置" class="headerlink" title="推荐的配置"></a>推荐的配置</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">pid_file /var/run/mosquitto.pid</span><br><span class="line"></span><br><span class="line">persistence true</span><br><span class="line"></span><br><span class="line">persistence_location /var/lib/mosquitto/</span><br><span class="line"></span><br><span class="line">include_dir /etc/mosquitto/conf.d</span><br><span class="line"></span><br><span class="line">allow_anonymous false</span><br><span class="line"></span><br><span class="line">password_file /etc/mosquitto/pwfile</span><br><span class="line"></span><br><span class="line">acl_file /etc/mosquitto/aclfile</span><br></pre></td></tr></table></figure><h3 id="mosquitto-passwd"><a href="#mosquitto-passwd" class="headerlink" title="mosquitto_passwd"></a>mosquitto_passwd</h3><blockquote><p>mosquitto_passwd [ -c | -D ] passwordfile username<br>mosquitto_passwd -b passwordfile username password<br>mosquitto_passwd -U passwordfile</p></blockquote><p>mosquitto_passwd用于管理mosquitto代理的mqtt服务器的密码文件, 其中username不能包含”:”.</p><p>参数:</p><ul><li>-b: 以批量模式运行. 这允许在命令行中提供密码, 这可以很方便, 但应小心使用, 因为密码将在命令行和命令历史记录中可见.</li><li>-c: 创建新的密码文件. 如果文件已经存在, 它将被覆盖.</li><li>-D: 从密码文件中删除指定的用户.</li><li>-U: 此选项可用于升级&#x2F;将带有纯文本密码的密码文件升级&#x2F;转换为使用哈希密码的文件. 它将修改指定的文件, 它无法检测密码是否已经哈希，因此在已经包含哈希密码的密码文件中使用它将根据旧哈希生成新的哈希, 并使密码文件无法使用.</li><li>passwordfile: 要修改密码的文件.</li><li>username: 添加&#x2F;更新&#x2F;删除的用户名.</li><li>password: 在批次模式下使用的密码.</li></ul><p>例如:<br>将用户test添加到新的密码文件:<br><code>mosquitto_passwd -c /etc/mosquitto/passwd test</code></p><p>从密码文件中删除用户test:<br><code>mosquitto_passwd -D /etc/mosquitto/passwd test</code></p><h3 id="aclfile"><a href="#aclfile" class="headerlink" title="aclfile"></a>aclfile</h3><p>详见 <a href="#%E4%B8%BB%E8%A6%81%E7%9A%84%E8%AE%BE%E7%BD%AE">主要的设置</a></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><em>[1]<a href="https://mosquitto.org/man/mosquitto-8.html">mosquitto</a></em><br><em>[2]<a href="https://mosquitto.org/man/mosquitto-conf-5.html">mosquitto.conf</a></em><br><em>[3]<a href="https://mosquitto.org/man/mosquitto_passwd-1.html">mosquitto_passwd</a></em><br><em>[4]<a href="https://www.cnblogs.com/cxt-janson/p/10136128.html">mosquitto.conf配置参数详细说明</a></em></p>]]></content>
    
    
    <summary type="html">简单介绍如何在Ubuntu平台使用Mosqitto部署和使用mqtt服务器.</summary>
    
    
    
    <category term="物联网" scheme="https://zzz5.github.io/categories/%E7%89%A9%E8%81%94%E7%BD%91/"/>
    
    
    <category term="lot" scheme="https://zzz5.github.io/tags/lot/"/>
    
    <category term="mosqitto" scheme="https://zzz5.github.io/tags/mosqitto/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu平台使用nginx部署Django项目</title>
    <link href="https://zzz5.github.io/2020/11/04/web/django/django-nginx/"/>
    <id>https://zzz5.github.io/2020/11/04/web/django/django-nginx/</id>
    <published>2020-11-04T08:06:00.000Z</published>
    <updated>2020-11-04T09:33:30.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>在 Django 的学习过程中, 我们一直使用 Django 的 manage.py 的runserver 命令来运行 Django 应用, 这个命令启动的是 Django 自带的用于开发的简易服务器, 它是一个用纯 Python 写的轻量级的 Web 服务器. 这个服务器内置在 Django 中是为了方便开发, 并不适用于实际的生产环境. 当项目真正部署上线的时候我们就必须 Django 项目部署到特定的web服务器上了.</p><p>这里我们采用的是 Ubuntu + nginx + django 方式.</p><h2 id="我的配置"><a href="#我的配置" class="headerlink" title="我的配置"></a>我的配置</h2><ul><li>系统: <a href="https://ubuntu.com/download/desktop">ubuntu-20.04.1-desktop-amd64</a></li><li>Web服务器软件: nginx&#x2F;1.18.0 (Ubuntu)</li><li>uwsgi: uWSGI-2.0 </li><li>Django: Django-3.1.3</li></ul><h2 id="安装Nginx"><a href="#安装Nginx" class="headerlink" title="安装Nginx"></a>安装Nginx</h2><p>Nginx是一款轻量级的Web服务器&#x2F;反向代理服务器及电子邮件(IMAP&#x2F;POP3)代理服务器, 并在一个BSD-like协议下发行. 其特点是占用内存少, 并发能力强, 事实上nginx的并发能力确实在同类型的网页服务器中表现较好.</p><p>打开Ubuntu终端, 利用Ubuntu的仓库安装, 运行命令<code>sudo apt-get install nginx</code>即可进行安装.</p><p>启动, 关闭, 重载nginx则可用以下命令</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">nginx</span><br><span class="line">nginx -s stop</span><br><span class="line">nginx -s reload</span><br></pre></td></tr></table></figure><p>这里可以启动测试一下, 进入 <a href="http://127.0.0.1/">http://127.0.0.1:80/</a> 页面应该会出现”Welcome to nginx!”界面, 这样就说明启动成功了.</p><p>这样安装的Nginx的配置文件放在<code>/etc/nginx/nginx.conf</code>位置, 后续我们要对配置进行修改.</p><h2 id="安装uwsgi"><a href="#安装uwsgi" class="headerlink" title="安装uwsgi"></a>安装uwsgi</h2><p>是实现了uwsgi和WSGI两种协议的Web服务器, 但是并不全面, 所以我们后续还要配置nginx服务器</p><ul><li>uwsgi:<a href="https://pypi.python.org/pypi/uWSGI">https://pypi.python.org/pypi/uWSGI</a></li><li>uwsgi 参数详解：<a href="http://uwsgi-docs.readthedocs.org/en/latest/Options.html">http://uwsgi-docs.readthedocs.org/en/latest/Options.html</a></li></ul><p>这里我们可以使用pip进行安装<code>pip3 install uwsgi</code></p><h2 id="配置Nginx和uwsgi"><a href="#配置Nginx和uwsgi" class="headerlink" title="配置Nginx和uwsgi"></a>配置Nginx和uwsgi</h2><p>配置uwsgi需要我们在项目根目录创建一个ini或者xml文件, 项目目录如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysite/</span><br><span class="line">├── manage.py</span><br><span class="line">├── mysite/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── settings.py</span><br><span class="line">│   ├── urls.py</span><br><span class="line">│   └── wsgi.py</span><br><span class="line">└── uwsgi.ini</span><br></pre></td></tr></table></figure><p>在我们通过Django创建mysite项目时, 在子目录mysite下已经帮我们生成的 wsgi.py文件. 所以, 我们只需要再创建uwsgi的配置文件即可, uwsgi支持多种类型的配置文件, 如xml，ini等, 此处，使用ini类型的配置.</p><p>ini 文件内容如下：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[uwsgi]</span></span><br><span class="line"><span class="comment"># Django-related settings</span></span><br><span class="line"><span class="attr">socket</span> = :<span class="number">8099</span></span><br><span class="line"><span class="attr">chdir</span>           = /home/mysite  <span class="comment">#这里写入你自己项目的地址</span></span><br><span class="line"><span class="attr">module</span>          = mysite.wsgi</span><br><span class="line"><span class="attr">master</span>          = <span class="literal">true</span></span><br><span class="line"><span class="attr">processes</span>       = <span class="number">4</span></span><br><span class="line"><span class="attr">vacuum</span>          = <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>uwsgi的配置常用ini文件, 这里先列出一些参数:</p><ul><li>http: 协议类型和端口号</li><li>processes: 开启的进程数量</li><li>workers: 开启的进程数量，等同于processes（官网的说法是spawn the specified number ofworkers &#x2F; processes）</li><li>chdir: 指定运行目录（chdir to specified directory before apps loading）</li><li>wsgi-file: 载入wsgi-file（load .wsgi file）</li><li>stats: 在指定的地址上，开启状态服务（enable the stats server on the specified address）</li><li>threads: 运行线程。由于GIL的存在，我觉得这个真心没啥用。（run each worker in prethreaded mode with the specified number of threads）</li><li>master: 允许主进程存在（enable master process）</li><li>daemonize: 使进程在后台运行，并将日志打到指定的日志文件或者udp服务器（daemonize uWSGI）。实际上最常用的，还是把运行记录输出到一个本地文件上。</li><li>pidfile: 指定pid文件的位置，记录主进程的pid号。</li><li>vacuum: 当服务器退出的时候自动清理环境，删除unix socket文件和pid文件（try to remove all of the generated file&#x2F;sockets）</li></ul><p>接下来, 进入mysite项目目录下, 通过uwsgi命令读取uwsgi.ini文件启动项目.<br><code>uwsgi --ini myweb_uwsgi.ini</code><br>注意查看uwsgi的启动信息, 如果有错, 就要检查配置文件的参数是否设置有误.</p><p>再接下来要做的就是修改nginx.conf配置文件. 打开&#x2F;etc&#x2F;nginx&#x2F;nginx.conf文件, 编辑内容: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">events &#123;</span><br><span class="line">    worker_connections  1024;</span><br><span class="line">&#125;</span><br><span class="line">http &#123;</span><br><span class="line">    include       mime.types;</span><br><span class="line">    default_type  application/octet-stream;</span><br><span class="line">    sendfile        on;</span><br><span class="line">    server &#123;</span><br><span class="line">        listen         80; </span><br><span class="line">        server_name    127.0.0.1; #这里一般填写网址, 没有的话就填写127.0.0.1</span><br><span class="line">        charset UTF-8;</span><br><span class="line">        access_log      /var/log/nginx/mysite_access.log;</span><br><span class="line">        error_log       /var/log/nginx/mysite_error.log;</span><br><span class="line">        client_max_body_size 75M;</span><br><span class="line">        location / &#123; </span><br><span class="line">            include uwsgi_params;</span><br><span class="line">            uwsgi_pass 127.0.0.1:8099; # 该处端口要和uwsgi的ini文件中一样</span><br><span class="line">            uwsgi_read_timeout 2;</span><br><span class="line">        &#125;   </span><br><span class="line">        location /static &#123;</span><br><span class="line">            expires 30d;</span><br><span class="line">            autoindex on; </span><br><span class="line">            add_header Cache-Control private;</span><br><span class="line">            alias /home/mysite/static/;  # 静态文件地址</span><br><span class="line">        &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>关于nginx.conf文件的配置, 可以参考 <a href="https://docs.nginx.com/nginx-app-protect/configuration/">NGINX Docs | Configuration Guide</a></p><p>现在重新启动nginx，翻看上面重启动nginx的命令. 然后, 访问: <a href="http://127.0.0.1/">http://127.0.0.1:80/</a> 即可.</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li><em><a href="https://www.cnblogs.com/fnng/p/5268633.html">通过Nginx部署Django（基于ubuntu) - 虫师 - 博客园</a></em></li><li><em><a href="https://www.jianshu.com/p/d6f9138fab7b">Django快速部署简约版 v3.0 - 简书</a></em></li></ol>]]></content>
    
    
    <summary type="html">简单介绍如何在Ubuntu平台使用nginx部署Django项目</summary>
    
    
    
    <category term="web" scheme="https://zzz5.github.io/categories/web/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="django" scheme="https://zzz5.github.io/tags/django/"/>
    
  </entry>
  
  <entry>
    <title>使用Matplotlib绘制嵌套饼图</title>
    <link href="https://zzz5.github.io/2020/10/13/python/matplotlib/python-matplotlib-nested%20pie%20chart/"/>
    <id>https://zzz5.github.io/2020/10/13/python/matplotlib/python-matplotlib-nested%20pie%20chart/</id>
    <published>2020-10-13T01:30:00.000Z</published>
    <updated>2021-03-16T12:36:08.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="嵌套饼图"><a href="#嵌套饼图" class="headerlink" title="嵌套饼图"></a>嵌套饼图</h2><p>我们可以用嵌套饼图更直观地展示某些的类型数据, 下面是用一个例子详细介绍如何用python绘制嵌套饼图.</p><p>这里要绘制的是一个最内圈 6 个类别, 每个类别再细分为几个方向, 各个方向上再进行细分.</p><p>大致的效果如下:</p><p><img src="/assets/blogImg/matplotlib/matplotlib16.svg" alt="matplotlib16"></p><p>下面我们来一步一步实现这个嵌套饼图.</p><h2 id="绘制饼图"><a href="#绘制饼图" class="headerlink" title="绘制饼图"></a>绘制饼图</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">ax.pie( vals_first, radius=<span class="number">1</span>-size-size,</span><br><span class="line">        colors=inner_colors, labels=labels_first, </span><br><span class="line">        labeldistance=<span class="number">0.5</span>, rotatelabels=<span class="literal">True</span>, </span><br><span class="line">        textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;, wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br><span class="line"></span><br><span class="line">ax.pie( vals_second.flatten(), radius=<span class="number">1</span>-size, </span><br><span class="line">        colors=outer_colors, labels=labels_seocnd, </span><br><span class="line">        labeldistance=<span class="number">0.7</span>,  rotatelabels=<span class="literal">True</span>, </span><br><span class="line">        textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;, wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br><span class="line"></span><br><span class="line">ax.pie( vals_third.flatten(), radius=<span class="number">1</span>, </span><br><span class="line">        colors=outer_colors, labels=labels_third, </span><br><span class="line">        labeldistance=<span class="number">0.8</span>,  rotatelabels=<span class="literal">True</span>, </span><br><span class="line">        textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;, wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br></pre></td></tr></table></figure><p>这里是使用matplotlib绘制饼图的代码, 绘制了三层饼图, 对每一层饼图的半径和宽度进行了设置, 使得三层饼图可以一层一层套在一起.</p><p>这些参数中, 第一个参数为绘制饼图的数值, radius为饼图的半径, textprop为标签的属性, 设置rotatelabels为True, 使得标签自动旋转. wedgeprops中可以设置饼图的各项数据, 这里我们设置了饼图的宽度和边颜色, 使得饼图显示成嵌套饼图的样子.</p><p>这边数据, 颜色, 标签的设置会在后面慢慢细说.</p><h2 id="数据设置"><a href="#数据设置" class="headerlink" title="数据设置"></a>数据设置</h2><p>数据方面, 由于这次的数据差别太大, 若按照比例来绘制的话, 必然有一些数据显示不出来, 所以我选择将所有数据添加一个固定的数值, 再按照比例来绘制. 但是还有一个问题, 部分数据过大或过小, 会非常影响图形的观感, 这里我只能在绘制图形时手动设置该部分所占的大小.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 第二圈的数据</span></span><br><span class="line">vals_b = np.array([</span><br><span class="line">    [<span class="number">47.5</span>,<span class="number">11.7</span>,<span class="number">15.2</span>,<span class="number">9.6</span>],</span><br><span class="line">    [<span class="number">0</span>,<span class="number">44.8</span>,<span class="number">7.5</span>,<span class="number">0</span>], </span><br><span class="line">    [<span class="number">9.2</span>, <span class="number">68.5</span> , <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.2</span>, <span class="number">7.2</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">80</span>,<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.7</span>, <span class="number">18.9</span>, <span class="number">0</span>, <span class="number">0</span>]</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三圈的数据</span></span><br><span class="line">vals_c = np.array([ </span><br><span class="line">    [<span class="number">47.5</span>,<span class="number">11.7</span>,<span class="number">15.2</span>,<span class="number">9.6</span>],</span><br><span class="line">    [<span class="number">0</span>,<span class="number">36.6</span>,<span class="number">8.2</span>,<span class="number">7.5</span>], </span><br><span class="line">    [<span class="number">9.2</span>,<span class="number">38.1</span>,<span class="number">30.4</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.2</span>, <span class="number">5.8</span>, <span class="number">1.4</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">80</span>,<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.7</span>, <span class="number">18.9</span>, <span class="number">0</span>, <span class="number">0</span>]</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第一圈的数据</span></span><br><span class="line">vals_inner = vals_middle.<span class="built_in">sum</span>(axis=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绘图时最内圈使用的数值为内圈各类数据加上base</span></span><br><span class="line">vals_first = vals_inner + base</span><br><span class="line"></span><br><span class="line">’‘’</span><br><span class="line">绘图时第二圈使用的数值, 因为最内圈每个类别都加上了base, 所以为了确保第二圈的数值和内圈相匹配, 第二圈的各类别要按照各自所占的比例分配各类的总数值.</span><br><span class="line">‘’‘</span><br><span class="line">vals_second = np.zeros((<span class="number">6</span>, <span class="number">4</span>))</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">    s_a = vals_first[i]</span><br><span class="line">    s_b = vals_a[i].<span class="built_in">sum</span>()</span><br><span class="line">    <span class="comment"># 如果第二圈某类总数值为0, 则分配base.</span></span><br><span class="line">    <span class="keyword">if</span> s_b == <span class="number">0.0</span>:</span><br><span class="line">        vals_second[i][<span class="number">1</span>] = base</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        vals_second[i][j] = (vals_mid[i][j] / s_b) * s_a</span><br><span class="line">    </span><br><span class="line"><span class="comment"># 第三圈使用的数值, 和上方同理</span></span><br><span class="line">vals_third = np.zeros((<span class="number">6</span>, <span class="number">4</span>))</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">    s_a = vals_first[i]</span><br><span class="line">    s_b = vals_outer[i].<span class="built_in">sum</span>()</span><br><span class="line">    <span class="keyword">if</span> s_b == <span class="number">0.0</span>:</span><br><span class="line">        vals_third[i][<span class="number">1</span>] = base</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        vals_third[i][j] = (vals_outer[i][j] / s_b) * s_a</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="颜色设置"><a href="#颜色设置" class="headerlink" title="颜色设置"></a>颜色设置</h2><p><a href="https://matplotlib.org/tutorials/colors/colormaps.html#sphx-glr-tutorials-colors-colormaps-py">官方自带的 Colormaps</a>, 这里我需要的是6×4共24种颜色, 于是我选择了colormap tab20c的全部色彩和 tab20b 中 5至8 颜色, 一共24种, 当然你也可以通过 rgba 自己定义颜色而不是选择colormap. </p><p><img src="/assets/blogImg/matplotlib/matplotlib17.png" alt="matplotlib17"></p><p>具体代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获取colormap tab20c和tab20b的颜色</span></span><br><span class="line">cmap_c = plt.get_cmap(<span class="string">&quot;tab20c&quot;</span>)</span><br><span class="line">cmap_b = plt.get_cmap(<span class="string">&quot;tab20b&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用tab20c的全部颜色和tab20b中的 5至8 颜色</span></span><br><span class="line">cmap_1 = cmap_c(np.arange(<span class="number">20</span>))</span><br><span class="line">cmap_2 = cmap_b(np.array([<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>]))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 内圈的颜色是每4个颜色中色彩最深的那个. vstack是将两类颜色叠加在一起</span></span><br><span class="line">inner_colors = np.vstack((cmap_1[::<span class="number">4</span>], cmap_2[<span class="number">0</span>]))</span><br><span class="line"><span class="comment"># 外圈的颜色是全部24种颜色</span></span><br><span class="line">outer_colors = np.vstack((cmap_1, cmap_2))</span><br></pre></td></tr></table></figure><h2 id="标签设置"><a href="#标签设置" class="headerlink" title="标签设置"></a>标签设置</h2><p>标签总共是三圈的标签, 内圈, 中圈和外圈, 这里内圈我为了更美观得显示数据, 更改了某些类别的占比, 所以不能直接从输入数据中获取到值, 只能手动输入数值.</p><p>中圈和外圈也是同理, 因为绘制的量不大, 中圈和外圈我全部采取了手动输入的方法, 事实上效率确实是大大下降了, 但是毕竟是为了图案美观, 不得已而为之. 如果数据允许的话, 这三种标签中的数据都可以采用输入的数据, 而非手动填写.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line">labels_first=[<span class="string">&quot;餐厨废弃物\n&#123;&#125;万吨&quot;</span>.<span class="built_in">format</span>(vals_first[<span class="number">0</span>]), </span><br><span class="line">        <span class="string">&quot;农业秸秆\n&#123;&#125;万吨&quot;</span>.<span class="built_in">format</span>(vals_first[<span class="number">1</span>]), </span><br><span class="line">        <span class="string">&quot;水草\n151.2万吨&quot;</span>, </span><br><span class="line">        <span class="string">&quot;园林绿化\n废弃物\n&#123;&#125;万吨&quot;</span>.<span class="built_in">format</span>(vals_first[<span class="number">3</span>]),</span><br><span class="line">        <span class="string">&quot;淤泥\n432.0万吨&quot;</span>,</span><br><span class="line">        <span class="string">&quot;畜禽粪便\n21.6万吨&quot;</span></span><br><span class="line">        ]</span><br><span class="line"></span><br><span class="line">labels_seocnd=[</span><br><span class="line">    <span class="string">&quot;未分类收集\n67.6万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;生物干化\n3.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;厌氧发酵\n10.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;油水分离\n2.6万吨&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;粉碎\n46.8万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;好氧发酵\n3.5万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未处理\n4.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;藻水分离\n147.0万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未处理\n1.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;粉碎\n7.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;堆放\n432.0万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未处理\n0.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;好氧发酵\n19.9万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">labels_third=[</span><br><span class="line">    <span class="string">&quot;未处理\n67.5万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化、发电\n3.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;沼气、沼渣发电\n10.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;焚烧\n2.6万吨&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;还田\n42.6万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;燃料化\n4.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n3.5万吨&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n4.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;燃料化\n80.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n66.8万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n1.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n5.8万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;燃料化\n1.4万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n432.0万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n0.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n19.9万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h2 id="图例"><a href="#图例" class="headerlink" title="图例"></a>图例</h2><p>这里因为只需要内圈的图例, 所以我把内圈的handels单独拿了出来</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">handles, labels =  ax.pie(vals_first, radius=<span class="number">1</span>-size-size, </span><br><span class="line">                labels=labels_first, </span><br><span class="line">                labeldistance=<span class="number">0.5</span>,  rotatelabels=<span class="literal">True</span>, textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;, </span><br><span class="line">                colors=inner_colors, wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br></pre></td></tr></table></figure><p>再绘制图例时不再简单使用<code>plt.legend()</code>而是使用:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">plt.legend(handles=handles, labels=[</span><br><span class="line">        <span class="string">&quot;餐厨废弃物&quot;</span>, </span><br><span class="line">        <span class="string">&quot;农业秸秆&quot;</span>, </span><br><span class="line">        <span class="string">&quot;水草&quot;</span>, </span><br><span class="line">        <span class="string">&quot;园林绿化废弃物&quot;</span>, </span><br><span class="line">        <span class="string">&quot;淤泥&quot;</span>,</span><br><span class="line">        <span class="string">&quot;畜禽粪便&quot;</span>],</span><br><span class="line">        loc = <span class="number">1</span></span><br><span class="line">        )</span><br></pre></td></tr></table></figure><p>这样出来的图中就只会显示最内层的图例了.</p><h2 id="附全部代码"><a href="#附全部代码" class="headerlink" title="附全部代码"></a>附全部代码</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> matplotlib <span class="keyword">import</span> pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">size = <span class="number">0.25</span></span><br><span class="line">base = <span class="number">50</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&#x27;font.family&#x27;</span>] = <span class="string">&#x27;SimHei&#x27;</span></span><br><span class="line">fig, ax = plt.subplots(figsize = (<span class="number">10</span>, <span class="number">10</span>))</span><br><span class="line"></span><br><span class="line">vals_middle = np.array([</span><br><span class="line">    [<span class="number">47.5</span>,<span class="number">11.7</span>,<span class="number">15.2</span>,<span class="number">9.6</span>],</span><br><span class="line">    [<span class="number">0</span>,<span class="number">44.8</span>,<span class="number">7.5</span>,<span class="number">0</span>], </span><br><span class="line">    [<span class="number">9.2</span>, <span class="number">68.5</span> , <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.2</span>, <span class="number">7.2</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">80</span>,<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.7</span>, <span class="number">18.9</span>, <span class="number">0</span>, <span class="number">0</span>]</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">vals_outer = np.array([ </span><br><span class="line">    [<span class="number">47.5</span>,<span class="number">11.7</span>,<span class="number">15.2</span>,<span class="number">9.6</span>],</span><br><span class="line">    [<span class="number">0</span>,<span class="number">36.6</span>,<span class="number">8.2</span>,<span class="number">7.5</span>], </span><br><span class="line">    [<span class="number">9.2</span>,<span class="number">38.1</span>,<span class="number">30.4</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.2</span>, <span class="number">5.8</span>, <span class="number">1.4</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">80</span>,<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">1.7</span>, <span class="number">18.9</span>, <span class="number">0</span>, <span class="number">0</span>]</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">vals_inner = vals_middle.<span class="built_in">sum</span>(axis=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 最内圈使用的数值为内圈各类数据加上base</span></span><br><span class="line">vals_first = vals_inner + base</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">第二圈使用的数值, 因为最内圈每个类别都加上了base, 所以为了确保第二圈的数值和内圈相匹配, 第二圈的各类别要按照各自所占的比例分配各类的总数值.</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">vals_second = np.zeros((<span class="number">6</span>, <span class="number">4</span>))</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">    s_a = vals_first[i]</span><br><span class="line">    s_b = vals_middle[i].<span class="built_in">sum</span>()</span><br><span class="line">    <span class="comment"># 如果第二圈某类总数值为0, 则分配base.</span></span><br><span class="line">    <span class="keyword">if</span> s_b == <span class="number">0.0</span>:</span><br><span class="line">        vals_second[i][<span class="number">1</span>] = base</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        vals_second[i][j] = (vals_middle[i][j] / s_b) * s_a</span><br><span class="line">    </span><br><span class="line"><span class="comment"># 第三圈使用的数值, 和上方同理</span></span><br><span class="line">vals_third = np.zeros((<span class="number">6</span>, <span class="number">4</span>))</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">    s_a = vals_first[i]</span><br><span class="line">    s_b = vals_outer[i].<span class="built_in">sum</span>()</span><br><span class="line">    <span class="keyword">if</span> s_b == <span class="number">0.0</span>:</span><br><span class="line">        vals_third[i][<span class="number">1</span>] = base</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        vals_third[i][j] = (vals_outer[i][j] / s_b) * s_a</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取colormap tab20c和tab20b的颜色</span></span><br><span class="line">cmap_c = plt.get_cmap(<span class="string">&quot;tab20c&quot;</span>)</span><br><span class="line">cmap_b = plt.get_cmap(<span class="string">&quot;tab20b&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用tab20c的全部颜色和tab20b中的 5至8 颜色</span></span><br><span class="line">cmap_1 = cmap_c(np.arange(<span class="number">20</span>))</span><br><span class="line">cmap_2 = cmap_b(np.array([<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>]))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 内圈的颜色是每4个颜色中色彩最深的那个. vstack是将两类颜色叠加在一起</span></span><br><span class="line">inner_colors = np.vstack((cmap_1[::<span class="number">4</span>], cmap_2[<span class="number">0</span>]))</span><br><span class="line"><span class="comment"># 外圈的颜色是全部24种颜色</span></span><br><span class="line">outer_colors = np.vstack((cmap_1, cmap_2))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">labels_first=[<span class="string">&quot;餐厨废弃物\n&#123;&#125;万吨&quot;</span>.<span class="built_in">format</span>(vals_inner[<span class="number">0</span>]), </span><br><span class="line">        <span class="string">&quot;农业秸秆\n&#123;&#125;万吨&quot;</span>.<span class="built_in">format</span>(vals_inner[<span class="number">1</span>]), </span><br><span class="line">        <span class="string">&quot;水草\n151.2万吨&quot;</span>, </span><br><span class="line">        <span class="string">&quot;园林绿化\n废弃物\n&#123;&#125;万吨&quot;</span>.<span class="built_in">format</span>(vals_inner[<span class="number">3</span>]),</span><br><span class="line">        <span class="string">&quot;淤泥\n432.0万吨&quot;</span>,</span><br><span class="line">        <span class="string">&quot;畜禽粪便\n21.6万吨&quot;</span></span><br><span class="line">        ]</span><br><span class="line"></span><br><span class="line">labels_seocnd=[</span><br><span class="line">    <span class="string">&quot;未分类收集\n67.6万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;生物干化\n3.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;厌氧发酵\n10.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;油水分离\n2.6万吨&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;粉碎\n46.8万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;好氧发酵\n3.5万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未处理\n4.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;藻水分离\n147.0万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未处理\n1.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;粉碎\n7.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;堆放\n432.0万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未处理\n0.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;好氧发酵\n19.9万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">labels_third=[</span><br><span class="line">    <span class="string">&quot;未处理\n67.5万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化、发电\n3.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;沼气、沼渣发电\n10.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;焚烧\n2.6万吨&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;还田\n42.6万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;燃料化\n4.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n3.5万吨&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n4.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;燃料化\n80.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n66.8万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n1.2万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n5.8万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;燃料化\n1.4万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n432.0万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;未利用\n0.7万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;肥料化\n19.9万吨&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">handles, labels =  ax.pie(vals_first, radius=<span class="number">1</span>-size-size, </span><br><span class="line">                labels=labels_first, </span><br><span class="line">                labeldistance=<span class="number">0.5</span>,  rotatelabels=<span class="literal">True</span>, textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;, </span><br><span class="line">                colors=inner_colors, wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br><span class="line"></span><br><span class="line">ax.pie(vals_second.flatten(),   radius=<span class="number">1</span>-size, colors=outer_colors,</span><br><span class="line">    labels=labels_seocnd, </span><br><span class="line">    labeldistance=<span class="number">0.7</span>,  rotatelabels=<span class="literal">True</span>, textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;, </span><br><span class="line">    wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br><span class="line"></span><br><span class="line">ax.pie(vals_third.flatten(), radius=<span class="number">1</span>, colors=outer_colors,</span><br><span class="line">    labels=labels_third, </span><br><span class="line">    labeldistance=<span class="number">0.8</span>,  rotatelabels=<span class="literal">True</span>, textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">11</span>&#125;,</span><br><span class="line">    wedgeprops=<span class="built_in">dict</span>(width=size, edgecolor=<span class="string">&#x27;w&#x27;</span>))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">plt.title(<span class="string">&#x27;某市有机废弃物产生、处理、利用情况&#x27;</span>, fontsize=<span class="number">25</span>)</span><br><span class="line">plt.legend(handles=handles, labels=[</span><br><span class="line">        <span class="string">&quot;餐厨废弃物&quot;</span>, </span><br><span class="line">        <span class="string">&quot;农业秸秆&quot;</span>, </span><br><span class="line">        <span class="string">&quot;水草&quot;</span>, </span><br><span class="line">        <span class="string">&quot;园林绿化废弃物&quot;</span>, </span><br><span class="line">        <span class="string">&quot;淤泥&quot;</span>,</span><br><span class="line">        <span class="string">&quot;畜禽粪便&quot;</span>],</span><br><span class="line">        loc = <span class="number">1</span></span><br><span class="line">        )</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">Matplotlib绘制嵌套饼图.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="matplotlib" scheme="https://zzz5.github.io/tags/matplotlib/"/>
    
  </entry>
  
  <entry>
    <title>Python之Seaborn库学习</title>
    <link href="https://zzz5.github.io/2020/07/25/python/seaborn/python-seaborn/"/>
    <id>https://zzz5.github.io/2020/07/25/python/seaborn/python-seaborn/</id>
    <published>2020-07-25T03:18:00.000Z</published>
    <updated>2020-10-25T13:10:02.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="Seaborn库简介"><a href="#Seaborn库简介" class="headerlink" title="Seaborn库简介"></a>Seaborn库简介</h2><p>Seborn是一个基于matplotlib的Python数据可视化库, 它提供了一个高度交互式界面, 用于绘制有吸引力和信息量的统计图形.</p><p>Seaborn其实是在matplotlib的基础上进行了更高级的API封装, 从而使得作图更加容易, 在大多数情况下使用seaborn就能做出很具有吸引力的图, 而使用matplotlib就能制作具有更多特色的图. 所以应该把Seaborn视为matplotlib的补充, 而不是替代物. 同时它能高度兼容numpy与pandas以及scipy与statsmodels等科学计算库. 掌握seaborn能很大程度帮助我们更高效的观察数据与图表, 并且更加深入了解它们.</p><p>Seaborn其有如下特点:</p><ul><li>基于matplotlib aesthetics绘图风格, 增加了一些绘图模式;</li><li>增加调色板功能, 利用色彩丰富的图像揭示您数据中的模式;</li><li>运用数据子集绘制与比较单变量和双变量分布的功能;</li><li>运用聚类算法可视化矩阵数据;</li><li>灵活运用处理时间序列数据;</li><li>利用网格建立复杂图像集.</li></ul><h2 id="Seaborn绘制散点图"><a href="#Seaborn绘制散点图" class="headerlink" title="Seaborn绘制散点图"></a>Seaborn绘制散点图</h2><blockquote><p><a href="http://seaborn.pydata.org/generated/seaborn.scatterplot.html#seaborn.scatterplot">seaborn.scatterplot(x&#x3D;None, y&#x3D;None, hue&#x3D;None, style&#x3D;None, size&#x3D;None, data&#x3D;None, palette&#x3D;None, hue_order&#x3D;None, hue_norm&#x3D;None, sizes&#x3D;None, size_order&#x3D;None, size_norm&#x3D;None, markers&#x3D;True, style_order&#x3D;None, alpha&#x3D;’auto’, legend&#x3D;’brief’, ax&#x3D;None, **kwargs)</a></p></blockquote><p>参数:</p><ul><li>x, y (names of variables in data or vector data, optional): 输入的x, y 轴变量, 必须是数字. 可以直接传递数据, 或者引用<code>data</code>中的列.</li><li>hue(name of variables in data or vector data, optional): 将输入的变量分组, 并分别给与不同的颜色, 可以是文字分类列也可以是数字列, 这两者的结果会有所不同. </li><li>size(name of variables in data or vector data, optional): 将输入的变量分组, 并分别给与不同的大小, 可以是文字分类列也可以是数字列, 这两者的结果会有所不同. </li><li>style(name of variables in data or vector data, optional): 将输入的变量分组, 并分别给与不同的标志, 可以是文字分类列也可以是数字列, 但是数字列始终是被认为是分类标志. </li><li>data(DataFrame): 整齐数据框, 其中每一列都是变量, 每一行都是观察值.</li><li>palette (palette name, list, or dict, optional): 调色板, 要应用于hue不同级别的色板, 具体使用可以参考<a href="https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.scatter.html#matplotlib.axes.Axes.scatter">color_palette()</a></li><li>hue_order(list, optional): hue不同级别色彩出现的详细顺序, 不指定则由数据确定, 当hue变量为数字时不相关.</li><li>hue_norm(tuple or Normalize object, optional): 当hue变量为数值时, 使其归一化以上色. </li><li>sizes(list, dict, or tuple, optional): 当size已经给定了, 决定散点大小可选的list, 或者针对不同的size值给定不同的大小(dict), 当size值为数字时, 同样可以用元组(tuple)决定散点大小的范围.</li><li>size_order(list, optional): 散点大小不同级别的详细顺序. 不指定则由数据确定, 当size变量为数字时不相关.</li><li>size_norm(tuple or Normalize object, optional): 当size变量为数值时, 使其归一化以上色.</li><li>markers(boolean, list, or dictionary, optional): 散点标志, 设置为True则使用默认设置, 或者你可以给定一个marker的list以供选择, 你也可以根据style参数对不同的类别给定不同的marker(dict).</li><li>style_order(list, optional): marker不同级别出现的详细顺序, 不指定则由数据确定, 当style变量为数字时不相关.</li><li>alpha(float): 散点的透明度.</li><li>legend(“brief”, “full”, 或者 False, optional): 如何绘制图例, 如果是”brief”,  则数字的hue和size变量将以均匀的间隔表示(并不会把所有类别的图例都表示出来); 如果是”full”, 则每个类别都会在图例中表示出来; 如果是 False , 则不会绘制图例.</li><li>ax(matplotlib Axes, optional): 在哪个axes上绘制该图, 不填写则使用当前axes.</li><li>kwargs(key, value mappings): 其他关键字参数传递给 <a href="https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.scatter.html#matplotlib.axes.Axes.scatter">matplotlib.axes.Axes.scatter()</a>.</li></ul><p>这边举几个例子:</p><p>这里我使用的是seaborn的官方数据集, 因为国内网络问题, 无法在线加载数据集, 我是下载后使用的, <a href="https://github.com/mwaskom/seaborn-data">数据集地址</a></p><blockquote><p>绘制x为total_bill, y为tip的散点图</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"></span><br><span class="line">tips = pd.read_csv(<span class="string">&quot;seaborn-data/tips.csv&quot;</span>)</span><br><span class="line">tips</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果:</span></span><br><span class="line"><span class="string">total_billtipsexsmokerdaytimesize</span></span><br><span class="line"><span class="string">016.991.01FemaleNoSunDinner2</span></span><br><span class="line"><span class="string">110.341.66MaleNoSunDinner3</span></span><br><span class="line"><span class="string">221.013.50MaleNoSunDinner3</span></span><br><span class="line"><span class="string">323.683.31MaleNoSunDinner2</span></span><br><span class="line"><span class="string">424.593.61FemaleNoSunDinner4</span></span><br><span class="line"><span class="string">........................</span></span><br><span class="line"><span class="string">23929.035.92MaleNoSatDinner3</span></span><br><span class="line"><span class="string">24027.182.00FemaleYesSatDinner2</span></span><br><span class="line"><span class="string">24122.672.00MaleYesSatDinner2</span></span><br><span class="line"><span class="string">24217.821.75MaleNoSatDinner2</span></span><br><span class="line"><span class="string">24318.783.00FemaleNoThurDinner2</span></span><br><span class="line"><span class="string">244 rows × 7 columns</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">sns.scatterplot(x=<span class="string">&quot;total_bill&quot;</span>, y=<span class="string">&quot;tip&quot;</span>, data=tips)</span><br></pre></td></tr></table></figure><p>绘制出来的图形如下:</p><p><img src="/assets/blogImg/seaborn/seaborn01.svg" alt="seaborn01"></p><blockquote><p>按照不同的性别绘制不同的颜色:</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sns.scatterplot(x=<span class="string">&quot;total_bill&quot;</span>, y=<span class="string">&quot;tip&quot;</span>, data=tips, hue=<span class="string">&#x27;sex&#x27;</span>)</span><br></pre></td></tr></table></figure><p>绘制出来的图形如下:</p><p><img src="/assets/blogImg/seaborn/seaborn02.svg" alt="seaborn02"></p><blockquote><p>按照不同的size绘制不同的颜色:</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sns.scatterplot(x=<span class="string">&quot;total_bill&quot;</span>, y=<span class="string">&quot;tip&quot;</span>, data=tips, hue=<span class="string">&#x27;sex&#x27;</span>, size=<span class="string">&#x27;size&#x27;</span>, legend=<span class="string">&#x27;full&#x27;</span>)</span><br></pre></td></tr></table></figure><p>绘制出来的图形如下:</p><p><img src="/assets/blogImg/seaborn/seaborn03.svg" alt="seaborn03"></p><blockquote><p>按照是否抽烟绘制不同的marker:</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sns.scatterplot(x=<span class="string">&quot;total_bill&quot;</span>, y=<span class="string">&quot;tip&quot;</span>, data=tips, hue=<span class="string">&#x27;sex&#x27;</span>, size=<span class="string">&#x27;size&#x27;</span>, style=<span class="string">&#x27;smoker&#x27;</span>)</span><br></pre></td></tr></table></figure><p>绘制出来的图形如下:</p><p><img src="/assets/blogImg/seaborn/seaborn04.svg" alt="seaborn04"></p><h2 id="Seaborn绘制折线图"><a href="#Seaborn绘制折线图" class="headerlink" title="Seaborn绘制折线图"></a>Seaborn绘制折线图</h2><blockquote><p>seaborn.lineplot(x&#x3D;None, y&#x3D;None, hue&#x3D;None, size&#x3D;None, style&#x3D;None, data&#x3D;None, palette&#x3D;None, hue_order&#x3D;None, hue_norm&#x3D;None, sizes&#x3D;None, size_order&#x3D;None, size_norm&#x3D;None, dashes&#x3D;True, markers&#x3D;None, style_order&#x3D;None, units&#x3D;None, estimator&#x3D;’mean’, ci&#x3D;95, n_boot&#x3D;1000, seed&#x3D;None, sort&#x3D;True, err_style&#x3D;’band’, err_kws&#x3D;None, legend&#x3D;’brief’, ax&#x3D;None, **kwargs)</p></blockquote><p>参数基本和散点图一样, 但是还是多了一些可用的参数:</p><ul><li>dashes(boolean, list, or dictionary, optional): 确定为不同级别的style绘制什么样的marker. 设置为True将使用默认的dash codes, 或者你可以传递dash codes列表或style变量到dash codes的字典映射. 设置为False将对所有子集使用实线.</li><li>units(long_form_var): 分组变量标识采样单位. 使用时, 将使用适当的方式为每个unit绘制一条单独的线, 但不会添加图例. 当不需要精确的线条身份时, 可用于显示实验重复的分布.</li><li>estimator (name of pandas method or callable or None, optional): 在同一x级别上跨y变量的多个观测值进行聚合的方法. 如果为None, 将绘制所有观测值. </li><li>ci(int or “sd” or None, optional): 与估计量进行汇总时要绘制的置信区间的大小. “sd”表示绘制数据的标准差. 设置为None将跳过引导程序.</li><li>n_boot(int, optional): 用于计算置信区间的引导程序数.</li><li>seed(int, numpy.random.Generator, or numpy.random.RandomState, optional): 种子或随机数生成器, 可重现自举.</li><li>sort(boolean, optional): 如果为True, 则数据将按x和y变量排序, 否则线条将按照它们在数据集中出现的顺序出现.</li><li>err_style(“band” or “bars”, optional): 是否绘制具有半透明误差带或离散误差线的置信区间.</li><li>err_kws(dict of keyword arguments): 其他控制误差线外观的参数, 传递给matplotlib.axes.Axes.fill_between() 或者 matplotlib.axes.Axes.errorbar(), 取决于err_style.</li></ul><p>举几个例子:</p><p>对于复杂的数据集, 如对相同的x变量值进行多次测量, seaborn中的默认行为是通过绘制平均值和围绕平均值的95%置信区间来聚合每个x值处的多个测量值.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"></span><br><span class="line">fmri = pd.read_csv(<span class="string">&quot;seaborn-data/fmri.csv&quot;</span>)</span><br><span class="line">fmri</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果:</span></span><br><span class="line"><span class="string">subjecttimepointeventregionsignal</span></span><br><span class="line"><span class="string">0s1318stimparietal-0.017552</span></span><br><span class="line"><span class="string">1s514stimparietal-0.080883</span></span><br><span class="line"><span class="string">2s1218stimparietal-0.081033</span></span><br><span class="line"><span class="string">3s1118stimparietal-0.046134</span></span><br><span class="line"><span class="string">4s1018stimparietal-0.037970</span></span><br><span class="line"><span class="string">..................</span></span><br><span class="line"><span class="string">1059s08cuefrontal0.018165</span></span><br><span class="line"><span class="string">1060s137cuefrontal-0.029130</span></span><br><span class="line"><span class="string">1061s127cuefrontal-0.004939</span></span><br><span class="line"><span class="string">1062s117cuefrontal-0.025367</span></span><br><span class="line"><span class="string">1063s00cueparietal-0.006899</span></span><br><span class="line"><span class="string">1064 rows × 5 columns</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">sns.lineplot(x=<span class="string">&quot;timepoint&quot;</span>, y=<span class="string">&quot;signal&quot;</span>, data=fmri)</span><br></pre></td></tr></table></figure><p>绘制出来的图形如下:</p><p><img src="/assets/blogImg/seaborn/seaborn05.svg" alt="seaborn05"></p><p>使用自动计算置信区间, 对于较大的数据集, 可能会占用大量时间, 因此可以设置<code>ci=None</code>来禁用它.</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><em>[1] <a href="http://seaborn.pydata.org/tutorial.html">Official seaborn tutorial - seaborn 0.10.1 documentation</a></em><br><em>[2] <a href="https://zhuanlan.zhihu.com/p/24464836">10分钟python图表绘制 | seaborn入门（一）：distplot与kdeplot - 知乎</a></em></p>]]></content>
    
    
    <summary type="html">简单介绍如何使用Python的Seaborn库绘图.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="seaborn" scheme="https://zzz5.github.io/tags/seaborn/"/>
    
  </entry>
  
  <entry>
    <title>Django博客开发</title>
    <link href="https://zzz5.github.io/2020/07/21/web/django/django-blog/"/>
    <id>https://zzz5.github.io/2020/07/21/web/django/django-blog/</id>
    <published>2020-07-21T04:28:00.000Z</published>
    <updated>2020-07-29T08:23:36.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="说在前面"><a href="#说在前面" class="headerlink" title="说在前面"></a>说在前面</h2><p>这里重点介绍Django后端, 前端我用的是bootstrap5前端组件库, 水平不够就不进行过多介绍了.</p><h2 id="开发环境搭建"><a href="#开发环境搭建" class="headerlink" title="开发环境搭建"></a>开发环境搭建</h2><ul><li>使用的python版本为Python3.8;</li><li>使用的django版本为Django3.0;</li><li>使用的编辑器为vscode.</li></ul><h2 id="项目需求分析"><a href="#项目需求分析" class="headerlink" title="项目需求分析"></a>项目需求分析</h2><p>这里我要搭建的是一个普通的blog, 需要的是blog的基础功能, 包括:</p><ul><li>导航栏</li><li>横幅幻灯片</li><li>文章列表</li><li>文章分类</li><li>文章标签</li><li>文章内容</li><li>文章浏览量</li><li>文章搜索</li><li>热门排行</li></ul><p>其实也没多少东西, 包含的页面主要有:</p><ol><li>博客首页</li><li>文章分类页</li><li>文章标签页</li><li>文章搜索页</li><li>文章内容页</li></ol><h2 id="数据库设计"><a href="#数据库设计" class="headerlink" title="数据库设计"></a>数据库设计</h2><h3 id="文章表结构"><a href="#文章表结构" class="headerlink" title="文章表结构"></a>文章表结构</h3><table><thead><tr><th>表字段</th><th>字段类型</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>int</td><td>系统自动生成的主键</td></tr><tr><td>title</td><td>CharField(max_length&#x3D;128)</td><td>文章标题</td></tr><tr><td>category</td><td>ForeignKey</td><td>多对一, 关联文章分类表</td></tr><tr><td>tags</td><td>ManyToManyField</td><td>多对多, 关联标签列表</td></tr><tr><td>cover</td><td>ImageField</td><td>文章封面</td></tr><tr><td>views</td><td>PositiveIntegerField</td><td>文章浏览数</td></tr><tr><td>abstract</td><td>TextField(max_length&#x3D;250)</td><td>文章摘要</td></tr><tr><td>content</td><td>TextField</td><td>文章内容</td></tr><tr><td>created_time</td><td>DateTimeField</td><td>文章创建时间</td></tr><tr><td>modified_time</td><td>DateTimeField</td><td>文章修改时间</td></tr></tbody></table><h3 id="幻灯片表结构"><a href="#幻灯片表结构" class="headerlink" title="幻灯片表结构"></a>幻灯片表结构</h3><table><thead><tr><th>表字段</th><th>字段类型</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>int</td><td>系统自动生成的主键</td></tr><tr><td>text_info</td><td>CharField(max_length&#x3D;100)</td><td>图片文本信息</td></tr><tr><td>img</td><td>ImageField</td><td>幻灯片图片</td></tr><tr><td>link_url</td><td>URLField</td><td>图片链接的URL</td></tr><tr><td>index</td><td>int</td><td>图片播放位序</td></tr><tr><td>is_active</td><td>BooleanField</td><td>是否播放</td></tr></tbody></table><h3 id="文章分类表"><a href="#文章分类表" class="headerlink" title="文章分类表"></a>文章分类表</h3><table><thead><tr><th>表字段</th><th>字段类型</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>int</td><td>系统自动生成的主键</td></tr><tr><td>name</td><td>CharField(max_length&#x3D;30)</td><td>类别名</td></tr><tr><td>index</td><td>index</td><td>类别次序</td></tr></tbody></table><h3 id="文章标签表"><a href="#文章标签表" class="headerlink" title="文章标签表"></a>文章标签表</h3><table><thead><tr><th>表字段</th><th>字段类型</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>int</td><td>系统自动生成的主键</td></tr><tr><td>name</td><td>CharField(max_length&#x3D;30)</td><td>标签名</td></tr></tbody></table><h2 id="项目创建以及配置"><a href="#项目创建以及配置" class="headerlink" title="项目创建以及配置"></a>项目创建以及配置</h2><h3 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h3><p>打开命令行, cd 到一个你想放置你代码的目录, 然后运行以下命令:<br><code>django-admin startproject myblog</code><br>这行代码将会在当前目录下创建一个 myblog 目录.<br>进入 myblog 目录,输入命令<br><code>python manage.py runserver</code><br>即可在  <a href="https://127.0.0.1:8000/">https://127.0.0.1:8000/</a> 中看到欢迎界面<br>创建应用时使用命令<br><code>python manage.py startapp blog</code><br>这将会创建一个名为blog的目录, 即为应用.</p><p>其它常用命令还有:<br><code>python manage.py makemigrations</code><br>Django 会检测你对模型文件的修改, 并且把修改的部分储存为一次迁移.<br>再使用命令<br><code>python manage.py migrate</code><br>进行迁移.</p><p><code>python manage.py shell</code><br>使用该命令可以在<code>shell</code>界面对django项目进行简单调试.</p><h3 id="myblog-settings-py配置"><a href="#myblog-settings-py配置" class="headerlink" title="myblog&#x2F;settings.py配置"></a>myblog&#x2F;settings.py配置</h3><blockquote><p>设置语言和时区:</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#settings</span></span><br><span class="line">LANGUAGE_CODE = <span class="string">&#x27;zh-hans&#x27;</span>  <span class="comment"># 修改语言为中文</span></span><br><span class="line">TIME_ZONE = <span class="string">&#x27;Asia/Shanghai&#x27;</span>  <span class="comment"># 修改时区为上海</span></span><br><span class="line">USE_TZ = <span class="literal">True</span>  <span class="comment"># 建议设置为True, 这样后端全局都会使用UTC时间, 但是在模板中渲染时会自动转换为当前时区 </span></span><br></pre></td></tr></table></figure><blockquote><p>在INSTALL_APPS中添加应用: </p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">INSTALLED_APPS = [</span><br><span class="line">    <span class="string">&#x27;blog.apps.BlogConfig&#x27;</span>,<span class="comment">#注册APP应用</span></span><br><span class="line">    ...</span><br><span class="line">]</span><br></pre></td></tr></table></figure><blockquote><p>配置数据库：</p></blockquote><p>这里我用的是mysql数据库(默认是使用sqlite3), 需要安装 MySQLdb 才能使用.<br><code>pip install MySQLdb</code><br>并在<code>blog/__init__.py</code>中添加 import MySQLdb.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改成mysql如下</span></span><br><span class="line">DATABASES = &#123;</span><br><span class="line">    <span class="string">&#x27;default&#x27;</span>: &#123;</span><br><span class="line">        <span class="string">&#x27;ENGINE&#x27;</span>: <span class="string">&#x27;django.db.backends.mysql&#x27;</span>, </span><br><span class="line">        <span class="string">&#x27;NAME&#x27;</span>: <span class="string">&#x27;myblog&#x27;</span>,  <span class="comment">#  你的数据库名称</span></span><br><span class="line">        <span class="string">&#x27;USER&#x27;</span>: <span class="string">&#x27;name&#x27;</span>,  <span class="comment">#  你的数据库用户名</span></span><br><span class="line">        <span class="string">&#x27;PASSWORD&#x27;</span>: <span class="string">&#x27;password&#x27;</span>,  <span class="comment">#  你的数据库密码</span></span><br><span class="line">        <span class="string">&#x27;HOST&#x27;</span>: <span class="string">&#x27;localhost&#x27;</span>,  <span class="comment">#  你的数据库主机，留空默认为localhost</span></span><br><span class="line">        <span class="string">&#x27;PORT&#x27;</span>: <span class="string">&#x27;3306&#x27;</span>,  <span class="comment">#  你的数据库端口</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意要提前建好schema.    </p><blockquote><p>设置静态文件夹目录的路径:</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置静态文件目录</span></span><br><span class="line">STATIC_URL = <span class="string">&#x27;/static/&#x27;</span></span><br><span class="line">STATICFILES_DIRS = (</span><br><span class="line">    os.path.join(BASE_DIR, <span class="string">&quot;static&quot;</span>),</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置媒体文件上传目录</span></span><br><span class="line">MEDIA_URL = <span class="string">&#x27;/media/&#x27;</span>  </span><br><span class="line">MEDIA_ROOT = os.path.join(BASE_DIR, <span class="string">&#x27;media&#x27;</span>)    <span class="comment"># </span></span><br></pre></td></tr></table></figure><h2 id="Django-ORM创建"><a href="#Django-ORM创建" class="headerlink" title="Django ORM创建"></a>Django ORM创建</h2><p>Django是通过Model操作数据库, Model准确且唯一的描述了数据. 它包含你储存的数据的重要字段和行为. 一般来说, 每一个模型都映射一张数据库表.</p><ul><li>每个模型都是一个 Python 的类, 这些类继承 django.db.models.Model.</li><li>模型类的每个属性都相当于一个数据库的字段.</li><li>利用这些, Django 提供了一个自动生成访问数据库的 API.</li></ul><p>根据我们数据库的设计, 可以很快写出这个Model(文章内容我是使用富文本保存的, 但是django并没有集成富文本, 这里使用的是django-ckeditor富文本编辑器, 可以使用<code>pip install django-cheditor</code>自行安装, 注意在INSTALL_APPS中注册<code>ckeditor</code>, 具体使用方法可以查看 <a href="https://www.jianshu.com/p/a14970431762">Django搭建个人博客：使用django-ckeditor富文本编辑器</a>):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># myblog/blog/models.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"><span class="keyword">from</span> ckeditor.fields <span class="keyword">import</span> RichTextField</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Category</span>(models.Model):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;分类表&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    name = models.CharField(max_length=<span class="number">100</span>, unique=<span class="literal">True</span>)</span><br><span class="line">    index = models.IntegerField(default=<span class="number">999</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line">        <span class="comment"># 排序方式按照index排序</span></span><br><span class="line">        ordering = [<span class="string">&quot;index&quot;</span>]</span><br><span class="line">        verbose_name = <span class="string">&#x27;category&#x27;</span></span><br><span class="line">        verbose_name_plural = <span class="string">&#x27;categorys&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Tag</span>(models.Model):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;标签表&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    name = models.CharField(max_length=<span class="number">100</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line">        verbose_name = <span class="string">&#x27;tag&#x27;</span></span><br><span class="line">        verbose_name_plural = <span class="string">&#x27;tags&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Article</span>(models.Model):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文章表&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    title = models.CharField(max_length=<span class="number">128</span>)</span><br><span class="line">    <span class="comment"># 使用ckeditor的富文本保存.</span></span><br><span class="line">    content = RichTextField(blank=<span class="literal">True</span>, null=<span class="literal">True</span>)</span><br><span class="line">    abstract = models.TextField(max_length=<span class="number">500</span>, blank=<span class="literal">True</span>, null=<span class="literal">True</span>)</span><br><span class="line">    cover = models.ImageField(upload_to=<span class="string">&#x27;article_cover/%Y/%m/%d/&#x27;</span>,</span><br><span class="line">                              verbose_name=<span class="string">&#x27;article_cover&#x27;</span>, blank=<span class="literal">True</span>, null=<span class="literal">True</span>)</span><br><span class="line">    category = models.ForeignKey(</span><br><span class="line">        Category, on_delete=models.DO_NOTHING, blank=<span class="literal">True</span>, null=<span class="literal">True</span>)</span><br><span class="line">    tags = models.ManyToManyField(Tag, blank=<span class="literal">True</span>, null=<span class="literal">True</span>)</span><br><span class="line">    views = models.PositiveIntegerField(default=<span class="number">0</span>)</span><br><span class="line">    created_time = models.DateTimeField(auto_now_add=<span class="literal">True</span>)</span><br><span class="line">    modified_time = models.DateTimeField(auto_now=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line">        ordering = [<span class="string">&quot;-created_time&quot;</span>]</span><br><span class="line">        verbose_name = <span class="string">&#x27;article&#x27;</span></span><br><span class="line">        verbose_name_plural = <span class="string">&#x27;articles&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.title</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Banner</span>(models.Model):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;幻灯片表&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    text_info = models.CharField(max_length=<span class="number">50</span>, default=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    img = models.ImageField(upload_to=<span class="string">&#x27;banner/&#x27;</span>)</span><br><span class="line">    link_url = models.URLField(max_length=<span class="number">100</span>)</span><br><span class="line">    is_active = models.BooleanField(default=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.text_info</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line">        verbose_name = <span class="string">&#x27;banner&#x27;</span></span><br><span class="line">        verbose_name_plural = <span class="string">&#x27;banners&#x27;</span></span><br></pre></td></tr></table></figure><p>各个参数我就不一一赘述了, 有需要的可以在<a href="https://docs.djangoproject.com/zh-hans/3.0/topics/db/models/">官方文档</a>查询.</p><p>ORM创建后, 要执行命令<br><code>python manage.py makemigrations</code><br><code>python manage.py migrate</code><br>进行检测和迁移.</p><h2 id="Admin管理界面设计"><a href="#Admin管理界面设计" class="headerlink" title="Admin管理界面设计"></a>Admin管理界面设计</h2><p>先要创建一个管理员账号<code>python manage.py createsuperuser</code>按照指示注册账号密码.<br>启动服务器后, 打开浏览器, 转到你本地域名的 “&#x2F;admin&#x2F;“ 目录, – 比如 “<a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a>“ .你应该会看见管理员登录界面.</p><p>需要在你的应用的<code>admin.py</code>文件中注册.<br>打开<code>blog/admin.py</code>, 键入以下代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Article, Banner, Category, Tag</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TagAdmin</span>(admin.ModelAdmin):</span><br><span class="line">    fields = [</span><br><span class="line">        <span class="string">&#x27;name&#x27;</span></span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CategoryAdmin</span>(admin.ModelAdmin):</span><br><span class="line">    fields = [</span><br><span class="line">        <span class="string">&#x27;name&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;index&#x27;</span></span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ArticleAdmin</span>(admin.ModelAdmin):</span><br><span class="line">    <span class="comment"># 编辑时分为多个字段集</span></span><br><span class="line">    fieldsets = [</span><br><span class="line">        (<span class="string">&#x27;文章标题&#x27;</span>, &#123;<span class="string">&#x27;fields&#x27;</span>: [<span class="string">&#x27;title&#x27;</span>]&#125;),</span><br><span class="line">        (<span class="string">&#x27;封面&#x27;</span>, &#123;<span class="string">&#x27;fields&#x27;</span>: [<span class="string">&#x27;cover&#x27;</span>]&#125;),</span><br><span class="line">        (<span class="string">&#x27;文章内容&#x27;</span>, &#123;<span class="string">&#x27;fields&#x27;</span>: [<span class="string">&#x27;abstract&#x27;</span>, <span class="string">&#x27;content&#x27;</span>]&#125;),</span><br><span class="line">        (<span class="string">&#x27;阅读量&#x27;</span>, &#123;<span class="string">&#x27;fields&#x27;</span>: [<span class="string">&#x27;views&#x27;</span>], <span class="string">&#x27;classes&#x27;</span>: [<span class="string">&#x27;collapse&#x27;</span>]&#125;),</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 文章列表里显示想要显示的字段</span></span><br><span class="line">    list_display = (<span class="string">&#x27;id&#x27;</span>, <span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;category&#x27;</span>, <span class="string">&#x27;views&#x27;</span>, <span class="string">&#x27;created_time&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 满50条数据就自动分页</span></span><br><span class="line">    list_per_page = <span class="number">50</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 后台数据列表排序方式</span></span><br><span class="line">    ordering = (<span class="string">&#x27;-created_time&#x27;</span>,)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 设置哪些字段可以点击进入编辑界面</span></span><br><span class="line">    list_display_links = (<span class="string">&#x27;id&#x27;</span>, <span class="string">&#x27;title&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 设置后台查询时用的字段</span></span><br><span class="line">    search_fields = [<span class="string">&#x27;title&#x27;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 用来过滤字段的过滤器</span></span><br><span class="line">    list_filter = [<span class="string">&#x27;created_time&#x27;</span>, <span class="string">&#x27;modified_time&#x27;</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ArticleInline</span>(admin.StackedInline):</span><br><span class="line">    model = Article</span><br><span class="line">    extra = <span class="number">2</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">BannerAdmin</span>(admin.ModelAdmin):</span><br><span class="line">    <span class="comment"># 编辑时的排列顺序</span></span><br><span class="line">    fields = [</span><br><span class="line">        <span class="string">&#x27;text_info&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;img&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;link_url&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;is_active&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;index&#x27;</span></span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">admin.site.register(Article, ArticleAdmin)</span><br><span class="line">admin.site.register(Category, CategoryAdmin)</span><br><span class="line">admin.site.register(Tag, TagAdmin)</span><br><span class="line">admin.site.register(Banner, BannerAdmin)</span><br></pre></td></tr></table></figure><p>此时后台站点管理的界面是这样的:</p><p><img src="/assets/blogImg/django/django01.png" alt="django01"></p><p>添加文章界面是这样的:</p><p><img src="/assets/blogImg/django/django02.png" alt="django02"></p><h2 id="URL设计"><a href="#URL设计" class="headerlink" title="URL设计"></a>URL设计</h2><p>先打开<code>myblog/urls.py</code>, 键入以下代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path, re_path, include</span><br><span class="line"><span class="keyword">from</span> django.conf <span class="keyword">import</span> settings</span><br><span class="line"><span class="keyword">from</span> django.views <span class="keyword">import</span> static</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    <span class="comment"># 后台站点管理的url</span></span><br><span class="line">    path(<span class="string">&#x27;admin/&#x27;</span>, admin.site.urls),</span><br><span class="line">    <span class="comment"># 获取储存图片的url</span></span><br><span class="line">    re_path(<span class="string">r&#x27;^media/(?P&lt;path&gt;.*)$&#x27;</span>, static.serve,</span><br><span class="line">            &#123;<span class="string">&#x27;document_root&#x27;</span>: settings.MEDIA_ROOT&#125;),</span><br><span class="line">    <span class="comment"># blog应用的url</span></span><br><span class="line">    path(<span class="string">&#x27;&#x27;</span>, include(<span class="string">&#x27;blog.urls&#x27;</span>))</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>再在blog应用文件夹中创建urls.py, 键入以下代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># blog/urls.py</span></span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">app_name = <span class="string">&#x27;blog&#x27;</span></span><br><span class="line">urlpatterns = [</span><br><span class="line">    <span class="comment"># 首页的url</span></span><br><span class="line">    path(<span class="string">&#x27;&#x27;</span>, views.index, name=<span class="string">&#x27;index&#x27;</span>),</span><br><span class="line">    <span class="comment"># 文章内容的url, 并将文章id传给views</span></span><br><span class="line">    path(<span class="string">&#x27;articles/&lt;int:id&gt;/&#x27;</span>, views.article, name=<span class="string">&#x27;articles),</span></span><br><span class="line"><span class="string">    # 同分类文章列表的url, 并将category传给views</span></span><br><span class="line"><span class="string">    path(&#x27;</span>categories/&lt;<span class="built_in">str</span>:category&gt;/<span class="string">&#x27;, views.category, name=&#x27;</span>categories<span class="string">&#x27;),</span></span><br><span class="line"><span class="string">    # 同标签文章列表的url, 并将tag传给views</span></span><br><span class="line"><span class="string">    path(&#x27;</span>tags/&lt;<span class="built_in">str</span>:tag&gt;/<span class="string">&#x27;, views.tag, name=&#x27;</span>tags<span class="string">&#x27;),</span></span><br><span class="line"><span class="string">    # 搜索文章的url</span></span><br><span class="line"><span class="string">    path(&#x27;</span>search/<span class="string">&#x27;, views.search, name=&#x27;</span>search<span class="string">&#x27;),</span></span><br><span class="line"><span class="string">]</span></span><br></pre></td></tr></table></figure><h2 id="Views视图函数"><a href="#Views视图函数" class="headerlink" title="Views视图函数"></a>Views视图函数</h2><p>打开<code>blog/views.py</code>, 键入以下代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"><span class="keyword">from</span> django.core.paginator <span class="keyword">import</span> Paginator</span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Article, Banner, Category, Tag</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取全部页面都有的一些参数, 包括所有分类, 标签和热门文章</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getGlobalParameter</span>():</span><br><span class="line">    categories = Category.objects.<span class="built_in">all</span>()</span><br><span class="line">    tags = Tag.objects.<span class="built_in">all</span>()</span><br><span class="line">    hot_articles = Article.objects.order_by(<span class="string">&#x27;-views&#x27;</span>)[:<span class="number">5</span>]</span><br><span class="line">    result = &#123;<span class="string">&#x27;categories&#x27;</span>: categories, <span class="string">&#x27;category_link&#x27;</span>: <span class="string">&#x27;/categories/&#x27;</span>,</span><br><span class="line">              <span class="string">&#x27;tags&#x27;</span>: tags, <span class="string">&#x27;tag_link&#x27;</span>: <span class="string">&#x27;/tags/&#x27;</span>,</span><br><span class="line">              <span class="string">&#x27;hot_articles&#x27;</span>: hot_articles, <span class="string">&#x27;article_link&#x27;</span>: <span class="string">&#x27;/articles/&#x27;</span>&#125;</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取给定文章的基本信息</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getArticlesInfo</span>(<span class="params">articles, has_content=<span class="literal">False</span></span>):</span><br><span class="line">    articles_info = []</span><br><span class="line">    <span class="comment"># 所有文章的除内容外的信息</span></span><br><span class="line">    <span class="keyword">for</span> article <span class="keyword">in</span> articles:</span><br><span class="line">        info = &#123;<span class="string">&#x27;id&#x27;</span>: article.<span class="built_in">id</span>, <span class="string">&#x27;title&#x27;</span>: article.title, <span class="string">&#x27;cover&#x27;</span>: article.cover,</span><br><span class="line">                <span class="string">&#x27;abstract&#x27;</span>: article.abstract, <span class="string">&#x27;views&#x27;</span>: article.views,</span><br><span class="line">                <span class="string">&#x27;category&#x27;</span>: article.category, <span class="string">&#x27;tags&#x27;</span>: article.tags.<span class="built_in">all</span>(),</span><br><span class="line">                <span class="string">&#x27;created_time&#x27;</span>: article.created_time,</span><br><span class="line">                <span class="string">&#x27;link&#x27;</span>: <span class="string">&#x27;/articles/&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(article.<span class="built_in">id</span>)</span><br><span class="line">                &#125;</span><br><span class="line">        <span class="keyword">if</span> has_content:</span><br><span class="line">            info.update(&#123;<span class="string">&#x27;content&#x27;</span>: article.content&#125;)</span><br><span class="line">        articles_info.append(info)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> articles_info</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取给定文章的分页信息和当前页面的文章</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getPageInfoAndArticles</span>(<span class="params">articles, current_page=<span class="number">1</span>, pages_num=<span class="number">5</span></span>):</span><br><span class="line">    paginator = Paginator(articles, pages_num)</span><br><span class="line">    <span class="keyword">if</span> current_page &lt; <span class="number">0</span> <span class="keyword">or</span> current_page &gt; paginator.num_pages:</span><br><span class="line">        current_page = <span class="number">1</span></span><br><span class="line">    current_articles = paginator.page(current_page)</span><br><span class="line">    page_info = &#123;<span class="string">&#x27;pages&#x27;</span>: paginator.page_range, <span class="string">&#x27;current&#x27;</span>: current_page,</span><br><span class="line">                 <span class="string">&#x27;has_next&#x27;</span>: current_articles.has_next(), <span class="string">&#x27;has_previous&#x27;</span>: current_articles.has_previous&#125;</span><br><span class="line">    <span class="keyword">return</span> page_info, current_articles</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 首页</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>(<span class="params">request</span>):</span><br><span class="line">    <span class="comment"># 获取当前请求的页面</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        current_page = <span class="built_in">int</span>(request.GET.get(</span><br><span class="line">            <span class="string">&quot;page&quot;</span>)) <span class="keyword">if</span> request.GET.get(<span class="string">&quot;page&quot;</span>) <span class="keyword">else</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        current_page = <span class="number">1</span></span><br><span class="line">    <span class="comment"># 获取页面信息和当前页面的文章</span></span><br><span class="line">    page_info, articles = getPageInfoAndArticles(</span><br><span class="line">        Article.objects.<span class="built_in">all</span>(), current_page=current_page, pages_num=<span class="number">5</span>)</span><br><span class="line">    <span class="comment"># 获取幻灯片</span></span><br><span class="line">    banners = Banner.objects.<span class="built_in">filter</span>(is_active=<span class="literal">True</span>)</span><br><span class="line">    <span class="comment"># 将所有内容传递给模板</span></span><br><span class="line">    content = &#123;<span class="string">&#x27;page_info&#x27;</span>: page_info,</span><br><span class="line">               <span class="string">&#x27;banners&#x27;</span>: banners, <span class="string">&#x27;articles&#x27;</span>: getArticlesInfo(articles)&#125;</span><br><span class="line">    content.update(getGlobalParameter())</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/index.html&#x27;</span>, content)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文章内容页</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">article</span>(<span class="params">request, <span class="built_in">id</span></span>):</span><br><span class="line">    article = Article.objects.get(<span class="built_in">id</span>=<span class="built_in">id</span>)</span><br><span class="line">    <span class="comment"># 更新该文章阅读数</span></span><br><span class="line">    article.views += <span class="number">1</span></span><br><span class="line">    article.save()</span><br><span class="line">    <span class="comment"># 获取包括内容在内的文章信息, 并将文章信息加入content传递给模板</span></span><br><span class="line">    content = &#123;<span class="string">&#x27;article&#x27;</span>: getArticlesInfo([article], has_content=<span class="literal">True</span>)[<span class="number">0</span>]&#125;</span><br><span class="line">    content.update(getGlobalParameter())</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/article.html&#x27;</span>, content)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">category</span>(<span class="params">request, category</span>):</span><br><span class="line">    <span class="comment"># 获取当前请求的页面</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        current_page = <span class="built_in">int</span>(request.GET.get(</span><br><span class="line">            <span class="string">&quot;page&quot;</span>)) <span class="keyword">if</span> request.GET.get(<span class="string">&quot;page&quot;</span>) <span class="keyword">else</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        current_page = <span class="number">1</span></span><br><span class="line">    <span class="comment"># 获取页面信息和当前页面的同类文章</span></span><br><span class="line">    page_info, articles = getPageInfoAndArticles(</span><br><span class="line">        Article.objects.<span class="built_in">filter</span>(category__name=category), current_page=current_page, pages_num=<span class="number">5</span>)</span><br><span class="line">    content = &#123;<span class="string">&#x27;page_info&#x27;</span>: page_info, <span class="string">&#x27;articles&#x27;</span>: getArticlesInfo(articles)&#125;</span><br><span class="line">    content.update(getGlobalParameter())</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/category.html&#x27;</span>, content)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">tag</span>(<span class="params">request, tag</span>):</span><br><span class="line">    <span class="comment"># 获取当前请求的页面</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        current_page = <span class="built_in">int</span>(request.GET.get(</span><br><span class="line">            <span class="string">&quot;page&quot;</span>)) <span class="keyword">if</span> request.GET.get(<span class="string">&quot;page&quot;</span>) <span class="keyword">else</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        current_page = <span class="number">1</span></span><br><span class="line">    <span class="comment"># 获取页面信息和当前页面的同类文章</span></span><br><span class="line">    page_info, articles = getPageInfoAndArticles(</span><br><span class="line">        Article.objects.<span class="built_in">filter</span>(tags__name=tag), current_page=current_page, pages_num=<span class="number">5</span>)</span><br><span class="line">    content = &#123;<span class="string">&#x27;page_info&#x27;</span>: page_info, <span class="string">&#x27;articles&#x27;</span>: getArticlesInfo(articles)&#125;</span><br><span class="line">    content.update(getGlobalParameter())</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/tag.html&#x27;</span>, content)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">search</span>(<span class="params">request</span>):</span><br><span class="line">    q = request.GET.get(<span class="string">&#x27;q&#x27;</span>)  <span class="comment"># 获取搜索的关键词q</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        current_page = <span class="built_in">int</span>(request.GET.get(</span><br><span class="line">            <span class="string">&quot;page&quot;</span>)) <span class="keyword">if</span> request.GET.get(<span class="string">&quot;page&quot;</span>) <span class="keyword">else</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        current_page = <span class="number">1</span></span><br><span class="line">    content = &#123;&#125;</span><br><span class="line">    <span class="keyword">if</span> q != <span class="literal">None</span>:</span><br><span class="line">        <span class="comment"># 获取到搜索关键词通过标题进行匹配</span></span><br><span class="line">        page_info, articles = getPageInfoAndArticles(</span><br><span class="line">            Article.objects.<span class="built_in">filter</span>(title__icontains=q), current_page=current_page, pages_num=<span class="number">5</span>)</span><br><span class="line">        content = &#123;<span class="string">&#x27;q&#x27;</span>: q,</span><br><span class="line">                   <span class="string">&#x27;page_info&#x27;</span>: page_info,</span><br><span class="line">                   <span class="string">&#x27;articles&#x27;</span>: getArticlesInfo(articles)&#125;</span><br><span class="line">    content.update(getGlobalParameter())</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/search.html&#x27;</span>, content)</span><br></pre></td></tr></table></figure><h2 id="模板搭建"><a href="#模板搭建" class="headerlink" title="模板搭建"></a>模板搭建</h2><p>前端模板我是使用的bootstrap前端框架, 技术不够成熟, 只能做个简单演示.</p><p><img src="/assets/blogImg/django/django03.png" alt="django03"></p><p>将各个功能分成多个HTML, 最终将其组合起来, 这样有利于修改.<br>最终首页HTML的实现就非常简单了:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#123;% extends &quot;blog\includes\base.html&quot; %&#125;</span><br><span class="line"></span><br><span class="line">&#123;% block banner %&#125;</span><br><span class="line">&#123;% include &quot;blog\includes\banner.html&quot; %&#125;</span><br><span class="line">&#123;% endblock banner %&#125;</span><br><span class="line"></span><br><span class="line">&#123;% block article_list %&#125;</span><br><span class="line">&#123;% include &quot;blog\includes\article_list.html&quot; %&#125;</span><br><span class="line">&#123;% endblock article_list%&#125;</span><br><span class="line"></span><br><span class="line">&#123;% block pagination %&#125;</span><br><span class="line">&#123;% include &quot;blog\includes\pagination.html&quot; %&#125;</span><br><span class="line">&#123;% endblock pagination %&#125;</span><br></pre></td></tr></table></figure><p>其中所有页面都有的界面, 如分类, 热门文章, 标签, 页脚, 都放在了<code>base.html</code>, 其他所有页面都继承自<code>base.html</code>, 并在其界面基础上进行修改, 比如搜索界面, 就加入了搜索框, 并重写了pagination(分页)界面.</p><h2 id="各个页面的实现"><a href="#各个页面的实现" class="headerlink" title="各个页面的实现"></a>各个页面的实现</h2><p>最终各个页面的成果也出来了:</p><p>首页:</p><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://i.loli.net/2020/07/24/DbG9EdNuwcrBMfJ.png","alt":"首页大屏页面","title":""},{"url":"https://i.loli.net/2020/07/25/w2RAtaMZoXxridg.png","alt":"首页小屏页面","title":""}]</div>  </div><p>分类页面:</p><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://i.loli.net/2020/07/25/aQu1xNJfmwcl7tO.png","alt":"分类大屏页面","title":""},{"url":"https://i.loli.net/2020/07/25/RpAsDeI8wB2FzS5.png","alt":"分类小屏页面","title":""}]</div>  </div><p>标签页面:</p><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://i.loli.net/2020/07/25/j5qJkmdcY8AyvfR.png","alt":"标签大屏界面","title":""},{"url":"https://i.loli.net/2020/07/25/auGJwUzNcjOvPlt.png","alt":"标签小屏界面","title":""}]</div>  </div><p>文章页面:</p><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://i.loli.net/2020/07/25/osezSb28gXvkJRd.png","alt":"文章大屏界面","title":""},{"url":"https://i.loli.net/2020/07/25/vFIx6cR2eGNoprZ.png","alt":"文章小屏界面","title":""}]</div>  </div><p>搜索页面:</p><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://i.loli.net/2020/07/25/PDRTGqlAHxmCJ1M.png","alt":"搜索大屏界面.png","title":""},{"url":"https://i.loli.net/2020/07/25/DyoxB8FMpLncYw1.png","alt":"搜索小屏界面.png","title":""}]</div>  </div><h2 id="最后再说两句"><a href="#最后再说两句" class="headerlink" title="最后再说两句"></a>最后再说两句</h2><p>该博客我已上传到 github , 有需要可以自取 <a href="https://github.com/zzZ5/myblog">myblog</a>.<br>关于很多细节方便我没过多说明, 参考文献中的三个文档都有详细说明, 可以在那里面找到.</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>[1] <a href="https://docs.djangoproject.com/zh-hans/3.0/">Django3.0官方文档</a></em><br><em>[2] <a href="https://www.liujiangblog.com/course/django/">Django Django教程 Django视频 Django实战 Django开发 - 刘江的Django教程</a></em><br><em>[3] <a href="https://www.django.cn/">Django中文网,Django中国,Django中文社区,django教程,Django！</a></em></p>]]></content>
    
    
    <summary type="html">简单介绍我在Django建站学习过程中遇到的坑和解决方法.</summary>
    
    
    
    <category term="web" scheme="https://zzz5.github.io/categories/web/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="django" scheme="https://zzz5.github.io/tags/django/"/>
    
  </entry>
  
  <entry>
    <title>Python的Pandas库中的行列转换</title>
    <link href="https://zzz5.github.io/2020/07/12/python/pandas/python-pandas-03/"/>
    <id>https://zzz5.github.io/2020/07/12/python/pandas/python-pandas-03/</id>
    <published>2020-07-12T01:47:00.000Z</published>
    <updated>2020-07-12T05:17:54.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="stack-和unstack"><a href="#stack-和unstack" class="headerlink" title="stack()和unstack()"></a>stack()和unstack()</h2><p>pandas行转列, 列转行, 以及一行生成多行中, 我们主要要用到 stack() 和 unstack() 操作.</p><blockquote><p><a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.stack.html">DataFrame.stack(self, level&#x3D;- 1)</a><br>stack是将DataFram变为Series, DataFrame的列变成具有多级索引Series的最后一级索引, DataFrame的索引变成具有多级索引Series的其他索引.</p></blockquote><blockquote><p><a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.unstack.html">DataFrame.unstack(self, level&#x3D;- 1) &#x2F; Series.unstack(self, level&#x3D;- 1)</a><br>对于Series来说, unstack()是把最后一级索引变成对应DataFrame的列, 其他索引变成对应DataFrame的索引; 对于DataFrame来说, unstack()是将DataFram变为Series, DataFrame的列变成具有多级索引Series的一级索引, DataFrame的索引变成具有多级索引Series的其他索引.</p></blockquote><p>例如: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">df0 = pd.DataFrame([[<span class="number">80</span>, <span class="number">85</span>], [<span class="number">92</span>, <span class="number">90</span>], [<span class="number">88</span>, <span class="number">78</span>]], index=[<span class="string">&#x27;小红&#x27;</span>, <span class="string">&#x27;小华&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>], columns=[<span class="string">&#x27;数学&#x27;</span>, <span class="string">&#x27;语文&#x27;</span>])</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df0:</span></span><br><span class="line"><span class="string">数学语文</span></span><br><span class="line"><span class="string">小红8085</span></span><br><span class="line"><span class="string">小华9290</span></span><br><span class="line"><span class="string">小明8878</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># =========================分割线=========================</span></span><br><span class="line"></span><br><span class="line">se1 = df0.unstack(level=-<span class="number">1</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;se1:</span></span><br><span class="line"><span class="string">数学    小红    80</span></span><br><span class="line"><span class="string">        小华    92</span></span><br><span class="line"><span class="string">        小明    88</span></span><br><span class="line"><span class="string">语文    小红    85</span></span><br><span class="line"><span class="string">        小华    90</span></span><br><span class="line"><span class="string">        小明    78</span></span><br><span class="line"><span class="string">dtype: int64</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 可以看到df1的列学科变成了se1的一级索引, 索引则会变成se1的其他索引.</span></span><br><span class="line"></span><br><span class="line">df1 = se1.unstack(level=-<span class="number">1</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df1</span></span><br><span class="line"><span class="string">    小红小华小明</span></span><br><span class="line"><span class="string">数学809288</span></span><br><span class="line"><span class="string">语文859078</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 可以看到se1的最后一级索引姓名变成了df1的列, 其它索引则会变成df1的索引.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># =========================分割线=========================</span></span><br><span class="line"></span><br><span class="line">se2 = df0.stack(level=-<span class="number">1</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;se2:</span></span><br><span class="line"><span class="string">小红    数学    80</span></span><br><span class="line"><span class="string">        语文    85</span></span><br><span class="line"><span class="string">小华    数学    92</span></span><br><span class="line"><span class="string">        语文    90</span></span><br><span class="line"><span class="string">小明    数学    88</span></span><br><span class="line"><span class="string">        语文    78</span></span><br><span class="line"><span class="string">dtype: int64</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 可以看到df1的列索引分数变成了se1的最后一级索引, 其它索引则会变成se1的索引.</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">df2 = se2.unstack(level=-<span class="number">1</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df2:</span></span><br><span class="line"><span class="string">    数学语文</span></span><br><span class="line"><span class="string">小红8085</span></span><br><span class="line"><span class="string">小华9290</span></span><br><span class="line"><span class="string">小明8878</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 可以看到se2的最后一级索引学科变成了df2的列, 其它索引则会变成df2的索引.</span></span><br></pre></td></tr></table></figure><h2 id="pivot"><a href="#pivot" class="headerlink" title="pivot()"></a>pivot()</h2><blockquote><p><a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pivot.html">DataFrame.pivot(self, index&#x3D;None, columns&#x3D;None, values&#x3D;None)</a><br>用来调整DataFrame的索引, 列, 值.</p></blockquote><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">df = pd.DataFrame(&#123; <span class="string">&#x27;姓名&#x27;</span>:[<span class="string">&#x27;小红&#x27;</span>, <span class="string">&#x27;小红&#x27;</span>, <span class="string">&#x27;小华&#x27;</span>, <span class="string">&#x27;小华&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>],</span><br><span class="line">                    <span class="string">&#x27;科目&#x27;</span>:[<span class="string">&#x27;数学&#x27;</span>, <span class="string">&#x27;语文&#x27;</span>, <span class="string">&#x27;数学&#x27;</span>, <span class="string">&#x27;语文&#x27;</span>, <span class="string">&#x27;数学&#x27;</span>, <span class="string">&#x27;语文&#x27;</span>],</span><br><span class="line">                    <span class="string">&#x27;成绩&#x27;</span>:[<span class="number">88</span>, <span class="number">85</span>, <span class="number">92</span>, <span class="number">90</span>, <span class="number">88</span>, <span class="number">78</span>] &#125;)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">姓名科目成绩</span></span><br><span class="line"><span class="string">0小红数学88</span></span><br><span class="line"><span class="string">1小红语文85</span></span><br><span class="line"><span class="string">2小华数学92</span></span><br><span class="line"><span class="string">3小华语文90</span></span><br><span class="line"><span class="string">4小明数学88</span></span><br><span class="line"><span class="string">5小明语文78</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">df = df.pivot(index=<span class="string">&#x27;姓名&#x27;</span>, columns)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">科目数学语文</span></span><br><span class="line"><span class="string">姓名</span></span><br><span class="line"><span class="string">小华9290</span></span><br><span class="line"><span class="string">小明8878</span></span><br><span class="line"><span class="string">小红8885</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将列名置空, rename_axis()方法是给坐标轴重命名</span></span><br><span class="line">df = df.rename_axis(columns=<span class="literal">None</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">    数学语文</span></span><br><span class="line"><span class="string">姓名</span></span><br><span class="line"><span class="string">小华9290</span></span><br><span class="line"><span class="string">小明8878</span></span><br><span class="line"><span class="string">小红8885</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重新设置索引, 若index变为空的话，那么在reset_index之后，列名会变成index</span></span><br><span class="line">df = df.reset_index()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">姓名数学语文</span></span><br><span class="line"><span class="string">0小华9290</span></span><br><span class="line"><span class="string">1小明8878</span></span><br><span class="line"><span class="string">2小红8885</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="分割value问题"><a href="#分割value问题" class="headerlink" title="分割value问题"></a>分割value问题</h2><p>有这样一组数据:</p><table><thead><tr><th>姓名</th><th>年龄</th><th>爱好</th></tr></thead><tbody><tr><td>小红</td><td>16</td><td>跳舞,唱歌,钢琴</td></tr><tr><td>小华</td><td>18</td><td>唱,跳,rap,篮球</td></tr><tr><td>小明</td><td>20</td><td>古筝,翻译</td></tr></tbody></table><p>我们想要把它变成下面这样:</p><table><thead><tr><th>姓名</th><th>年龄</th><th>爱好</th></tr></thead><tbody><tr><td>小红</td><td>16</td><td>跳舞</td></tr><tr><td>小红</td><td>16</td><td>唱歌</td></tr><tr><td>小红</td><td>16</td><td>钢琴</td></tr><tr><td>小华</td><td>18</td><td>唱</td></tr><tr><td>小华</td><td>18</td><td>跳</td></tr><tr><td>小华</td><td>18</td><td>rap</td></tr><tr><td>小华</td><td>18</td><td>篮球</td></tr><tr><td>小明</td><td>20</td><td>古筝</td></tr><tr><td>小明</td><td>20</td><td>翻译</td></tr></tbody></table><p>先导入数据:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">df = pd.DataFrame(&#123; <span class="string">&#x27;姓名&#x27;</span>:[<span class="string">&#x27;小红&#x27;</span>, <span class="string">&#x27;小华&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>],</span><br><span class="line">                    <span class="string">&#x27;年龄&#x27;</span>:[<span class="number">16</span>, <span class="number">18</span>, <span class="number">20</span>],</span><br><span class="line">                    <span class="string">&#x27;爱好&#x27;</span>:[<span class="string">&#x27;跳舞,唱歌,钢琴&#x27;</span>, <span class="string">&#x27;唱,跳,rap,篮球&#x27;</span>, <span class="string">&#x27;古筝,翻译&#x27;</span>] &#125;)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">姓名年龄爱好</span></span><br><span class="line"><span class="string">0小红16跳舞,唱歌,钢琴</span></span><br><span class="line"><span class="string">1小华18唱,跳,rap,篮球</span></span><br><span class="line"><span class="string">2小明20古筝,翻译</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="第一种方法"><a href="#第一种方法" class="headerlink" title="第一种方法"></a>第一种方法</h3><p>首先将df的姓名和年龄设置为索引(当前为列名):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">df = df.set_index([<span class="string">&quot;姓名&quot;</span>, <span class="string">&quot;年龄&quot;</span>])</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">            爱好</span></span><br><span class="line"><span class="string">姓名年龄</span></span><br><span class="line"><span class="string">小红16跳舞,唱歌,钢琴</span></span><br><span class="line"><span class="string">小华18唱,跳,rap,篮球</span></span><br><span class="line"><span class="string">小明20古筝,翻译</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>此时df只有爱好这一列, 再将爱好这列分割:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">df = df[<span class="string">&quot;爱好&quot;</span>].<span class="built_in">str</span>.split(<span class="string">&quot;,&quot;</span>, expand=<span class="literal">True</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">            0123</span></span><br><span class="line"><span class="string">姓名年龄</span></span><br><span class="line"><span class="string">小红16跳舞唱歌钢琴None</span></span><br><span class="line"><span class="string">小华18唱跳rap篮球</span></span><br><span class="line"><span class="string">小明20古筝翻译NoneNone</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>此时字段已经被分割开来, 我们需要将这多列变为一列, 此时我们可以调用 stack(), 将列转变为Series的最后一级索引:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">se = df.stack()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;se:</span></span><br><span class="line"><span class="string">姓名  年龄   </span></span><br><span class="line"><span class="string">小红  16  0     跳舞</span></span><br><span class="line"><span class="string">        1     唱歌</span></span><br><span class="line"><span class="string">        2     钢琴</span></span><br><span class="line"><span class="string">小华  18  0      唱</span></span><br><span class="line"><span class="string">        1      跳</span></span><br><span class="line"><span class="string">        2    rap</span></span><br><span class="line"><span class="string">        3     篮球</span></span><br><span class="line"><span class="string">小明  20  0     古筝</span></span><br><span class="line"><span class="string">        1     翻译</span></span><br><span class="line"><span class="string">dtype: object</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>此时这个df已经转变为Series, 而此Series的最后一级索引一级没有了作用, 可以直接删掉:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">se = se.reset_index(drop=<span class="literal">True</span>, level=-<span class="number">1</span>)</span><br><span class="line"><span class="comment"># 这个reset_index因为指定了删除level=-1, 所以只是删除了该Series的最后一级索引, 并没有将该Series转变为DataFrame</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;se:</span></span><br><span class="line"><span class="string">姓名  年龄</span></span><br><span class="line"><span class="string">小红  16     跳舞</span></span><br><span class="line"><span class="string">    16     唱歌</span></span><br><span class="line"><span class="string">    16     钢琴</span></span><br><span class="line"><span class="string">小华  18      唱</span></span><br><span class="line"><span class="string">    18      跳</span></span><br><span class="line"><span class="string">    18    rap</span></span><br><span class="line"><span class="string">    18     篮球</span></span><br><span class="line"><span class="string">小明  20     古筝</span></span><br><span class="line"><span class="string">    20     翻译</span></span><br><span class="line"><span class="string">dtype: object</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>我们再调用一次reset_index(), 将Series的索引转变为DataFrame的列:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">df = se.reset_index()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">姓名年龄0</span></span><br><span class="line"><span class="string">0小红16跳舞</span></span><br><span class="line"><span class="string">1小红16唱歌</span></span><br><span class="line"><span class="string">2小红16钢琴</span></span><br><span class="line"><span class="string">3小华18唱</span></span><br><span class="line"><span class="string">4小华18跳</span></span><br><span class="line"><span class="string">5小华18rap</span></span><br><span class="line"><span class="string">6小华18篮球</span></span><br><span class="line"><span class="string">7小明20古筝</span></span><br><span class="line"><span class="string">8小明20翻译</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>最后再更改一下列名就完成了:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">df = df.rename(columns=&#123;<span class="number">0</span>: <span class="string">&quot;爱好&quot;</span>&#125;)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;python</span></span><br><span class="line"><span class="string">姓名年龄爱好</span></span><br><span class="line"><span class="string">0小红16跳舞</span></span><br><span class="line"><span class="string">1小红16唱歌</span></span><br><span class="line"><span class="string">2小红16钢琴</span></span><br><span class="line"><span class="string">3小华18唱</span></span><br><span class="line"><span class="string">4小华18跳</span></span><br><span class="line"><span class="string">5小华18rap</span></span><br><span class="line"><span class="string">6小华18篮球</span></span><br><span class="line"><span class="string">7小明20古筝</span></span><br><span class="line"><span class="string">8小明20翻译</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="第二种方法"><a href="#第二种方法" class="headerlink" title="第二种方法"></a>第二种方法</h3><p>使用下面方法, 可以迅速使列表炸裂开.</p><blockquote><p><a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.explode.html">DataFrame.explode(self, column)</a></p></blockquote><p>首先我们将数据的”爱好”列转换为列表</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">df[<span class="string">&quot;爱好&quot;</span>] = df[<span class="string">&quot;爱好&quot;</span>].<span class="built_in">str</span>.split(<span class="string">&quot;,&quot;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">    姓名年龄爱好</span></span><br><span class="line"><span class="string">0小红16[跳舞, 唱歌, 钢琴]</span></span><br><span class="line"><span class="string">1小华18[唱, 跳, rap, 篮球]</span></span><br><span class="line"><span class="string">2小明20[古筝, 翻译]</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>再使用该方法, 并指定”爱好”列, 就完成了:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">df = df.explode(<span class="string">&quot;爱好&quot;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;df:</span></span><br><span class="line"><span class="string">姓名年龄爱好</span></span><br><span class="line"><span class="string">0小红16跳舞</span></span><br><span class="line"><span class="string">0小红16唱歌</span></span><br><span class="line"><span class="string">0小红16钢琴</span></span><br><span class="line"><span class="string">1小华18唱</span></span><br><span class="line"><span class="string">1小华18跳</span></span><br><span class="line"><span class="string">1小华18rap</span></span><br><span class="line"><span class="string">1小华18篮球</span></span><br><span class="line"><span class="string">2小明20古筝</span></span><br><span class="line"><span class="string">2小明20翻译</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>[1] <a href="https://www.cnblogs.com/traditional/p/11967360.html">pandas行转列、列转行、以及一行生成多行 - 古明地盆 - 博客园</a></em></p>]]></content>
    
    
    <summary type="html">pandas行转列, 列转行, 以及一行生成多行.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="pandas" scheme="https://zzz5.github.io/tags/pandas/"/>
    
  </entry>
  
  <entry>
    <title>Python设计模式(2)</title>
    <link href="https://zzz5.github.io/2020/07/10/python/others/python-designPatterns-02/"/>
    <id>https://zzz5.github.io/2020/07/10/python/others/python-designPatterns-02/</id>
    <published>2020-07-10T06:14:00.000Z</published>
    <updated>2020-07-15T04:45:16.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="策略模式-Strategy-Pattern"><a href="#策略模式-Strategy-Pattern" class="headerlink" title="策略模式(Strategy Pattern)"></a>策略模式(Strategy Pattern)</h2><h3 id="什么是策略模式"><a href="#什么是策略模式" class="headerlink" title="什么是策略模式"></a>什么是策略模式</h3><p>策略模式定义了算法族, 分别封装起来, 让它们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户.<br>将内部实现委托给了行为类, 自身并不了解是如何实现的.</p><blockquote><p><strong>tips</strong>: 多用组合, 少用继承</p></blockquote><h3 id="python实现策略模式"><a href="#python实现策略模式" class="headerlink" title="python实现策略模式"></a>python实现策略模式</h3><p>假设我们有一个订单, 平常是原价, 但是到了节假日有多种打折方式, 我们想要用一个<code>price_after_discount()</code>方法解决所有打折方式.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, price, discount_strategy=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="comment"># 建立订单时必须设定价格, 可以选择折扣方式, 若不选择则为原价</span></span><br><span class="line">        <span class="variable language_">self</span>.price = price</span><br><span class="line">        <span class="variable language_">self</span>.discount_strategy = discount_strategy</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">set_strategy</span>(<span class="params">self, discount_strategy</span>):</span><br><span class="line">        <span class="comment"># 临时更改折扣方式</span></span><br><span class="line">        <span class="variable language_">self</span>.discount_strategy = discount_strategy</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">price_after_discount</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 计算价格, 若无折扣, 则按原价, 若有折扣则减去折扣</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.discount_strategy:</span><br><span class="line">            discount = <span class="variable language_">self</span>.discount_strategy(<span class="variable language_">self</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            discount = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 若折扣超过原价, 则返回0</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.price - discount <span class="keyword">if</span> <span class="variable language_">self</span>.price &gt; discount <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 格式化tostring()方法</span></span><br><span class="line">        fmt = <span class="string">&quot;&lt;Price: &#123;&#125;, price after discount: &#123;&#125;&gt;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> fmt.<span class="built_in">format</span>(<span class="variable language_">self</span>.price, <span class="variable language_">self</span>.price_after_discount())</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">ten_percent_discount</span>(<span class="params">order</span>):</span><br><span class="line">    <span class="comment"># 打九折, 即减去原价的10%</span></span><br><span class="line">    <span class="keyword">return</span> order.price * <span class="number">0.10</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">on_sale_discount</span>(<span class="params">order</span>):</span><br><span class="line">    <span class="comment"># 打75折再加20的优惠券</span></span><br><span class="line">    <span class="keyword">return</span> order.price * <span class="number">0.25</span> + <span class="number">20</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(Order(<span class="number">100</span>))</span><br><span class="line"><span class="comment"># 输出结果: &lt;Price: 100, price after discount: 100&gt;</span></span><br><span class="line"><span class="built_in">print</span>(Order(<span class="number">100</span>, discount_strategy=ten_percent_discount))</span><br><span class="line"><span class="comment"># 输出结果: &lt;Price: 100, price after discount: 90.0&gt;</span></span><br><span class="line"><span class="built_in">print</span>(Order(<span class="number">100</span>, discount_strategy=on_sale_discount))</span><br><span class="line"><span class="comment"># 输出结果: &lt;Price: 100, price after discount: 55.0&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>tips</strong>: 针对接口编程, 而不是针对实现编程.</p></blockquote><h2 id="观察者模式-Observer-Pattern"><a href="#观察者模式-Observer-Pattern" class="headerlink" title="观察者模式(Observer Pattern)"></a>观察者模式(Observer Pattern)</h2><h3 id="什么是观察者模式"><a href="#什么是观察者模式" class="headerlink" title="什么是观察者模式"></a>什么是观察者模式</h3><p>观察者模式–在对象之间定义一对多的依赖, 这样一来, 当一个对象改变状态, 依赖它的对象都会收到通知并自动更新.</p><h3 id="python实现观察者模式"><a href="#python实现观察者模式" class="headerlink" title="python实现观察者模式"></a>python实现观察者模式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Observable</span>:</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    Observable类追踪所有的观察者并通知他们</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 初始化时创建观察者列表</span></span><br><span class="line">        <span class="variable language_">self</span>._observers = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register_observer</span>(<span class="params">self, observer</span>):</span><br><span class="line">        <span class="comment"># 注册观察者到需要通知的观察者列表</span></span><br><span class="line">        <span class="keyword">if</span> observer <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._observers:</span><br><span class="line">            <span class="variable language_">self</span>._observers.append(observer)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">remove_observer</span>(<span class="params">self, observer</span>):</span><br><span class="line">        <span class="comment"># 将观察者移除需要通知的观察者列表</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="variable language_">self</span>._observers.remove(observer)</span><br><span class="line">        <span class="keyword">except</span> ValueError:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">notify_observers</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 数据发生变化时通知所有观察者</span></span><br><span class="line">        <span class="keyword">for</span> observer <span class="keyword">in</span> <span class="variable language_">self</span>._observers:</span><br><span class="line">            observer.update(<span class="variable language_">self</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Data</span>(<span class="title class_ inherited__">Observable</span>):</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    Data类继承自Observable, 不同类型的消息可以使用同样的方法通知</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 初始化时设置数据为&#x27;&#x27;</span></span><br><span class="line">        Observable.__init__(<span class="variable language_">self</span>)</span><br><span class="line">        <span class="variable language_">self</span>._data = <span class="string">&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">data</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># get 信息</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._data</span><br><span class="line"></span><br><span class="line"><span class="meta">    @data.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">data</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="comment"># set 信息</span></span><br><span class="line">        <span class="variable language_">self</span>._data = value</span><br><span class="line">        <span class="comment"># 设置信息后调用修改信息方法以通知各个观察者.</span></span><br><span class="line">        <span class="variable language_">self</span>.notify_observers()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Observer</span>():</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    观察者父类</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name=<span class="string">&#x27;&#x27;</span></span>):</span><br><span class="line">        <span class="comment"># 初始化时为每个观察者单独设置一个名字</span></span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, Observable</span>):</span><br><span class="line">        <span class="comment"># 当消息更新时调用该方法</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrdinaryObserver</span>(<span class="title class_ inherited__">Observer</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, Observable</span>):</span><br><span class="line">        <span class="comment"># 为各类观察者重写不同的update()方法</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;Observer &#123;&#125; has data &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">            <span class="variable language_">self</span>.name, Observable.data))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">VipObserver</span>(<span class="title class_ inherited__">Observer</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, Observable</span>):</span><br><span class="line">        <span class="comment"># 为各类观察者重写不同的update()方法</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;Distinguished observer &#123;&#125; has data &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">            <span class="variable language_">self</span>.name, Observable.data))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建两个信息类data1和data2, 并将观察者1, 2都注册到data1和data2.</span></span><br><span class="line">data1 = Data()</span><br><span class="line">data2 = Data()</span><br><span class="line">observer1 = OrdinaryObserver(<span class="string">&#x27;A&#x27;</span>)</span><br><span class="line">observer2 = VipObserver(<span class="string">&#x27;B&#x27;</span>)</span><br><span class="line">data1.register_observer(observer1)</span><br><span class="line">data1.register_observer(observer2)</span><br><span class="line">data2.register_observer(observer1)</span><br><span class="line">data2.register_observer(observer2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">data1.data = <span class="string">&quot;Big news&quot;</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Observer A has data Big news</span></span><br><span class="line"><span class="string">Distinguished observer B has data Big news</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">data2.data = <span class="string">&quot;Missing person notice&quot;</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Observer A has data Missing person notice</span></span><br><span class="line"><span class="string">Distinguished observer B has data Missing person notice</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">data1.data = <span class="string">&quot;Latest news&quot;</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Observer A has data Latest news</span></span><br><span class="line"><span class="string">Distinguished observer B has data Latest news</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">data2.data = <span class="string">&quot;Lawyer&#x27;s letter&quot;</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Observer A has data Lawyer&#x27;s letter</span></span><br><span class="line"><span class="string">Distinguished observer B has data Lawyer&#x27;s letter</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># data1去除观察者1, data2去除观察者2, 再对信息进行修改</span></span><br><span class="line">data1.remove_observer(observer1)</span><br><span class="line">data2.remove_observer(observer2)</span><br><span class="line">data1.data = <span class="string">&quot;Live news&quot;</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Distinguished observer B has data Live news</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">data2.data = <span class="string">&quot;Wanted order&quot;</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Observer A has data Wanted order</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="装饰者模式-Decorator-Pattern"><a href="#装饰者模式-Decorator-Pattern" class="headerlink" title="装饰者模式(Decorator Pattern)"></a>装饰者模式(Decorator Pattern)</h2><h3 id="什么是装饰者模式"><a href="#什么是装饰者模式" class="headerlink" title="什么是装饰者模式"></a>什么是装饰者模式</h3><p>装饰者模式–动态地将责任附加到对象上. 想要扩展功能, 装饰着提供有别于继承的另一种选择.</p><blockquote><p><strong>tips</strong>: 类应该对扩展开放, 对修改关闭</p></blockquote><h3 id="python实现装饰者模式"><a href="#python实现装饰者模式" class="headerlink" title="python实现装饰者模式"></a>python实现装饰者模式</h3><p>python自带了一个装饰器功能, 能够方便实现装饰者模式.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">time_it</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="comment"># 创建一个时间装饰器, 用于计算函数运行的时间.</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">call_func</span>():</span><br><span class="line">        start_time = time.time()</span><br><span class="line">        func()</span><br><span class="line">        stop_time = time.time()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;运行时间是&#123;&#125;&quot;</span>.<span class="built_in">format</span>(stop_time-start_time))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> call_func</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 用@方法的方式使用装饰器</span></span><br><span class="line"><span class="meta">@time_it</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;----test----&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">test()</span><br><span class="line"><span class="comment"># 输出结果为: 运行时间是0.0322401523590087</span></span><br></pre></td></tr></table></figure><p>也可以按照自己的需求编写装饰器:</p><p>比如在店里买饮料, 需要选择是否添加牛奶, 摩卡等, 这时就可以使用装饰者模式.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Beverage</span>():</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    饮料父类</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._description = <span class="string">&quot;Unknown beverage&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._cost = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 获取饮料的描述</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._description</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 获取饮料的价格</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._cost</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Condiment</span>(<span class="title class_ inherited__">Beverage</span>):</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    调料父类, 继承自Beverage并不是为了继承行为, 而是为了类型匹配</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_description</span>():</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_cost</span>():</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DarkRoast</span>(<span class="title class_ inherited__">Beverage</span>):</span><br><span class="line">    <span class="comment"># 深焙咖啡类, 并初始化描述和价格</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._description = <span class="string">&quot;DarkRoast&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._cost = <span class="number">12.5</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Espresso</span>(<span class="title class_ inherited__">Beverage</span>):</span><br><span class="line">    <span class="comment"># 浓缩咖啡类, 并初始化描述和价格</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._description = <span class="string">&quot;Espresso&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._cost = <span class="number">13.8</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Mocha</span>(<span class="title class_ inherited__">Condiment</span>):</span><br><span class="line">    <span class="comment"># 配料摩卡, 并初始化描述和价格</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, beverage</span>):</span><br><span class="line">        <span class="variable language_">self</span>.beverage = beverage</span><br><span class="line">        <span class="variable language_">self</span>._cost = <span class="number">2.5</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.beverage.get_description() + <span class="string">&quot;, Mocha&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">round</span>(<span class="variable language_">self</span>.beverage.get_cost() + <span class="variable language_">self</span>._cost, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Whip</span>(<span class="title class_ inherited__">Condiment</span>):</span><br><span class="line">    <span class="comment"># 配料奶泡, 并初始化描述和价格</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, beverage</span>):</span><br><span class="line">        <span class="variable language_">self</span>.beverage = beverage</span><br><span class="line">        <span class="variable language_">self</span>._cost = <span class="number">1.4</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.beverage.get_description() + <span class="string">&quot;, whip&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">round</span>(<span class="variable language_">self</span>.beverage.get_cost() + <span class="variable language_">self</span>._cost, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建一个深焙咖啡, 并添加摩卡和奶泡</span></span><br><span class="line">beverage1 = DarkRoast()</span><br><span class="line">beverage1 = Mocha(beverage1)</span><br><span class="line">beverage1 = Whip(beverage1)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建一个浓缩咖啡, 并添加两次奶泡</span></span><br><span class="line">beverage2 = Espresso()</span><br><span class="line">beverage2 = Whip(beverage2)</span><br><span class="line">beverage2 = Whip(beverage2)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(beverage1.get_description(), <span class="string">&quot; ￥&quot;</span>, beverage1.get_cost())</span><br><span class="line"><span class="comment"># 输出结果: DarkRoast, Mocha, whip  ￥ 16.4</span></span><br><span class="line"><span class="built_in">print</span>(beverage2.get_description(), <span class="string">&quot; ￥&quot;</span>, beverage2.get_cost())</span><br><span class="line"><span class="comment"># 输出结果: Espresso, whip, whip  ￥ 16.6</span></span><br></pre></td></tr></table></figure><h2 id="工厂模式-Factory-Pattern-和-抽象工厂模式-Abstract-Factory-Pattern"><a href="#工厂模式-Factory-Pattern-和-抽象工厂模式-Abstract-Factory-Pattern" class="headerlink" title="工厂模式(Factory Pattern) 和 抽象工厂模式(Abstract Factory Pattern)"></a>工厂模式(Factory Pattern) 和 抽象工厂模式(Abstract Factory Pattern)</h2><h3 id="什么是工厂模式和抽象工厂模式"><a href="#什么是工厂模式和抽象工厂模式" class="headerlink" title="什么是工厂模式和抽象工厂模式"></a>什么是工厂模式和抽象工厂模式</h3><p>工厂模式–定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个. 工厂方法让类把实例化推迟到子类.<br>抽象工厂模式–提供一个接口, 用于创建相关或依赖对象的家族, 而不需要明确指定具体类.</p><blockquote><p><strong>tips</strong>: 要依赖抽象类而不依赖具体类</p></blockquote><h3 id="python实现工厂模式"><a href="#python实现工厂模式" class="headerlink" title="python实现工厂模式"></a>python实现工厂模式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ChineseLocalizer</span>:</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;简单为几个单词中文本地化&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.translations = &#123;<span class="string">&quot;dog&quot;</span>: <span class="string">&quot;狗&quot;</span>, <span class="string">&quot;cat&quot;</span>: <span class="string">&quot;猫&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">localize</span>(<span class="params">self, msg</span>):</span><br><span class="line">        <span class="string">&#x27;&#x27;&#x27;如果没有该单词的翻译则返回原词&#x27;&#x27;&#x27;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.translations.get(msg, msg)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EnglishLocalizer</span>:</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;简单重复该单词&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">localize</span>(<span class="params">self, msg</span>):</span><br><span class="line">        <span class="keyword">return</span> msg</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_localizer</span>(<span class="params">language=<span class="string">&quot;English&quot;</span></span>):</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;语言工厂&#x27;&#x27;&#x27;</span></span><br><span class="line">    localizers = &#123;</span><br><span class="line">        <span class="string">&quot;English&quot;</span>: EnglishLocalizer,</span><br><span class="line">        <span class="string">&quot;Chinese&quot;</span>: ChineseLocalizer,</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> localizers[language]()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用工厂模式创建本地化类</span></span><br><span class="line">eng = get_localizer(language=<span class="string">&quot;English&quot;</span>)</span><br><span class="line">ch = get_localizer(language=<span class="string">&quot;Chinese&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(eng.localize(<span class="string">&#x27;dog&#x27;</span>), ch.localize(<span class="string">&#x27;dog&#x27;</span>))</span><br><span class="line"><span class="comment"># 输出结果: dog 狗</span></span><br><span class="line"><span class="built_in">print</span>(eng.localize(<span class="string">&#x27;cat&#x27;</span>), ch.localize(<span class="string">&#x27;cat&#x27;</span>))</span><br><span class="line"><span class="comment"># 输出结果: cat 猫</span></span><br><span class="line"><span class="built_in">print</span>(eng.localize(<span class="string">&#x27;pig&#x27;</span>), ch.localize(<span class="string">&#x27;pig&#x27;</span>))</span><br><span class="line"><span class="comment"># 输出结果: pig pig</span></span><br></pre></td></tr></table></figure><h3 id="python实现抽象工厂模式"><a href="#python实现抽象工厂模式" class="headerlink" title="python实现抽象工厂模式"></a>python实现抽象工厂模式</h3><p>在Java和其他语言中, 抽象工厂模式用于提供用于创建相关或依赖对象的接口, 而无需指定它们的实际类. 其思想是根据业务逻辑, 平台选择等决定抽象对象的创建.<br>在Python中, 我们使用的接口只是一个Callable对象, 这是Python中的”内置”接口, 在正常情况下, 我们可以简单地将类本身用作该Callable对象, 因为类在Python中是第一类对象.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PetShop</span>:</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;一个宠物商店&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, animal_factory=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="comment">#宠物工厂只是一个抽象工厂, 我们可以在未来使其实例化</span></span><br><span class="line">        <span class="variable language_">self</span>.pet_factory = animal_factory</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">show_pet</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 使用抽象工厂创建和展示宠物</span></span><br><span class="line">        pet = <span class="variable language_">self</span>.pet_factory()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;We have a lovely &#123;&#125;, it says &#123;&#125;.&quot;</span>.<span class="built_in">format</span>(pet, pet.speak()))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>:</span><br><span class="line">    <span class="comment"># Dog工厂</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;woof&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Dog&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cat</span>:</span><br><span class="line">    <span class="comment"># Cat工厂</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;meow&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Cat&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 额外的工厂:</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">random_animal</span>():</span><br><span class="line">    <span class="comment"># 创建一个随机动物</span></span><br><span class="line">    <span class="keyword">return</span> random.choice([Dog, Cat])()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建一个猫店</span></span><br><span class="line">cat_shop = PetShop(Cat)</span><br><span class="line">cat_shop.show_pet()</span><br><span class="line"><span class="comment"># 输出结果: We have a lovely Cat, it says meow.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 售卖随机动物的商店</span></span><br><span class="line">shop = PetShop(random_animal)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    shop.show_pet()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">We have a lovely Dog, it says woof.</span></span><br><span class="line"><span class="string">We have a lovely Dog, it says woof.</span></span><br><span class="line"><span class="string">We have a lovely Cat, it says meow.</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="单例模式-Singleton-Pattern"><a href="#单例模式-Singleton-Pattern" class="headerlink" title="单例模式(Singleton Pattern)"></a>单例模式(Singleton Pattern)</h2><h3 id="什么是单例模式"><a href="#什么是单例模式" class="headerlink" title="什么是单例模式"></a>什么是单例模式</h3><p>单例模式–确保一个类只有一个实例, 并提供一个全局访问点.</p><h3 id="python实现单例模式"><a href="#python实现单例模式" class="headerlink" title="python实现单例模式"></a>python实现单例模式</h3><p>python实现单例模式大体上有四种方法:</p><ul><li>函数装饰器实现单例模式</li><li>类装饰器实现单例模式</li><li>new关键字实现单例模式</li><li>metaclass实现单例模式</li></ul><h4 id="装饰器实现单例模式"><a href="#装饰器实现单例模式" class="headerlink" title="装饰器实现单例模式"></a>装饰器实现单例模式</h4><p>使用python自带的装饰器可以简单实现装饰器模式.</p><p>函数装饰器:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">singleton</span>(<span class="params">cls</span>):</span><br><span class="line">    <span class="comment"># 函数装饰器实现单例模式</span></span><br><span class="line">    _instance = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">inner</span>():</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> _instance:</span><br><span class="line">            _instance[cls] = cls()</span><br><span class="line">        <span class="keyword">return</span> _instance[cls]</span><br><span class="line">    <span class="keyword">return</span> inner</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@singleton</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">test1 = Test()</span><br><span class="line">test2 = Test()</span><br><span class="line"><span class="comment"># 测试两个类实例指针是否一样</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(test1) == <span class="built_in">id</span>(test2))</span><br><span class="line"><span class="comment"># 输出结果: True</span></span><br></pre></td></tr></table></figure><p>类装饰器:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Singleton</span>():</span><br><span class="line">    <span class="comment"># 类装饰器实现单例模式</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, cls</span>):</span><br><span class="line">        <span class="variable language_">self</span>._cls = cls</span><br><span class="line">        <span class="variable language_">self</span>._instance = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 类被调用时会触发此方法</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>._cls <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._instance:</span><br><span class="line">            <span class="variable language_">self</span>._instance[<span class="variable language_">self</span>._cls] = <span class="variable language_">self</span>._cls()</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._instance[<span class="variable language_">self</span>._cls]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Singleton</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">test1 = Test()</span><br><span class="line">test2 = Test()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(test1) == <span class="built_in">id</span>(test2))</span><br><span class="line"><span class="comment"># 输出结果: True</span></span><br></pre></td></tr></table></figure><h4 id="使用new关键字实现单例模式"><a href="#使用new关键字实现单例模式" class="headerlink" title="使用new关键字实现单例模式"></a>使用new关键字实现单例模式</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Singleton</span>():</span><br><span class="line">    <span class="comment"># new关键字实现单例模式</span></span><br><span class="line">    _instance = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">cls, *args, **kw</span>):</span><br><span class="line">        <span class="comment"># 在新建类实例时调用该方法</span></span><br><span class="line">        <span class="keyword">if</span> cls._instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            cls._instance = <span class="built_in">object</span>.__new__(cls, *args, **kw)</span><br><span class="line">        <span class="keyword">return</span> cls._instance</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">singleton1 = Singleton()</span><br><span class="line">singleton2 = Singleton()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(singleton1) == <span class="built_in">id</span>(singleton2))</span><br><span class="line"><span class="comment"># 输出结果: True</span></span><br></pre></td></tr></table></figure><h4 id="使用metaclass实现单例模式"><a href="#使用metaclass实现单例模式" class="headerlink" title="使用metaclass实现单例模式"></a>使用metaclass实现单例模式</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Singleton</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="comment"># metaclass实现单例模式</span></span><br><span class="line">    _instances = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> cls._instances:</span><br><span class="line">            cls._instances[cls] = <span class="built_in">super</span>(</span><br><span class="line">                Singleton, cls).__call__(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> cls._instances[cls]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>(metaclass=Singleton):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">test1 = Test()</span><br><span class="line">test2 = Test()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(test1) == <span class="built_in">id</span>(test2))</span><br></pre></td></tr></table></figure><p>这里, 我们将 metaclass 指向 Singleton 类, 让 Singleton 中的 type 来创造新的实例.</p><h2 id="命令模式-Command-Pattern"><a href="#命令模式-Command-Pattern" class="headerlink" title="命令模式(Command Pattern)"></a>命令模式(Command Pattern)</h2><h3 id="什么是命令模式"><a href="#什么是命令模式" class="headerlink" title="什么是命令模式"></a>什么是命令模式</h3><p>命令模式–将请求封装成对象, 这可以让你使用不同的请求, 队列, 或者日志请求来参数化其他对象. 命令模式也可以支持撤销对象.</p><h3 id="python实现命令模式"><a href="#python实现命令模式" class="headerlink" title="python实现命令模式"></a>python实现命令模式</h3><p>下面是一个带撤销按键的电灯遥控器的例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">RemoteControl</span>():</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">        遥控器类, 用于注册命令和触发命令.</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.buttons = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>.records = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">set_command</span>(<span class="params">self, button, command</span>):</span><br><span class="line">        <span class="comment"># 注册命令到遥控器按键上</span></span><br><span class="line">        <span class="variable language_">self</span>.buttons[button] = command</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">button_on_click</span>(<span class="params">self, button</span>):</span><br><span class="line">        <span class="comment"># 当按键被触发时执行命令, 并记录该命令</span></span><br><span class="line">        <span class="variable language_">self</span>.buttons[button].execute()</span><br><span class="line">        <span class="variable language_">self</span>.records.append(button)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo_on_click</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 当用户点击撤销时按顺序撤销命令</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(<span class="variable language_">self</span>.records) &gt; <span class="number">0</span>:</span><br><span class="line">            button = <span class="variable language_">self</span>.records.pop()</span><br><span class="line">            <span class="variable language_">self</span>.buttons[button].undo()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Command</span>():</span><br><span class="line">    <span class="comment"># 命令父类</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LightOnCommand</span>(<span class="title class_ inherited__">Command</span>):</span><br><span class="line">    <span class="comment"># 开灯命令</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 执行</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Light on!&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 撤销</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Light off!&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LightOffCommand</span>(<span class="title class_ inherited__">Command</span>):</span><br><span class="line">    <span class="comment"># 关灯命令</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 执行</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Light off!&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 撤销</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Light on!&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建遥控器类和命令类</span></span><br><span class="line">remote_control = RemoteControl()</span><br><span class="line">light_on_command = LightOnCommand()</span><br><span class="line">light_off_command = LightOffCommand()</span><br><span class="line"><span class="comment"># 将命令类注册到遥控器按键上</span></span><br><span class="line">remote_control.set_command(<span class="number">0</span>, light_on_command)</span><br><span class="line">remote_control.set_command(<span class="number">1</span>, light_off_command)</span><br><span class="line"><span class="comment"># 点击按键</span></span><br><span class="line">remote_control.button_on_click(<span class="number">1</span>)</span><br><span class="line">remote_control.button_on_click(<span class="number">0</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27; 输出结果: </span></span><br><span class="line"><span class="string">Light off!</span></span><br><span class="line"><span class="string">Light on!</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 点击撤销按键</span></span><br><span class="line">remote_control.undo_on_click()</span><br><span class="line">remote_control.undo_on_click()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Light off!</span></span><br><span class="line"><span class="string">Light on!</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="适配器模式-Adapter-Pattern"><a href="#适配器模式-Adapter-Pattern" class="headerlink" title="适配器模式(Adapter Pattern)"></a>适配器模式(Adapter Pattern)</h2><h3 id="什么是适配器模式"><a href="#什么是适配器模式" class="headerlink" title="什么是适配器模式"></a>什么是适配器模式</h3><p>适配器模式–将一个类的接口转换成客户期望的另一个接口, 适配器让原本不兼容的类可以合作无间.</p><h3 id="python实现适配器模式"><a href="#python实现适配器模式" class="headerlink" title="python实现适配器模式"></a>python实现适配器模式</h3><p>让狗伪装成人:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Human</span>():</span><br><span class="line">    <span class="comment"># 人类类, 会speak()</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Hello&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>():</span><br><span class="line">    <span class="comment"># 狗狗类, 会bark()</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">bark</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;woof&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AdapterHuman</span>(<span class="title class_ inherited__">Human</span>):</span><br><span class="line">    <span class="comment"># 让狗狗套进人皮套, 学会speak()</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, dog</span>):</span><br><span class="line">        <span class="variable language_">self</span>.dog = dog</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.dog.bark()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">dog = Dog()</span><br><span class="line">human = Human()</span><br><span class="line">dogman = AdapterHuman(dog)</span><br><span class="line"></span><br><span class="line">dogman.speak()</span><br><span class="line">human.speak()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">woof</span></span><br><span class="line"><span class="string">Hello</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><p>python还可以通过替代方法来适配对象</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = <span class="string">&quot;Dog&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">bark</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;woof!&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Adapter</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;通过替代方法来适配对象.</span></span><br><span class="line"><span class="string">    Usage</span></span><br><span class="line"><span class="string">    ------</span></span><br><span class="line"><span class="string">    dog = Dog()</span></span><br><span class="line"><span class="string">    dog = Adapter(dog, make_noise=dog.bark)</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, obj, **adapted_methods</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;我们在dict中设置适配方法.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.obj = obj</span><br><span class="line">        <span class="variable language_">self</span>.__dict__.update(adapted_methods)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__getattr__</span>(<span class="params">self, attr</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;所有未适配的调用也都传递给对象.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">getattr</span>(<span class="variable language_">self</span>.obj, attr)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">original_dict</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;返回原始对象的dict&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.obj.__dict__</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">dog = Dog()</span><br><span class="line">dog_adapter = Adapter(dog, make_noise=dog.bark)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(dog.name)</span><br><span class="line"><span class="built_in">print</span>(dog_adapter.name)</span><br><span class="line"><span class="built_in">print</span>(dog.bark())</span><br><span class="line"><span class="built_in">print</span>(dog_adapter.make_noise())</span><br><span class="line"><span class="built_in">print</span>(dog_adapter.bark())</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">Dog</span></span><br><span class="line"><span class="string">Dog</span></span><br><span class="line"><span class="string">woof!</span></span><br><span class="line"><span class="string">woof!</span></span><br><span class="line"><span class="string">woof!</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="外观模式-Facade-Pattern"><a href="#外观模式-Facade-Pattern" class="headerlink" title="外观模式(Facade Pattern)"></a>外观模式(Facade Pattern)</h2><h3 id="什么是外观模式"><a href="#什么是外观模式" class="headerlink" title="什么是外观模式"></a>什么是外观模式</h3><p>外观模式–提供了一个统一的接口, 用来访问子系统中的一群接口. 外观定义了一个高层接口, 让子系统更容易使用.</p><h3 id="python实现外观模式"><a href="#python实现外观模式" class="headerlink" title="python实现外观模式"></a>python实现外观模式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 一个复杂的电脑运行</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CPU</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    简单的CPU类.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">freeze</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Freezing processor.&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">jump</span>(<span class="params">self, position</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Jumping to:&quot;</span>, position)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Executing.&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Memory</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    简单的储存器类.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">load</span>(<span class="params">self, position, data</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Loading from &#123;0&#125; data: &#x27;&#123;1&#125;&#x27;.&quot;</span>.<span class="built_in">format</span>(position, data))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SolidStateDrive</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    简单的SSD类.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">read</span>(<span class="params">self, lba, size</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Some data from sector &#123;0&#125; with size &#123;1&#125;&quot;</span>.<span class="built_in">format</span>(lba, size)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ComputerFacade</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    用外观模式让计算机运行变得简单起来.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.cpu = CPU()</span><br><span class="line">        <span class="variable language_">self</span>.memory = Memory()</span><br><span class="line">        <span class="variable language_">self</span>.ssd = SolidStateDrive()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">start</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.cpu.freeze()</span><br><span class="line">        <span class="variable language_">self</span>.memory.load(<span class="string">&quot;0x00&quot;</span>, <span class="variable language_">self</span>.ssd.read(<span class="string">&quot;100&quot;</span>, <span class="string">&quot;1024&quot;</span>))</span><br><span class="line">        <span class="variable language_">self</span>.cpu.jump(<span class="string">&quot;0x00&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.cpu.execute()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">computer_facade = ComputerFacade()</span><br><span class="line">computer_facade.start()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果</span></span><br><span class="line"><span class="string">Freezing processor.</span></span><br><span class="line"><span class="string">Loading from 0x00 data: &#x27;Some data from sector 100 with size 1024&#x27;.</span></span><br><span class="line"><span class="string">Jumping to: 0x00</span></span><br><span class="line"><span class="string">Executing.</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="模板模式-Template-Pattern"><a href="#模板模式-Template-Pattern" class="headerlink" title="模板模式(Template Pattern)"></a>模板模式(Template Pattern)</h2><h3 id="什么是模板模式"><a href="#什么是模板模式" class="headerlink" title="什么是模板模式"></a>什么是模板模式</h3><p>模板模式–在一个方法中定义一个算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤.</p><h3 id="python实现模板模式"><a href="#python实现模板模式" class="headerlink" title="python实现模板模式"></a>python实现模板模式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;三种不同的获取数据方式&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_text</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;plain-text&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_pdf</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;pdf&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_csv</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;csv&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 转换数据格式</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">convert_to_text</span>(<span class="params">data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[CONVERT]&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#125; as text&quot;</span>.<span class="built_in">format</span>(data)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存数据</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">saver</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[SAVE]&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">template_function</span>(<span class="params">getter, converter=<span class="literal">False</span>, to_save=<span class="literal">False</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模板方法, 根据自己的需要使用不同的方法, 基础的算法骨架是一样的&quot;&quot;&quot;</span></span><br><span class="line">    data = getter()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Got `&#123;&#125;`&quot;</span>.<span class="built_in">format</span>(data))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(data) &lt;= <span class="number">3</span> <span class="keyword">and</span> converter:</span><br><span class="line">        data = converter(data)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Skip conversion&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> to_save:</span><br><span class="line">        saver()</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;`&#123;&#125;` was processed&quot;</span>.<span class="built_in">format</span>(data))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">template_function(get_text, to_save=<span class="literal">True</span>)</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果: </span></span><br><span class="line"><span class="string">Got `plain-text`</span></span><br><span class="line"><span class="string">Skip conversion</span></span><br><span class="line"><span class="string">[SAVE]</span></span><br><span class="line"><span class="string">`plain-text` was processed</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">template_function(get_pdf, converter=convert_to_text)</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果:</span></span><br><span class="line"><span class="string">Got `pdf`</span></span><br><span class="line"><span class="string">[CONVERT]</span></span><br><span class="line"><span class="string">`pdf as text` was processed</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">template_function(get_csv, to_save=<span class="literal">True</span>)</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果:</span></span><br><span class="line"><span class="string">Got `csv`</span></span><br><span class="line"><span class="string">Skip conversion</span></span><br><span class="line"><span class="string">[SAVE]</span></span><br><span class="line"><span class="string">`csv` was processed    </span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h2 id="迭代器模式-Iterator-Pattern"><a href="#迭代器模式-Iterator-Pattern" class="headerlink" title="迭代器模式(Iterator Pattern)"></a>迭代器模式(Iterator Pattern)</h2><h3 id="什么是迭代器模式"><a href="#什么是迭代器模式" class="headerlink" title="什么是迭代器模式"></a>什么是迭代器模式</h3><p>迭代器模式–提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示.</p><h3 id="python实现迭代器模式"><a href="#python实现迭代器模式" class="headerlink" title="python实现迭代器模式"></a>python实现迭代器模式</h3><p>python可以通过 <code>yield</code> 或者 <code>iter()</code>方法 快速简单实现迭代器模式.</p><p>利用yield迭代方法快速跳出二重循环:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">listA = [<span class="number">5</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="number">7</span>, <span class="number">2</span>]</span><br><span class="line">listB = [<span class="number">2</span>, <span class="number">8</span>, <span class="number">3</span>, <span class="number">6</span>, <span class="number">9</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_pairs</span>(<span class="params">listA, listB</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;在listA和listB范围中生成索引对&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> listA:</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> listB:</span><br><span class="line">            <span class="keyword">yield</span> i, j</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i, j <span class="keyword">in</span> get_index(listA, listB):</span><br><span class="line">    <span class="keyword">if</span> i == j:</span><br><span class="line">        <span class="keyword">break</span></span><br></pre></td></tr></table></figure><p>利用iter()方法进行迭代:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">NumberWords</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;数数, 从start到stop的英文数字迭代&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    _WORD_MAP = (</span><br><span class="line">        <span class="string">&#x27;one&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;two&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;three&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;four&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;five&#x27;</span>,</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, start, stop</span>):</span><br><span class="line">        <span class="variable language_">self</span>.start = start</span><br><span class="line">        <span class="variable language_">self</span>.stop = stop</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__iter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 该方法使得这个类可以迭代</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__next__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 该方法使得这个类成为迭代器, 每次迭代会调用该方法</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.start &gt; <span class="variable language_">self</span>.stop <span class="keyword">or</span> <span class="variable language_">self</span>.start &gt; <span class="built_in">len</span>(<span class="variable language_">self</span>._WORD_MAP):</span><br><span class="line">            <span class="keyword">raise</span> StopIteration</span><br><span class="line">        current = <span class="variable language_">self</span>.start</span><br><span class="line">        <span class="variable language_">self</span>.start += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._WORD_MAP[current - <span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> number <span class="keyword">in</span> NumberWords(start=<span class="number">1</span>, stop=<span class="number">5</span>):</span><br><span class="line">    <span class="built_in">print</span>(number)</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果:</span></span><br><span class="line"><span class="string">one</span></span><br><span class="line"><span class="string">two</span></span><br><span class="line"><span class="string">three</span></span><br><span class="line"><span class="string">four</span></span><br><span class="line"><span class="string">five</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h2 id="组合模式-Composite-Pattern"><a href="#组合模式-Composite-Pattern" class="headerlink" title="组合模式(Composite Pattern)"></a>组合模式(Composite Pattern)</h2><h3 id="什么是组合模式"><a href="#什么是组合模式" class="headerlink" title="什么是组合模式"></a>什么是组合模式</h3><p>组合模式–允许你将对象组合成树状结构来表现”整体&#x2F;部分”层次结构. 组合能让客户以一致的方式处理个别对象以及对象组合.</p><h3 id="python实现组合模式"><a href="#python实现组合模式" class="headerlink" title="python实现组合模式"></a>python实现组合模式</h3><p>一个可以任意组合的绘图类:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Graphic</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;绘图父类 &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError(<span class="string">&quot;You should implement this!&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CompositeGraphic</span>(<span class="title class_ inherited__">Graphic</span>):</span><br><span class="line">    <span class="comment"># 绘制复杂图像</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.graphics = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">for</span> graphic <span class="keyword">in</span> <span class="variable language_">self</span>.graphics:</span><br><span class="line">            graphic.render()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">self, graphic</span>):</span><br><span class="line">        <span class="variable language_">self</span>.graphics.append(graphic)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">remove</span>(<span class="params">self, graphic</span>):</span><br><span class="line">        <span class="variable language_">self</span>.graphics.remove(graphic)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Ellipse</span>(<span class="title class_ inherited__">Graphic</span>):</span><br><span class="line">    <span class="comment"># 绘制椭圆图像</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Ellipse: &#123;&#125;&quot;</span>.<span class="built_in">format</span>(<span class="variable language_">self</span>.name))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 三个椭圆</span></span><br><span class="line">ellipse1 = Ellipse(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">ellipse2 = Ellipse(<span class="string">&quot;2&quot;</span>)</span><br><span class="line">ellipse3 = Ellipse(<span class="string">&quot;3&quot;</span>)</span><br><span class="line"><span class="comment"># 两个复杂图像</span></span><br><span class="line">graphic1 = CompositeGraphic()</span><br><span class="line">graphic2 = CompositeGraphic()</span><br><span class="line"><span class="comment"># 将三个椭圆添加到这两个复杂图像中</span></span><br><span class="line">graphic1.add(ellipse1)</span><br><span class="line">graphic1.add(ellipse2)</span><br><span class="line">graphic2.add(ellipse3)</span><br><span class="line"><span class="comment"># 再将这两个复杂图像再添加到新的复杂图像中</span></span><br><span class="line">graphic = CompositeGraphic()</span><br><span class="line">graphic.add(graphic1)</span><br><span class="line">graphic.add(graphic2)</span><br><span class="line"><span class="comment"># 所有图像都可以任意组合, 输出只需要使用render()方法即可</span></span><br><span class="line">graphic.render()</span><br><span class="line"><span class="string">&quot;&quot;&quot;输出结果:</span></span><br><span class="line"><span class="string">Ellipse: 1</span></span><br><span class="line"><span class="string">Ellipse: 2</span></span><br><span class="line"><span class="string">Ellipse: 3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h2 id="状态模式-State-Pattern"><a href="#状态模式-State-Pattern" class="headerlink" title="状态模式(State Pattern)"></a>状态模式(State Pattern)</h2><h3 id="什么是状态模式"><a href="#什么是状态模式" class="headerlink" title="什么是状态模式"></a>什么是状态模式</h3><p>状态模式–允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类.</p><h3 id="python实现状态模式"><a href="#python实现状态模式" class="headerlink" title="python实现状态模式"></a>python实现状态模式</h3><p>实现一个可以切换波段的收音机.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">State</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;状态父类&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">scan</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;调频&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.pos += <span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.pos == <span class="built_in">len</span>(<span class="variable language_">self</span>.stations):</span><br><span class="line">            <span class="variable language_">self</span>.pos = <span class="number">0</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Scanning... Station is &#123;&#125; &#123;&#125;&quot;</span>.<span class="built_in">format</span>(</span><br><span class="line">            <span class="variable language_">self</span>.stations[<span class="variable language_">self</span>.pos], <span class="variable language_">self</span>.name))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmState</span>(<span class="title class_ inherited__">State</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;中波状态&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, radio</span>):</span><br><span class="line">        <span class="variable language_">self</span>.radio = radio</span><br><span class="line">        <span class="variable language_">self</span>.stations = [<span class="string">&quot;1250&quot;</span>, <span class="string">&quot;1380&quot;</span>, <span class="string">&quot;1510&quot;</span>]</span><br><span class="line">        <span class="variable language_">self</span>.pos = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.name = <span class="string">&quot;AM&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">toggle_amfm</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Switching to FM&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.radio.state = <span class="variable language_">self</span>.radio.fmstate</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FmState</span>(<span class="title class_ inherited__">State</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;短波状态&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, radio</span>):</span><br><span class="line">        <span class="variable language_">self</span>.radio = radio</span><br><span class="line">        <span class="variable language_">self</span>.stations = [<span class="string">&quot;81.3&quot;</span>, <span class="string">&quot;89.1&quot;</span>, <span class="string">&quot;103.9&quot;</span>]</span><br><span class="line">        <span class="variable language_">self</span>.pos = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.name = <span class="string">&quot;FM&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">toggle_amfm</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Switching to AM&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.radio.state = <span class="variable language_">self</span>.radio.amstate</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Radio</span>:</span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;&quot;一个收音机. 有一个调频按钮和AM/FM切换按钮.&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;可以收听中波和短波, 初始化为中波&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.amstate = AmState(<span class="variable language_">self</span>)</span><br><span class="line">        <span class="variable language_">self</span>.fmstate = FmState(<span class="variable language_">self</span>)</span><br><span class="line">        <span class="variable language_">self</span>.state = <span class="variable language_">self</span>.amstate</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">toggle_amfm</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 切换波段</span></span><br><span class="line">        <span class="variable language_">self</span>.state.toggle_amfm()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">scan</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 调频</span></span><br><span class="line">        <span class="variable language_">self</span>.state.scan()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建一个收音机, 并进行调频和切换波段</span></span><br><span class="line">radio = Radio()</span><br><span class="line">actions = [radio.scan] * <span class="number">2</span> + [radio.toggle_amfm] + [radio.scan] * <span class="number">2</span></span><br><span class="line">actions *= <span class="number">2</span></span><br><span class="line"><span class="keyword">for</span> action <span class="keyword">in</span> actions:</span><br><span class="line">    action()</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果</span></span><br><span class="line"><span class="string">Scanning... Station is 1380 AM</span></span><br><span class="line"><span class="string">Scanning... Station is 1510 AM</span></span><br><span class="line"><span class="string">Switching to FM</span></span><br><span class="line"><span class="string">Scanning... Station is 89.1 FM</span></span><br><span class="line"><span class="string">Scanning... Station is 103.9 FM</span></span><br><span class="line"><span class="string">Scanning... Station is 81.3 FM</span></span><br><span class="line"><span class="string">Scanning... Station is 89.1 FM</span></span><br><span class="line"><span class="string">Switching to AM</span></span><br><span class="line"><span class="string">Scanning... Station is 1250 AM</span></span><br><span class="line"><span class="string">Scanning... Station is 1380 AM</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="代理模式-Proxy-Pattern"><a href="#代理模式-Proxy-Pattern" class="headerlink" title="代理模式(Proxy Pattern)"></a>代理模式(Proxy Pattern)</h2><h3 id="什么是代理模式"><a href="#什么是代理模式" class="headerlink" title="什么是代理模式"></a>什么是代理模式</h3><p>代理模式–为一个对象提供一个替身或占位符以访问这个对象.</p><h3 id="python实现代理模式"><a href="#python实现代理模式" class="headerlink" title="python实现代理模式"></a>python实现代理模式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Subject</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    RealSubject和Proxy的接口应该是相同的,</span></span><br><span class="line"><span class="string">    因为客户端应该能够使用RealSubject或Proxy, 而不需要更改代码.</span></span><br><span class="line"><span class="string">    并非所有时候都需要此接口. 关键是客户端应该能够互换使用RealSubject或Proxy,</span></span><br><span class="line"><span class="string">    而无需更改代码.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">do_the_job</span>(<span class="params">self, user</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RealSubject</span>(<span class="title class_ inherited__">Subject</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;这是真正工作的对象.&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">do_the_job</span>(<span class="params">self, user</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;I am doing the job for &#123;&#125;&quot;</span>.<span class="built_in">format</span>(user))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Proxy</span>(<span class="title class_ inherited__">Subject</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;这个是代理对象&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._real_subject = RealSubject()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">do_the_job</span>(<span class="params">self, user</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        记录和控制访问.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;[log] Doing the job for &#123;&#125; is requested.&quot;</span>.<span class="built_in">format</span>(user))</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> user == <span class="string">&quot;admin&quot;</span>:</span><br><span class="line">            <span class="variable language_">self</span>._real_subject.do_the_job(user)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;[log] I can do the job just for `admins`.&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">client</span>(<span class="params">job_doer, user</span>):</span><br><span class="line">    job_doer.do_the_job(user)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">proxy = Proxy()</span><br><span class="line">real_subject = RealSubject()</span><br><span class="line">client(proxy, <span class="string">&#x27;admin&#x27;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27; 输出结果: </span></span><br><span class="line"><span class="string">[log] Doing the job for admin is requested.</span></span><br><span class="line"><span class="string">I am doing the job for admin</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">client(proxy, <span class="string">&#x27;anonymous&#x27;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">[log] Doing the job for anonymous is requested.</span></span><br><span class="line"><span class="string">[log] I can do the job just for `admins`.</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">client(real_subject, <span class="string">&#x27;admin&#x27;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">I am doing the job for admin</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">client(real_subject, <span class="string">&#x27;anonymous&#x27;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;输出结果:</span></span><br><span class="line"><span class="string">I am doing the job for anonymous</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>以上例子大部分是来自于github上的项目 <a href="https://github.com/faif/python-patterns">python-patterns</a>, 还有很多有趣的设计模式没有在这篇文章中记录, 有兴趣的可以去github上查阅, 这些设计模式也经常混搭使用, 但是要记住并不是大部分情况都要使用设计模式, 切忌生搬硬套.</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>[1] <a href="https://books.google.com/books?hl=zh-CN&lr=&id=NbCNAQAAQBAJ&oi=fnd&pg=PR25&dq=Head+First+Design+Patterns&ots=Jig_4nXAoy&sig=R-w8zKVn9eCnzdaPc2Gr_6dZslI#v=onepage&q=Head%20First%20Design%20Patterns&f=false">Freeman E, Robson E, Bates B, et al. Head first design patterns[M]. “ O’Reilly Media, Inc.”, 2008.</a></em><br><em>[2] <a href="https://github.com/faif/python-patterns">python-patterns</a></em></p>]]></content>
    
    
    <summary type="html">详细介绍几种常用设计模式的python实现方式, 包括策略模式, 观察者模式, 装饰者模式, 工厂模式, 抽象工厂模式, 单例模式等</summary>
    
    
    
    <category term="python" scheme="https://zzz5.github.io/categories/python/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="design pattern" scheme="https://zzz5.github.io/tags/design-pattern/"/>
    
  </entry>
  
  <entry>
    <title>使用scikit-learn实现各种降维算法</title>
    <link href="https://zzz5.github.io/2020/07/08/python/scikit-learn/sklearn-dimension%20reduction/"/>
    <id>https://zzz5.github.io/2020/07/08/python/scikit-learn/sklearn-dimension%20reduction/</id>
    <published>2020-07-08T03:01:00.000Z</published>
    <updated>2020-07-21T04:31:38.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="什么是降维"><a href="#什么是降维" class="headerlink" title="什么是降维"></a>什么是降维</h2><p>降维是高维数据可视分析的一种重要的数据抽象技术, 它将高维数据降解到低维空间, 同时尽可能保留诸如异常点, 聚类等特征. 降维的方法林林总总, 在可视分析中很难一步到位使用不需要任何适配的降维方法. 常见的做法是通过交互的方式, 将标准的降维方法适配到具体的应用场景中.</p><h2 id="主成分分析-PCA"><a href="#主成分分析-PCA" class="headerlink" title="主成分分析(PCA)"></a>主成分分析(PCA)</h2><h3 id="简述"><a href="#简述" class="headerlink" title="简述"></a>简述</h3><p>主成分分析(Principal Component Analysis, PCA); 通俗理解: 就是找出几个最主要的特征, 然后进行分析.</p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>PCA 工作原理简述(这里不讨论详细算法):</p><ol><li>找出第一个主成分的方向, 也就是数据 方差最大 的方向.</li><li>找出第二个主成分的方向, 也就是数据 方差次大 的方向, 并且该方向与第一个主成分方向 正交(orthogonal 如果是二维空间就叫垂直).</li><li>通过这种方式计算出所有的主成分方向.</li><li>通过数据集的协方差矩阵及其特征值分析, 我们就可以得到这些主成分的值.</li><li>一旦得到了协方差矩阵的特征值和特征向量, 我们就可以保留最大的 N 个特征. 这些特征向量也给出了 N 个最重要特征的真实结构, 我们就可以通过将数据乘上这 N 个特征向量 从而将它转换到新的空间上.</li></ol><h3 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h3><p>优点:</p><ol><li>它是无监督学习, 完全无参数限制的. 在PCA的计算过程中完全不需要人为的设定参数或是根据任何经验模型对计算进行干预, 最后的结果只与数据相关, 与用户是独立的.</li><li>用PCA技术可以对数据进行降维, 同时对新求出的”主元”向量的重要性进行排序, 根据需要取前面最重要的部分, 将后面的维数省去, 可以达到降维从而简化模型或是对数据进行压缩的效果, 同时最大程度的保持了原有数据的信息.</li><li>各主成分之间正交, 可消除原始数据成分间的相互影响.</li><li>计算方法简单, 易于在计算机上实现.</li></ol><p>缺点:</p><ol><li>如果用户对观测对象有一定的先验知识, 掌握了数据的一些特征, 却无法通过参数化等方法对处理过程进行干预, 可能会得不到预期的效果, 效率也不高.</li><li>贡献率小的主成分往往可能含有对样本差异的重要信息.</li><li>特征值矩阵的正交向量空间是否唯一有待讨论.</li><li>在非高斯分布的情况下, PCA方法得出的主元可能并不是最优的, 此时在寻找主元时不能将方差作为衡量重要性的标准.</li></ol><h3 id="scikit-learn库实现PCA"><a href="#scikit-learn库实现PCA" class="headerlink" title="scikit-learn库实现PCA"></a>scikit-learn库实现PCA</h3><p>使用的是sklearn.decomposition 模块中的 PCA方法.</p><blockquote><p><a href="https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html#sklearn.decomposition.PCA">sklearn.decomposition.PCA(n_components&#x3D;None, copy&#x3D;True, whiten&#x3D;False, svd_solver&#x3D;’auto’)</a></p></blockquote><p>各参数:</p><ul><li>n_components(int, float, None or str): <ul><li>要保留的特征数, 如果不设置或为’None’的话, <code>n_components == min(n_samples, n_features)</code>; </li><li>如果设置的为float类型的值, 则会保留全部特征数的百分比的特征数数量; </li><li>如果<code>n_components=&#39;mle&#39;</code> 且 <code>svd_solver=&#39;full&#39;</code> 则会使用最大似然估计法(MLE)来估计特征数, 使用<code>n_Components=&#39;mle&#39;</code>会将<code>svd_solver=&#39;auto&#39;</code>解释为<code>svd_solver=&#39;full&#39;</code>;</li><li>如果<code>svd_solver=&#39;arpack&#39;</code>, 则组件数量必须严格小于n_Feature和n_Samples的最小值. 因此, ‘None’会导致: <code>n_Components == min(n_Samples, n_Feature)-1</code></li></ul></li><li>copy(bool, default True): 如果为False, 则传递给fit的数据将被覆盖, 并且运行fit(X).transform(X)将不会产生预期的结果, 请改用fit_transform(X).</li><li>whiten(bool, optional, default False): 是否白化, 默认为否.</li><li>svd_solver (str, default ‘auto’): <ul><li>‘auto’: 解算器由基于<code>X.shape</code>和<code>n_components</code>的默认策略选择: 如果输入数据大于500x500, 并且要提取的特征数量低于数据最小维度的80%, 则启用更高效的”randomized”方法; 否则, 使用”full”方案.</li><li>‘full’: 通过scipy.linalg.svd调用标准LAPACK解算器运行精确完整的SVD, 并通过后处理选择特征.</li><li>‘arpack’: 通过scipy.parse.linalg.svds调用ARPACK解算器. 它严格要求<code>0 &lt; n_components &lt; min(X.shape)</code></li><li>‘randomized’: 用Halko等人的方法进行随机化奇异值(SVD)分解.</li></ul></li></ul><p>实际使用时需要先调用 <code>fit(X)</code> 使用X拟合模型, 再调用<code>transform(X)</code>对X进行PCA降维.</p><p>下面是用PCA将鸢尾花数据降为二维的例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</span><br><span class="line"><span class="keyword">from</span> sklearn.decomposition <span class="keyword">import</span> PCA</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入鸢尾花数据集</span></span><br><span class="line">iris = datasets.load_iris()</span><br><span class="line">X = iris.data</span><br><span class="line">y = iris.target</span><br><span class="line">target_names = iris.target_names</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行PCA降维</span></span><br><span class="line">pca = PCA(n_components=<span class="number">2</span>)</span><br><span class="line">X_r = pca.fit(X).transform(X)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绘制降维后的数据图</span></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line"><span class="keyword">for</span> i, target_name <span class="keyword">in</span> <span class="built_in">zip</span>(<span class="built_in">range</span>(<span class="number">3</span>), target_names):</span><br><span class="line">    ax.scatter(X_r[y==i, <span class="number">0</span>], X_r[y==i, <span class="number">1</span>], label=target_name, alpha=<span class="number">0.8</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.title(<span class="string">&#x27;PCA of IRIS dataset&#x27;</span>)</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/sklearn/sklearn07.svg" alt="sklearn07"></p><h2 id="线性判别分析-LDA"><a href="#线性判别分析-LDA" class="headerlink" title="线性判别分析(LDA)"></a>线性判别分析(LDA)</h2><h3 id="简述-1"><a href="#简述-1" class="headerlink" title="简述"></a>简述</h3><p>线性判别分析(Linear Discriminant Analysis,LDA)是一种可作为特征抽取的技术.<br>LDA可以提高数据分析过程中的计算效率, 对于未能正则化的模型, 可以降低维度灾难带来的过拟合.</p><h3 id="和PCA的异同"><a href="#和PCA的异同" class="headerlink" title="和PCA的异同"></a>和PCA的异同</h3><p>相同点:</p><ol><li>两者均可以对数据进行降维.</li><li>两者在降维时均使用了矩阵特征分解的思想.</li><li>两者都假设数据符合高斯分布.</li></ol><p>不同点:</p><ol><li>LDA是有监督的降维方法, 而PCA是无监督的降维方法.</li><li>LDA除了可以用于降维, 还可以用于分类.</li><li>LDA选择分类性能最好的投影方向, 而PCA选择样本点投影具有最大方差的方向.</li></ol><h3 id="原理-1"><a href="#原理-1" class="headerlink" title="原理"></a>原理</h3><ol><li>对D维数据集进行标准化处理(D为特征数量, 即维度)</li><li>对每一类别, 计算D维的均值向量</li><li>构造类间的散布矩阵以及类内的散布矩阵</li><li>计算矩阵的特征值所对应的特征向量, </li><li>选取前k个特征值对应的特征向量, 构造一个D x K维的转换矩阵W, 特征向量以列的形式排列</li><li>使用转换矩阵W将样本映射到新的特征子空间上</li></ol><h3 id="优缺点-1"><a href="#优缺点-1" class="headerlink" title="优缺点"></a>优缺点</h3><p>优点:</p><ol><li>在降维过程中可以使用类别的先验知识经验, 而像PCA这样的无监督学习则无法使用类别先验知识.</li><li>LDA在样本分类信息依赖均值而不是方差的时候, 比PCA之类的算法较优.</li></ol><p>缺点有:</p><ol><li>LDA不适合对非高斯分布样本进行降维, PCA也有这个问题.</li><li>LDA在样本分类信息依赖方差而不是均值的时候, 降维效果不好.</li><li>LDA可能过度拟合数据.</li></ol><h3 id="scikit-learn库实现LDA"><a href="#scikit-learn库实现LDA" class="headerlink" title="scikit-learn库实现LDA"></a>scikit-learn库实现LDA</h3><p>使用的是sklearn.discriminant_analysis 模块中的LinearDiscriminantAnalysis方法.</p><blockquote><p><a href="https://scikit-learn.org/stable/modules/generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis">sklearn.discriminant_analysis.LinearDiscriminantAnalysis(solver&#x3D;’svd’, shrinkage&#x3D;None, n_components&#x3D;None, store_covariance&#x3D;False)</a><br>各参数:</p></blockquote><ul><li><p>solver({‘svd’, ‘lsqr’, ‘eigen’}, default&#x3D;’svd’):</p><ul><li>‘svd’: 奇异值分解. 不计算协方差, 因此建议对具有大量特征的数据使用此求解器.</li><li>‘lsqr’: 最小二乘解, 特征值分解, 可以与收缩相结合.</li><li>‘eigen’: 特征值分解, 可以与收缩相结合.</li></ul></li><li><p>shrinkage(‘auto’ or float, default&#x3D;None): 收缩率参数</p><ul><li>‘auto’: 使用 Ledoit-Wolf lemma 自动收缩.</li><li>float(between 0 and 1): 在0和1之间浮动, 固定收缩参数.</li><li>注意: shrinkage只适用’lsqr’和’eigen’求解器.</li></ul></li><li><p>n_components(int, default&#x3D;None): 降维时要保留的特征数(&lt;&#x3D; min(n_classes - 1, n_features)).如果是 None, 则会设置为<code>min(n_classes - 1, n_features)</code>.</p></li></ul><p>实际使用时需要先调用 <code>fit(X, y)</code> 使用X, y拟合模型, 再调用<code>transform(X)</code>对X进行LDA降维.</p><p>下面是用LDA将鸢尾花数据降为二维的例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</span><br><span class="line"><span class="keyword">from</span> sklearn.discriminant_analysis <span class="keyword">import</span> LinearDiscriminantAnalysis</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入鸢尾花数据</span></span><br><span class="line">iris = datasets.load_iris()</span><br><span class="line">X = iris.data</span><br><span class="line">y = iris.target</span><br><span class="line">target_names = iris.target_names</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行LDA降维</span></span><br><span class="line">lda = LinearDiscriminantAnalysis(n_components=<span class="number">2</span>)</span><br><span class="line">X_r = lda.fit(X, y).transform(X)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绘制降维后的数据图</span></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line"><span class="keyword">for</span> i, target_name <span class="keyword">in</span> <span class="built_in">zip</span>(<span class="built_in">range</span>(<span class="number">3</span>), target_names):</span><br><span class="line">    ax.scatter(X_r[y==i, <span class="number">0</span>], X_r[y==i, <span class="number">1</span>], label=target_name, alpha=<span class="number">0.8</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.title(<span class="string">&#x27;LDA of IRIS dataset&#x27;</span>)</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/sklearn/sklearn08.svg" alt="sklearn08"></p><!-- ## 多维标度分析(MDS)### 简述多维标度分析(multidimensional scaling, MDS)是非常传统的降维的方法, 以距离为标准, 将高维坐标中的点投影到低维坐标中, 持彼此之间的相对距离变化最小, 更新的方法是T-SNE, 基于分布概率变化最小进行投影.### 原理1. 计算原始空间中数据点的距离矩阵.2. 计算内积矩阵.3. 对内积矩阵进行特征值分解, 获得特征值矩阵和特征向量矩阵.4. 取特征值矩阵最大的前 Z 项及其对应的特征向量### 优缺点优点: --><h2 id="未完待续"><a href="#未完待续" class="headerlink" title="未完待续"></a>未完待续</h2><blockquote><p>后续其他降维方法日后补充.</p></blockquote><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>[1] <a href="http://vis.pku.edu.cn/blog/%e9%99%8d%e7%bb%b4%e4%b8%ad%e7%9a%84%e5%8f%af%e8%a7%86%e4%ba%a4%e4%ba%92%ef%bc%9a%e4%b8%80%e4%b8%aa%e7%bb%93%e6%9e%84%e5%8c%96%e7%9a%84%e6%96%87%e7%8c%ae%e5%88%86%e6%9e%90-visual-interaction-with-dim/#more-5180">降维中的可视交互：一个结构化的文献分析 (Visual Interaction with Dimensionality Reduction: A Structured Literature Analysis) | PKU Visualization Blog</a></em><br><em>[2] <a href="https://github.com/apachecn/AiLearning/blob/master/docs/ml/13.%E5%88%A9%E7%94%A8PCA%E6%9D%A5%E7%AE%80%E5%8C%96%E6%95%B0%E6%8D%AE.md">AiLearning&#x2F;13.利用PCA来简化数据.md at master · apachecn&#x2F;AiLearning</a></em><br><em>[3] <a href="https://blog.csdn.net/u013719780/article/details/78352262">从零开始实现主成分分析(PCA)算法_风雪夜归子-CSDN博客_pca算法</a></em><br><em>[4] <a href="https://www.cnblogs.com/pinard/p/6244265.html">线性判别分析LDA原理总结 - 刘建平Pinard - 博客园</a></em><br><em>[5] <a href="https://scikit-learn.org/stable/modules/decomposition.html#pca">2.5. Decomposing signals in components (matrix factorization problems) &mdash; scikit-learn 0.23.1 documentation</a></em></p>]]></content>
    
    
    <summary type="html">详细介绍使用python的scikit-learn库实现各种降维算法, 包括PCA, LDA等.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="sklearn" scheme="https://zzz5.github.io/tags/sklearn/"/>
    
  </entry>
  
  <entry>
    <title>使用scikit-learn实现决策树</title>
    <link href="https://zzz5.github.io/2020/06/30/python/scikit-learn/sklearn-decision%20tree/"/>
    <id>https://zzz5.github.io/2020/06/30/python/scikit-learn/sklearn-decision%20tree/</id>
    <published>2020-06-30T03:01:00.000Z</published>
    <updated>2020-07-21T04:31:50.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="什么是决策树"><a href="#什么是决策树" class="headerlink" title="什么是决策树?"></a>什么是决策树?</h2><p>决策树(Decision Trees)一般都是自上而下的来生成的. 每个决策或事件(即自然状态)都可能引出两个或多个事件, 导致不同的结果, 把这种决策分支画成图形很像一棵树的枝干, 故称决策树. </p><p>决策树是一种用来分类和回归的无参监督学习方法, 其目的是创建一种模型从数据特征中学习简单的决策规则来预测一个目标变量的值.</p><h2 id="决策树的优缺点"><a href="#决策树的优缺点" class="headerlink" title="决策树的优缺点"></a>决策树的优缺点</h2><p>决策树的优点:</p><ol><li>便于理解和解释, 树的结构可以可视化出来.</li><li>训练需要的数据少.</li><li>训练树模型的时间复杂度是$O(log_2n)$.</li><li>能够处理离散值也可以处理连续值.</li><li>能够处理多路输出的问题.</li><li>使用白盒模型.</li><li>可以通过数值统计测试来验证该模型.</li><li>即使该模型假设的结果与真实模型所提供的数据有些违反, 其表现依旧良好.</li></ol><p>决策树的缺点:</p><ol><li>决策树模型容易产生一个过于复杂的模型, 这样的模型对数据的泛化性能会很差, 即过拟合. 这个问题可以通过一些策略像剪枝, 设置叶节点所需的最小样本数或设置树的最大深度来避免.</li><li>决策树可能是不稳定的, 因为数据中的微小变化可能会导致完全不同的树生成. 这个问题可以通过决策树的集成来得到缓解.</li><li>在多方面性能最优和简单化概念的要求下, 学习一棵最优决策树通常是一个NP-Complete问题. 因此, 实际的决策树学习算法是基于启发式算法, 例如在每个节点进行局部最优决策的贪心算法. 这样的算法不能保证返回全局最优决策树. 这个问题可以通过集成学习来训练多棵决策树来缓解, 这多棵决策树一般通过对特征和样本有放回的随机采样来生成.</li><li>有些概念很难被决策树学习到, 因为决策树很难清楚的表述这些概念.</li><li>如果某些类在问题中占主导地位会使得创建的决策树有偏差. 因此, 建议在拟合前先对数据集进行平衡.</li></ol><h2 id="scikit-learn决策树算法类库简单介绍"><a href="#scikit-learn决策树算法类库简单介绍" class="headerlink" title="scikit-learn决策树算法类库简单介绍"></a>scikit-learn决策树算法类库简单介绍</h2><p>scikit-learn决策树算法类库内部实现是使用了调优过的CART树算法, 既可以做分类, 又可以做回归. 分类决策树的类对应的是DecisionTreeClassifier, 而回归决策树的类对应的是DecisionTreeRegressor. 两者的参数定义几乎完全相同, 但是意义不全相同. </p><h2 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h2><h3 id="简单分类"><a href="#简单分类" class="headerlink" title="简单分类"></a>简单分类</h3><p>DecisionTreeClassifier 是能够在数据集上执行多分类的类, 与其他分类器一样, DecisionTreeClassifier 采用输入两个数组: 数组X, 用 [n_samples, n_features] size的数据来储存训练样本. 整数值数组Y, 用 [n_samples] 来保存训练样本的类标签.</p><p>下面简单展示一下如何按照给身高分类:</p><p>我们简单给出一组身高数据, 用0代表女性, 1代表男性, 分别用 0, 1指代低, 高两类身高(简单以165和175为一个界限, 不代表个人观点).</p><p>数据如下:</p><table><thead><tr><th>性别</th><th>身高</th><th>分类</th><th>性别</th><th>身高</th><th>分类</th></tr></thead><tbody><tr><td>0</td><td>158</td><td>0</td><td>1</td><td>165</td><td>0</td></tr><tr><td>0</td><td>160</td><td>0</td><td>1</td><td>171</td><td>0</td></tr><tr><td>0</td><td>156</td><td>0</td><td>1</td><td>180</td><td>1</td></tr><tr><td>0</td><td>165</td><td>1</td><td>1</td><td>178</td><td>1</td></tr><tr><td>0</td><td>171</td><td>1</td><td>1</td><td>168</td><td>0</td></tr><tr><td>0</td><td>149</td><td>0</td><td>1</td><td>172</td><td>0</td></tr><tr><td>0</td><td>168</td><td>1</td><td>1</td><td>177</td><td>1</td></tr><tr><td>0</td><td>170</td><td>1</td><td>1</td><td>182</td><td>1</td></tr><tr><td>0</td><td>162</td><td>0</td><td>1</td><td>162</td><td>0</td></tr></tbody></table><p>在建立决策树之前, 要先对数据进行预处理, 使X中的每一个特征都大概在一个数据范围</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">X = [[<span class="number">0</span>, <span class="number">158</span>], [<span class="number">0</span>, <span class="number">160</span>], [<span class="number">0</span>, <span class="number">156</span>], [<span class="number">0</span>, <span class="number">165</span>], [<span class="number">0</span>, <span class="number">171</span>], [<span class="number">0</span>, <span class="number">149</span>], [<span class="number">0</span>, <span class="number">168</span>], [<span class="number">0</span>, <span class="number">170</span>], [<span class="number">0</span>, <span class="number">162</span>], [<span class="number">1</span>, <span class="number">165</span>], [<span class="number">1</span>, <span class="number">171</span>], [<span class="number">1</span>, <span class="number">180</span>], [<span class="number">1</span>, <span class="number">178</span>], [<span class="number">1</span>, <span class="number">168</span>], [<span class="number">1</span>, <span class="number">172</span>], [<span class="number">1</span>, <span class="number">177</span>], [<span class="number">1</span>, <span class="number">182</span>], [<span class="number">1</span>, <span class="number">162</span>]]</span><br><span class="line">y = [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 对身高数据进行预处理, 使其与性别数据大小差不多</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> X:</span><br><span class="line">    i[<span class="number">1</span>] = i[<span class="number">1</span>] / <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 建立并训练决策树</span></span><br><span class="line">clf = tree.DecisionTreeClassifier()</span><br><span class="line">clf = clf.fit(X, y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 预测结果</span></span><br><span class="line">clf.predict([[<span class="number">0</span>, <span class="number">1.55</span>], [<span class="number">0</span>, <span class="number">1.67</span>], [<span class="number">1</span>, <span class="number">1.67</span>], [<span class="number">1</span>, <span class="number">1.79</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出结果为:</span></span><br><span class="line">array([<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>])</span><br></pre></td></tr></table></figure><h3 id="可视化"><a href="#可视化" class="headerlink" title="可视化"></a>可视化</h3><p>我们可以使用 <code>export_graphviz</code> 导出器以 <code>Graphviz</code> 格式导出决策树, 关于 <code>Graphviz</code> 可以参见我的上一篇文章 <a href="https://zzz5.xyz/2020/06/25/others/graphviz/">使用Graphviz(dot命令)绘制流程图</a>.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dot_data = tree.export_graphviz(clf, out_file=<span class="literal">None</span>)</span><br></pre></td></tr></table></figure><p>此时我们就获得了该决策树的Graphviz图数据, 将其写入.gv或者.dot文件即可:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;test.gv&quot;</span>, <span class="string">&#x27;w&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(dot_data)</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/sklearn/sklearn02.png" alt="sklearn02"></p><p>输出的Graphviz图像如下:</p><p><img src="/assets/blogImg/sklearn/sklearn03.svg" alt="sklearn03"></p><p><code>export_graphviz</code> 还支持各种美化, 包括通过他们的类着色节点(或回归值), 如果需要, 还能使用显式变量和类名.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">dot_data = tree.export_graphviz(clf, out_file=<span class="literal">None</span>,</span><br><span class="line">                     feature_names=[<span class="string">&#x27;Sex&#x27;</span>, <span class="string">&#x27;Height&#x27;</span>],  </span><br><span class="line">                     class_names=[<span class="string">&#x27;Short&#x27;</span>, <span class="string">&#x27;Tall&#x27;</span>],  </span><br><span class="line">                     filled=<span class="literal">True</span>, rounded=<span class="literal">True</span>,  </span><br><span class="line">                     special_characters=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>输出的Graphviz图如下:</p><p><img src="/assets/blogImg/sklearn/sklearn04.svg" alt="sklearn04"></p><blockquote><p>tips: 除了图像外, 还可以用 <code>export_text</code> 导出器导出文字的描述, 可以自己尝试一下.</p></blockquote><h3 id="scikit-learn自带的鸢尾花数据"><a href="#scikit-learn自带的鸢尾花数据" class="headerlink" title="scikit-learn自带的鸢尾花数据"></a>scikit-learn自带的鸢尾花数据</h3><p>我们使用scikot-learn自带的鸢尾花(Lris)数据集, 可以构造一个决策树.<br>如下所示:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.datasets <span class="keyword">import</span> load_iris</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> tree</span><br><span class="line">iris = load_iris()</span><br><span class="line">clf = tree.DecisionTreeClassifier()</span><br><span class="line">clf = clf.fit(iris.data, iris.target)</span><br></pre></td></tr></table></figure><p>可以使用同样的方法导出Graphviz图像:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">dot_data = tree.export_graphviz(clf, out_file=<span class="literal">None</span>,</span><br><span class="line">                    feature_names=iris.feature_names,  </span><br><span class="line">                    class_names=iris.target_names,  </span><br><span class="line">                    filled=<span class="literal">True</span>, rounded=<span class="literal">True</span>,  </span><br><span class="line">                    special_characters=<span class="literal">True</span>)</span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;iris.gv&quot;</span>, <span class="string">&#x27;w&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(dot_data)</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/sklearn/sklearn05.svg" alt="sklearn05"></p><h2 id="回归"><a href="#回归" class="headerlink" title="回归"></a>回归</h2><p>决策树通过使用 DecisionTreeRegressor 类来解决回归问题.<br>实际用法和分类问题差不多, 但是需要注意的是输入的y值和输出的预测值都是float而非int.</p><h2 id="一些需要注意的重要参数"><a href="#一些需要注意的重要参数" class="headerlink" title="一些需要注意的重要参数"></a>一些需要注意的重要参数</h2><p>大部分参数使用默认值即可, 但是有些参数在必要的时候还是需要更改:</p><table><thead><tr><th>参数</th><th>描述</th></tr></thead><tbody><tr><td>splitter(特征划分点选择标准)</td><td>可以使用 “best” 或者”random”. <br>前者在特征的所有划分点中找出最优的划分点; 后者是随机的在部分划分点中找局部最优的划分点. <br>默认的”best”适合样本量不大的时候, 而如果样本数据量非常大, 此时决策树构建推荐”random”</td></tr><tr><td>max_features(划分时考虑的最大特征数)</td><td>可以使用很多种类型的值, 默认是”None”, 意味着划分时考虑所有的特征数.<br>如果是”log2”意味着划分时最多考虑$log_2n$个特征; 如果是”sqrt”或者”auto”意味着划分时最多考虑$\sqrt{n}$个特征.<br>如果是整数, 代表考虑的特征绝对数. 如果是浮点数, 代表考虑特征百分比数的特征. 其中n为样本总特征数.<br>一般来说, 如果样本特征数不多, 比如小于50, 我们用默认的”None”就可以了;<br>如果特征数非常多, 我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数, 以控制决策树的生成时间.</td></tr><tr><td>max_depth(决策树最大深度)</td><td>决策树的最大深度, 默认可以不输入, 如果不输入的话, 决策树在建立子树的时候不会限制子树的深度.<br>一般来说，数据少或者特征少的时候可以不管这个值.<br>如果模型样本量多, 特征也多的情况下, 推荐限制这个最大深度, 具体的取值取决于数据的分布.<br>常用的可以取值10-100之间.</td></tr><tr><td>min_samples_split(内部节点再划分所需最小样本数)</td><td>这个值限制了子树继续划分的条件, 如果某节点的样本数少于min_samples_split, 不会继续再尝试选择最优特征来进行划分.<br>默认是2. 如果样本量不大, 不需要管这个值. 如果样本量数量级非常大, 则推荐增大这个值.</td></tr><tr><td>min_samples_leaf(叶子节点最少样本数)</td><td>这个值限制了叶子节点最少的样本数, 如果某叶子节点数目小于样本数, 则会和兄弟节点一起被剪枝.<br>默认是1, 可以输入最少的样本数的整数, 或者最少样本数占样本总数的百分比.<br>如果样本量不大, 不需要管这个值. 如果样本量数量级非常大，则推荐增大这个值.</td></tr><tr><td>max_leaf_nodes(最大叶子节点数)</td><td>通过限制最大叶子节点数, 可以防止过拟合, 默认是”None”, 即不限制最大的叶子节点数.<br>如果加了限制, 算法会建立在最大叶子节点数内最优的决策树.<br>如果特征不多, 可以不考虑这个值, 但是如果特征分成多的话, 可以加以限制, 具体的值可以通过交叉验证得到.</td></tr><tr><td>class_weight(类别权重)</td><td>指定样本各类别的的权重, 主要是为了防止训练集某些类别的样本过多, 导致训练的决策树过于偏向这些类别.<br>这里可以自己指定各个样本的权重, 或者用”balanced”, 如果使用“balanced”, 则算法会自己计算权重, 样本量少的类别所对应的样本权重会高.<br>当然, 如果你的样本类别分布没有明显的偏倚, 则可以不管这个参数, 选择默认的”None”</td></tr></tbody></table><h2 id="实际使用技巧和注意事项"><a href="#实际使用技巧和注意事项" class="headerlink" title="实际使用技巧和注意事项"></a>实际使用技巧和注意事项</h2><ol><li>当样本少数量但是样本特征非常多的时候, 决策树很容易过拟合. 一般来说, 样本数比特征数多一些会比较容易建立健壮的模型.</li><li>如果样本数量少但是样本特征非常多, 在拟合决策树模型前, 推荐先做降维, 比如主成分分析(PCA), 线性判别分析(LDA), 特征选择(Losso)等. 这样特征的维度会大大减小, 再来拟合决策树模型效果会好.</li><li>使用 <code>max_depth=3</code> 作为初始树深度, 先观察下生成的决策树里数据的初步拟合情况, 然后再决定是否要增加深度.</li><li>通过使用 <code>min_samples_split</code> 和 <code>min_samples_leaf</code> 来控制叶节点上的样本数量. 当这个值很小时意味着生成的决策树很可能会过拟合, 然而当这个值很大时将会不利于决策树的对样本的学习. 所以尝试 <code>min_samples_leaf=5</code> 作为初始值. 如果样本的变化量很大, 可以使用浮点数作为这两个参数中的百分比.</li><li>在训练模型前, 注意观察样本的类别情况(主要指分类树), 如果类别分布非常不均匀, 就要考虑用class_weight来限制模型过于偏向样本多的类别.</li><li>所有的决策树内部使用 np.float32 数组, 如果训练数据不是这种格式, 将会复制数据集再进行运算.</li></ol><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>[1] <a href="https://scikit-learn.org/stable/modules/tree.html">1.10. Decision Trees &mdash; scikit-learn 0.23.1 documentation</a></em><br><em>[2] <a href="https://wiki.mbalib.com/wiki/%E5%86%B3%E7%AD%96%E6%A0%91">决策树 - MBA智库百科</a></em><br><em>[3] <a href="https://www.cnblogs.com/pinard/p/6056319.html">scikit-learn决策树算法类库使用小结 - 刘建平Pinard - 博客园</a></em></p>]]></content>
    
    
    <summary type="html">详细介绍使用python的scikit-learn库实现决策树算法以及决策树可视化.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="sklearn" scheme="https://zzz5.github.io/tags/sklearn/"/>
    
  </entry>
  
  <entry>
    <title>使用Graphviz(dot命令)绘制流程图</title>
    <link href="https://zzz5.github.io/2020/06/25/others/graphviz/"/>
    <id>https://zzz5.github.io/2020/06/25/others/graphviz/</id>
    <published>2020-06-25T14:01:00.000Z</published>
    <updated>2020-07-12T01:38:40.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="安装Graphviz"><a href="#安装Graphviz" class="headerlink" title="安装Graphviz"></a>安装Graphviz</h2><p>在Graphviz官网下载<a href="http://graphviz.org/download/">各个操作系统的Graphviz</a>.<br>安装后再将安装目录的<code>graphviz\bin</code>加入环境变量<code>PATH</code>里.<br>比如我的安装路径为<code>C:\Program Files (x86)\Graphviz</code>, 我就在环境变量中的<code>PATH</code>里添加<code>C:\Program Files (x86)\Graphviz\bin</code>.<br>打开<code>cmd</code>输入<code>dot -V</code>, 若显示graphviz的版本即表示配置成功. 我这里显示的<code>dot - graphviz version 2.38.0 (20140413.2041)</code></p><h2 id="配置Graphviz编辑器"><a href="#配置Graphviz编辑器" class="headerlink" title="配置Graphviz编辑器"></a>配置Graphviz编辑器</h2><p>安装Graphviz时, 有些安装包会默认安装一个简易的编辑器gvedit, 可以实现简单的可视化. 但并没有代码补全等功能.<br>这里推荐使用vscoede配合插件(Graphviz (dot) language support for Visual Studio Code)进行编辑, 可以实现代码补全, 提示和实时的图像.</p><p><img src="/assets/blogImg/graphviz/graphviz01.gif" alt="graphviz01"></p><p>当然你也可以直接在cmd界面输出图像, <code>dot -T[输出图像后缀名] [后缀为gv的文件名] -o [输出图像文件名]</code></p><h2 id="图形类别"><a href="#图形类别" class="headerlink" title="图形类别"></a>图形类别</h2><h3 id="无向图"><a href="#无向图" class="headerlink" title="无向图"></a>无向图</h3><p>在最简单的应用中, DOT语言可以用来描述一张无向图. 无向图显示了对象间最简单的关系. 使用关键字<code>graph</code>开始一张无向图的定义, 并用大括号包含要描述的节点. 双连字号(–)被用来描述节点间的关系. 另外, 一行的末尾需要加上分号(;).</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">graph graphname &#123;</span><br><span class="line">    a -- b -- c;</span><br><span class="line">    b -- d;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz02.svg" alt="graphviz02"></p><h3 id="有向图"><a href="#有向图" class="headerlink" title="有向图"></a>有向图</h3><p>类似于无向图, DOT语言也可以用来描述一张有向图, 类似于流程图和树状图. 其语法与无向图相似, 但要在图的最开始使用关键字<code>digraph</code>, 并用箭头（-&gt;）表示节点直接的关系.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">digraph graphname &#123;</span><br><span class="line">    a-&gt;b-&gt;c;</span><br><span class="line">    c-&gt;a;</span><br><span class="line">    b-&gt;d;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz03.svg" alt="graphviz03"></p><h3 id="子图"><a href="#子图" class="headerlink" title="子图"></a>子图</h3><p>字体 <code>subgraph</code> 的作用主要有 3 个:</p><ol><li>表示图的结构, 对节点和边进行分组.</li><li>提供一个单独的上下位文设置属性.</li><li>针对特定引擎使用特殊的布局. 比如下面的例子, 如果 subgraph 的名字以 cluster 开头, 所有属于这个子图的节点会用一个矩形和其他节点分开.</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">digraph graphname&#123; </span><br><span class="line">    a -&gt; &#123;b c&#125;;</span><br><span class="line">    c -&gt; e;</span><br><span class="line">    b -&gt; d;</span><br><span class="line"></span><br><span class="line">    subgraph cluster_bc &#123;</span><br><span class="line">        bgcolor=red;</span><br><span class="line">        b;</span><br><span class="line">        c;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    subgraph cluster_de &#123;</span><br><span class="line">        label=&quot;Block&quot;</span><br><span class="line">        d;</span><br><span class="line">        e;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz04.svg" alt="graphviz04"></p><h2 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h2><h3 id="节点形状"><a href="#节点形状" class="headerlink" title="节点形状"></a>节点形状</h3><p>节点的默认属性为 <code>shape=ellipse, width=.75, height=.5</code>, 标签默认为节点名.<br>节点又分为基于多边形的节点和基于记录的节点.</p><h4 id="基于多边形的节点"><a href="#基于多边形的节点" class="headerlink" title="基于多边形的节点"></a>基于多边形的节点</h4><p>基于多边形的节点还有 <code>box, polygon, oval, circle, point, egg, triangle, plaintext, plain, diamond, trapezium, parallelogram, house, pentagon, hexagon, septagon, octagon, doublecircle, doubleoctagon, tripleoctagon, invtriangle, invtrapezium,invhouse, Mdiamond, Msquare, Mcircle, rect, rectangle, square, star, none, underline, cylinder, note, tab, folder, box3d, component, promoter, cds, terminator, utr, primersite, restrictionsite, fivepoverhang, threepoverhang,noverhang, assembly, signature, insulator, ribosite, rnastab, proteasesite, proteinstab, rpromoter, rarrow, larrow, lpromoter</code>. 其中很大一部分形状都可以由属性<code>sides</code>设置边的数目, 而设置<code>regular=true</code>, 节点形状会强制规整, 如正三角形.</p><blockquote><p>tips: 除非给定 <code>fixedsize=true</code>, 不然节点的大小还是会根据实际情况(如标签长度等)而改变.</p></blockquote><p>具体形状如下图:</p><p><img src="/assets/blogImg/graphviz/graphviz05.svg" alt="graphviz05"></p><h4 id="基于记录的节点"><a href="#基于记录的节点" class="headerlink" title="基于记录的节点"></a>基于记录的节点</h4><p>基于记录的节点主要是<code>shape=record</code>和<code>shape=Mrecord</code>两种.<br>基于记录的节点的结构主要是由<code>label</code>属性决定的, 基本结构如下例:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">digraph structs &#123;</span><br><span class="line">    name [shape=record, label=&quot;a | &#123; b |&lt;subname1&gt; c | d &#125; | &lt;subname2&gt; e&quot;];</span><br><span class="line">    name:subname2 -&gt; f;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz06.svg" alt="graphviz06"></p><p>其中<code>name</code>为节点名; <code>subname</code>为节点内次节点的标志, 主要用来作为边的起点或终点; <code>|</code>用来分隔文本; <code>&#123;&#125;</code> 会使得括号内结构与外部方向相反.</p><p>举一个更详细的例子:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">digraph structs &#123;</span><br><span class="line">    node [shape=record];</span><br><span class="line">    struct1 [label=&quot;&lt;f0&gt; left|&lt;f1&gt; mid&amp;#92; dle|&lt;f2&gt; right&quot;];</span><br><span class="line">    struct2 [label=&quot;&lt;f0&gt; one|&lt;f1&gt; two&quot;];</span><br><span class="line">    struct3 [label=&quot;hello&amp;#92;nworld |&#123; b |&#123;c|&lt;here&gt; d|e&#125;| f&#125;| g | h&quot;];</span><br><span class="line">    struct1:f1 -&gt; struct2:f0;</span><br><span class="line">    struct1:f2 -&gt; struct3:here;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz07.svg" alt="graphviz07"></p><p>想要了解更多, 可以参考 <a href="http://www.graphviz.org/doc/info/shapes.html">官方文档</a>.</p><h3 id="箭头形状"><a href="#箭头形状" class="headerlink" title="箭头形状"></a>箭头形状</h3><p>关于箭头形状, 这里就不详细讨论了, 详情可以参考 <a href="http://www.graphviz.org/doc/info/arrows.html">官方文档对箭头的说明</a>.</p><h3 id="节点和边的属性"><a href="#节点和边的属性" class="headerlink" title="节点和边的属性"></a>节点和边的属性</h3><p>节点和边的属性挺多的, 但是平常很少用到, 这里不详细展开了, 有兴趣可以参考 <a href="http://www.graphviz.org/doc/info/attrs.html">官方文档对节点和边属性的说明</a>.</p><h3 id="标签"><a href="#标签" class="headerlink" title="标签"></a>标签</h3><p>之前已经提到过了, 节点默认的标签是节点名, 边默认是没有标签的, 事实上节点和边都可以使用<code>label</code>属性来单独设置标签. </p><p>虽然看起来用<code>name</code>来设置标签很方便, 但是很多时候我们会遇到重复的标签, 或者不能用于<code>name</code>的字符, 这种时候就需要用到<code>label</code>属性了.</p><p><code>label</code>属性支持逃逸字符, 同时, 也可以使用 <code>labelloc</code> 属性来指定标签的位置, 可选的位置有<code>t</code>, 上部(top); <code>b</code>, 底部(bottom). 还可以使用 <code>labeljust</code> 属性决定字符左对齐还是右对齐, 左对齐: <code>l</code>; 右对齐: <code>r</code>.</p><p><code>label</code> 默认的字号是14, 字体为 Times-Roman, 黑色. 这些都可以通过 <code>fontsize</code>, <code>fontname</code>, <code>fontcolor</code> 进行修改. </p><p>节点只有一种标签, 而边有三种标签, 一种是在边头的<code>headlabel</code>, 一种是在边尾的<code>taillabel</code>, 还有在边中的<code>label</code>.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">digraph&#123;</span><br><span class="line">    a[shape=box, label=&quot;你好，世界&quot;, fontname=&quot;SimHei&quot;, fontsize=18, fontcolor=&quot;red&quot;, labelloc=&quot;t&quot;];</span><br><span class="line">    a -&gt; b [headlabel=&quot;I&#x27;m headlabel&quot;, label=&quot;I&#x27;m label&quot;, taillabel=&quot;I&#x27;m taillabel&quot;]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz08.svg" alt="graphviz08"></p><p>除此之外, 标签还支持 <code>html</code> 语言, 例如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">digraph html &#123;</span><br><span class="line">    abc [shape=none, margin=0, label=&lt;</span><br><span class="line">        &lt;TABLE BORDER=&quot;0&quot; CELLBORDER=&quot;1&quot; CELLSPACING=&quot;0&quot; CELLPADDING=&quot;4&quot;&gt;</span><br><span class="line">            &lt;TR&gt;&lt;TD ROWSPAN=&quot;3&quot;&gt;&lt;FONT COLOR=&quot;red&quot;&gt;hello&lt;/FONT&gt;&lt;BR/&gt;world&lt;/TD&gt;</span><br><span class="line">                &lt;TD COLSPAN=&quot;3&quot;&gt;b&lt;/TD&gt;</span><br><span class="line">                &lt;TD ROWSPAN=&quot;3&quot; BGCOLOR=&quot;lightgrey&quot;&gt;g&lt;/TD&gt;</span><br><span class="line">                &lt;TD ROWSPAN=&quot;3&quot;&gt;h&lt;/TD&gt;</span><br><span class="line">            &lt;/TR&gt;</span><br><span class="line">            &lt;TR&gt;</span><br><span class="line">                &lt;TD&gt;c&lt;/TD&gt;</span><br><span class="line">                &lt;TD PORT=&quot;here&quot;&gt;d&lt;/TD&gt;</span><br><span class="line">                &lt;TD&gt;e&lt;/TD&gt;</span><br><span class="line">            &lt;/TR&gt;</span><br><span class="line">            &lt;TR&gt;</span><br><span class="line">            &lt;TD COLSPAN=&quot;3&quot;&gt;f&lt;/TD&gt;</span><br><span class="line">            &lt;/TR&gt;</span><br><span class="line">        &lt;/TABLE&gt;</span><br><span class="line">    &gt;];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz09.svg" alt="graphviz09"></p><h3 id="颜色"><a href="#颜色" class="headerlink" title="颜色"></a>颜色</h3><p><a href="http://www.graphviz.org/doc/info/colors.html">官方给出的颜色表</a>.</p><h2 id="布局"><a href="#布局" class="headerlink" title="布局"></a>布局</h2><p>默认情况下图是从上到下布局的, <code>rankdir</code>属性 设置图形布局的排列方向(全局只有一个生效). “TB”, “LR”, “BT”, “RL”, 分别对应于从上到下，从左到右，从下到上和从右到左绘制的有向图.</p><p>还可以通过设置<code>rank</code>属性给节点排序, 最小等级是最顶部或最左侧, 最大等级是最底部或最右侧, 可选属性有:</p><ul><li>same: 所有节点都位于同一等级</li><li>min source: 所有节点都位于最小等级上</li><li>max sink: 所有节点都位于最大等级上.</li></ul><p>例如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">digraph example &#123;</span><br><span class="line"></span><br><span class="line">    2017[shape=plaintext];</span><br><span class="line">    2018[shape=plaintext];</span><br><span class="line">    2019[shape=plaintext];</span><br><span class="line">    2020[shape=plaintext];</span><br><span class="line"></span><br><span class="line">    2017 -&gt; 2018 -&gt; 2019 -&gt; 2020;</span><br><span class="line">    201701 -&gt; 201702 -&gt; 201703;</span><br><span class="line">    201801 -&gt; 201802 -&gt; 201803;</span><br><span class="line">    201901 -&gt; 201902 -&gt; 201903;</span><br><span class="line">    202001 -&gt; 202002;</span><br><span class="line"></span><br><span class="line">    &#123; rank=min; 2018; 201801; 201802; 201803&#125;;</span><br><span class="line">    &#123; rank=same; 2019; 201901; 201902; 201903&#125;;</span><br><span class="line">    &#123; rank=max; 2017; 201701; 201702; 201703&#125;;</span><br><span class="line">    &#123; rank=sink; 2020; 202001; 202002&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/graphviz/graphviz10.svg" alt="graphviz10"></p><h2 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h2><h3 id="自动生成-python-UML图"><a href="#自动生成-python-UML图" class="headerlink" title="自动生成 python UML图"></a>自动生成 python UML图</h3><p>Pyreverse工具就是基于Graphiviz的自动生成UML图的工具. pylint中集成了该工具.</p><p>通过命令pyreverse -ASmy -o gv test.py就可以在当前目录下生成UML类图文件.</p><ul><li>-o：指定输出的图形格式</li><li>test.py: 指定要生成类图的源码文件, 也可以是目录</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>总觉得自己教程写得挺烂的, 推荐一篇大神参考官方文档写的教程: <a href="https://www.cnblogs.com/shuqin/p/11897207.html">Graphviz 画图的一些总结</a>.</p><p>平常绘制流程图, 我还是推荐使用 <a href="https://app.diagrams.net/">draw.io</a>.</p><p>……</p><blockquote><p>虽然在最后说有点问题, 但我还是想说, <a href="https://github.com/mermaid-js/mermaid">mermaid</a>其实也是挺好用的, 更美观一些, 不过二者优劣势还是挺明显的, 可以看情况选择使用哪一个.</p></blockquote><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>1. <a href="http://www.graphviz.org/pdf/dotguide.pdf">Emden R. Gansner and Eleftherios Koutsofios and Stephen North. Drawing graphs with dot. January 5, 2015.</a></em><br><em>2. <a href="https://zhuanlan.zhihu.com/p/21993254">lfyzjck. Graphviz 入门指南 - 知乎. 2017.</a></em><br><em>3. <a href="http://www.graphviz.org/documentation/">Graphviz 官方文档</a></em></p>]]></content>
    
    
    <summary type="html">dot命令是Graphviz的一部分, 通过编写一些类似脚本的语言, 可以容易地生成流程图.</summary>
    
    
    
    <category term="杂七杂八" scheme="https://zzz5.github.io/categories/%E6%9D%82%E4%B8%83%E6%9D%82%E5%85%AB/"/>
    
    
    <category term="Graphviz" scheme="https://zzz5.github.io/tags/Graphviz/"/>
    
  </entry>
  
  <entry>
    <title>使用Python进行统计假设检验</title>
    <link href="https://zzz5.github.io/2020/06/10/python/statistic/python-hypothesis%20testing/"/>
    <id>https://zzz5.github.io/2020/06/10/python/statistic/python-hypothesis%20testing/</id>
    <published>2020-06-10T03:07:00.000Z</published>
    <updated>2020-07-02T13:32:14.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="简单说明"><a href="#简单说明" class="headerlink" title="简单说明"></a>简单说明</h2><p>R是一个专门用于统计分析的编程语言, Python则是一个通用编程语言, 以统计相关的模块的方式进行统计分析. R比Python有更多的统计功能, 以及对统计更亲切的语法. 但无论如何, 当建立更复杂的工作流, 如与图像分析, 文本挖掘或者物理实验控制集成时, Python的通用性与相关模块就成了无价的财富.</p><h2 id="统计假设检验"><a href="#统计假设检验" class="headerlink" title="统计假设检验"></a>统计假设检验</h2><p>假设检验的基本思想是“小概率事件”原理, 其统计推断方法是带有某种概率性质的反证法. 小概率思想是指小概率事件在一次试验中基本上不会发生. 反证法思想是先提出检验假设, 再用适当的统计方法, 利用小概率原理, 确定假设是否成立. 即为了检验一个假设$H_0$是否正确, 首先假定该假设$H_0$正确, 然后根据样本对假设$H_0$做出接受或拒绝的决策. 如果样本观察值导致了“小概率事件”发生, 就应拒绝假设$H_0$, 否则应接受假设$H_0$.</p><h2 id="基本步骤"><a href="#基本步骤" class="headerlink" title="基本步骤"></a>基本步骤</h2><ol><li>提出检验假设又称无效假设, 符号是$H_0$; 备择假设的符号是$H_1$.<br>$H_0$: 样本与总体或样本与样本间的差异是由抽样误差引起的;<br>$H_1$: 样本与总体或样本与样本间存在本质差异;<br>预先设定的检验水准为0.05; 当检验假设为真, 但被错误地拒绝的概率, 记作$\alpha$, 通常取$\alpha&#x3D;0.05$或$\alpha&#x3D;0.01$.</li><li>选定统计方法, 由样本观察值按相应的公式计算出统计量的大小, 如$X_2$值, t值等. 根据资料的类型和特点, 可分别选用Z检验, T检验, 秩和检验和卡方检验等.</li><li>根据统计量的大小及其分布确定检验假设成立的可能性P的大小并判断结果. 若 $P&gt;\alpha$, 结论为按α所取水准不显著, 不拒绝H0, 即认为差别很可能是由于抽样误差造成的, 在统计上不成立; 如果 $P≤\alpha$, 结论为按所取α水准显著, 拒绝H0, 接受H1, 则认为此差别不大可能仅由抽样误差所致, 很可能是实验因素不同造成的, 故在统计上成立. P值的大小一般可通过查阅相应的界值表得到.</li><li>注意问题:</li></ol><ul><li>作假设检验之前, 应注意资料本身是否有可比性.</li><li>当差别有统计学意义时应注意这样的差别在实际应用中有无意义.</li><li>根据资料类型和特点选用正确的假设检验方法.</li><li>根据专业及经验确定是选用单侧检验还是双侧检验.</li><li>判断结论时不能绝对化, 应注意无论接受或拒绝检验假设, 都有判断错误的可能性.</li></ul><h2 id="t检验"><a href="#t检验" class="headerlink" title="t检验"></a>t检验</h2><p>t检验, 亦称student t检验(Student’s t test), 主要用于样本含量较小(例如 $n &lt; 30$), 总体标准差$\delta$未知的正态分布. t检验是用t分布理论来推论差异发生的概率, 从而比较两个平均数的差异是否显著.</p><h3 id="Python实现单样本t检验"><a href="#Python实现单样本t检验" class="headerlink" title="Python实现单样本t检验"></a>Python实现单样本t检验</h3><p>单样本t检验用于比较样本数据与一个特定数值之间是否的差异情况.</p><p>假设有这样一组数据(某省各城市的平均月工资):</p><table><thead><tr><th>City</th><th>AverageMonthlySalary</th></tr></thead><tbody><tr><td>A</td><td>9000</td></tr><tr><td>B</td><td>8800</td></tr><tr><td>C</td><td>8900</td></tr><tr><td>D</td><td>7500</td></tr><tr><td>E</td><td>8500</td></tr><tr><td>F</td><td>7600</td></tr><tr><td>G</td><td>8000</td></tr><tr><td>H</td><td>7800</td></tr><tr><td>I</td><td>8200</td></tr><tr><td>J</td><td>8300</td></tr></tbody></table><p>我们要检验其与全国月平均工资8829元是否存在显著差异.</p><p>假设:<br>$H_0$: 该省各城市的平均月工资与全国月平均工资相同<br>$H_1$: 该省各城市的平均月工资与全国月平均工资不同</p><p>选用双侧检验, 选用$\alpha&#x3D;0.01$的统计显著水平</p><h4 id="导入数据"><a href="#导入数据" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;salary.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data[<span class="string">&#x27;AverageMonthlySalary&#x27;</span>]</span><br><span class="line"><span class="number">0</span>    <span class="number">9000</span></span><br><span class="line"><span class="number">1</span>    <span class="number">8800</span></span><br><span class="line"><span class="number">2</span>    <span class="number">8900</span></span><br><span class="line"><span class="number">3</span>    <span class="number">7500</span></span><br><span class="line"><span class="number">4</span>    <span class="number">8500</span></span><br><span class="line"><span class="number">5</span>    <span class="number">7600</span></span><br><span class="line"><span class="number">6</span>    <span class="number">8000</span></span><br><span class="line"><span class="number">7</span>    <span class="number">7800</span></span><br><span class="line"><span class="number">8</span>    <span class="number">8200</span></span><br><span class="line"><span class="number">9</span>    <span class="number">8300</span></span><br><span class="line">Name: AverageMonthlySalary, dtype: int64</span><br></pre></td></tr></table></figure><h4 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">ax.bar(data[<span class="string">&#x27;City&#x27;</span>],data[<span class="string">&#x27;AverageMonthlySalary&#x27;</span>])</span><br><span class="line">ax.set_xlabel(<span class="string">&#x27;City&#x27;</span>)</span><br><span class="line">ax.set_ylabel(<span class="string">&#x27;Average Monthly Salary&#x27;</span>)</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic01.png" alt="statistic01"></p><h4 id="单样本t检验"><a href="#单样本t检验" class="headerlink" title="单样本t检验"></a>单样本t检验</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ttest_1samp.html#scipy.stats.ttest_1samp">scipy.stats.ttest_1samp(a, popmean)</a> 检验是样本数据与一个特定数值之间是否的差异情况. 它返回t值与p值.</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.ttest_1samp(data[<span class="string">&#x27;AverageMonthlySalary&#x27;</span>], <span class="number">8829</span>)</span><br><span class="line">Ttest_1sampResult(statistic=-<span class="number">3.3451302386251056</span>, pvalue=<span class="number">0.008589795741200947</span>)</span><br></pre></td></tr></table></figure><p>可以看到 $p&lt;0.01$, 说明统计结果达到显著意义, 接受$H_1$, 在$\alpha&#x3D;0.01$的统计显著水平下该省各城市的平均月工资与全国月平均工资不同.</p><h3 id="Python实现独立样本t检验"><a href="#Python实现独立样本t检验" class="headerlink" title="Python实现独立样本t检验"></a>Python实现独立样本t检验</h3><p>独立样本t检验用于分析定类数据(X)与定量数据(Y)之间的差异情况.</p><p>假设有这样一组数据(某公司男女身高数据):</p><table><thead><tr><th>Name</th><th>Gender</th><th>Height</th></tr></thead><tbody><tr><td>A</td><td>Female</td><td>164</td></tr><tr><td>B</td><td>Female</td><td>160</td></tr><tr><td>C</td><td>Male</td><td>166</td></tr><tr><td>D</td><td>Male</td><td>175</td></tr><tr><td>E</td><td>Female</td><td>170</td></tr><tr><td>F</td><td>Male</td><td>183</td></tr><tr><td>G</td><td>Male</td><td>176</td></tr><tr><td>H</td><td>Female</td><td>156</td></tr><tr><td>I</td><td>Male</td><td>168</td></tr><tr><td>J</td><td>Male</td><td>177</td></tr><tr><td>k</td><td>Female</td><td>161</td></tr></tbody></table><p>我们要检验该公司男性与女性的身高是否存在显著差异.</p><p>假设:<br>$H_0$: 该公司男性与女性的身高不存在显著差异<br>$H_1$: 该公司男性与女性的身高存在显著差异</p><p>选用双侧检验, 选用$\alpha&#x3D;0.01$的统计显著水平</p><h4 id="导入数据-1"><a href="#导入数据-1" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;salary.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">    NameGenderHeight</span><br><span class="line"><span class="number">0</span>AFemale<span class="number">164</span></span><br><span class="line"><span class="number">1</span>BFemale<span class="number">160</span></span><br><span class="line"><span class="number">2</span>CMale<span class="number">166</span></span><br><span class="line"><span class="number">3</span>DMale<span class="number">175</span></span><br><span class="line"><span class="number">4</span>EFemale<span class="number">170</span></span><br><span class="line"><span class="number">5</span>FMale<span class="number">183</span></span><br><span class="line"><span class="number">6</span>GMale<span class="number">176</span></span><br><span class="line"><span class="number">7</span>HFemale<span class="number">156</span></span><br><span class="line"><span class="number">8</span>IMale<span class="number">168</span></span><br><span class="line"><span class="number">9</span>JMale<span class="number">177</span></span><br><span class="line"><span class="number">10</span>kFemale<span class="number">161</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-1"><a href="#数据可视化-1" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">fig, axs = plt.subplots(<span class="number">1</span>,<span class="number">2</span>)</span><br><span class="line">data_male = data[data[<span class="string">&#x27;Gender&#x27;</span>] == <span class="string">&#x27;Male&#x27;</span>]</span><br><span class="line">data_female = data[data[<span class="string">&#x27;Gender&#x27;</span>] == <span class="string">&#x27;Female&#x27;</span>]</span><br><span class="line">axs[<span class="number">0</span>].bar(data_male[<span class="string">&#x27;Name&#x27;</span>], data_male[<span class="string">&#x27;Height&#x27;</span>], color=<span class="string">&#x27;b&#x27;</span>, label=<span class="string">&#x27;Male&#x27;</span>)</span><br><span class="line">axs[<span class="number">1</span>].bar(data_female[<span class="string">&#x27;Name&#x27;</span>], data_female[<span class="string">&#x27;Height&#x27;</span>], color=<span class="string">&#x27;r&#x27;</span>, label=<span class="string">&#x27;Female&#x27;</span>)</span><br><span class="line"><span class="keyword">for</span> ax <span class="keyword">in</span> axs:</span><br><span class="line">    ax.set_xlabel(<span class="string">&#x27;Name&#x27;</span>)</span><br><span class="line">    ax.set_ylabel(<span class="string">&#x27;Height(cm)&#x27;</span>)</span><br><span class="line">    ax.legend()</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic02.png" alt="statistic02"></p><h4 id="独立样本t检验"><a href="#独立样本t检验" class="headerlink" title="独立样本t检验"></a>独立样本t检验</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ttest_ind.html#scipy.stats.ttest_ind">scipy.stats.ttest_ind(a, b, equal_var)</a> 检验两样本之间差异的显著性. 它返回t值与p值. equal_var参数为bool类型, 是否等方差.</p></blockquote><p>当不确定两总体方差是否相等时, 应先利用levene检验, 检验两总体是否具有方差齐性：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.levene(data_male[<span class="string">&#x27;Height&#x27;</span>], data_female[<span class="string">&#x27;Height&#x27;</span>])</span><br><span class="line">LeveneResult(statistic=<span class="number">0.14705486820871433</span>, pvalue=<span class="number">0.7102672800386702</span>)</span><br></pre></td></tr></table></figure><p>p值远大于0.05, 认为两总体具有方差齐性.<br>如果两总体不具有方差齐性, 需要将equal_val参数设定为False.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.ttest_ind(data_male[<span class="string">&#x27;Height&#x27;</span>], data_female[<span class="string">&#x27;Height&#x27;</span>])</span><br><span class="line">Ttest_indResult(statistic=<span class="number">3.4023893451544494</span>, pvalue=<span class="number">0.007843582968708454</span>)</span><br></pre></td></tr></table></figure><p>可以看到 $p&lt;0.05$, 说明统计结果达到显著意义, 接受$H_1$, 在$\alpha&#x3D;0.01$的统计显著水平下该公司男性与女性的身高存在显著差异.</p><h3 id="Python实现配对样本t检验"><a href="#Python实现配对样本t检验" class="headerlink" title="Python实现配对样本t检验"></a>Python实现配对样本t检验</h3><p>配对样本t检验用于分析配对定量数据之间的差异对比关系. 与独立样本t检验相比, 配对样本t检验要求样本是配对的. 两个样本的样本量要相同, 样本先后的顺序是一一对应的.</p><p>假设有这样一组数据(某实验的前后数据):</p><table><thead><tr><th>Test</th><th>Before</th><th>After</th></tr></thead><tbody><tr><td>1</td><td>30</td><td>38</td></tr><tr><td>2</td><td>35</td><td>36</td></tr><tr><td>3</td><td>33</td><td>38</td></tr><tr><td>4</td><td>32</td><td>40</td></tr><tr><td>5</td><td>29</td><td>33</td></tr><tr><td>6</td><td>32</td><td>37</td></tr><tr><td>7</td><td>35</td><td>39</td></tr></tbody></table><p>我们要检验该实验是否对数据大小存在显著影响.</p><p>假设:<br>$H_0$: 该实验对数据大小不存在显著影响<br>$H_1$: 该实验对数据大小存在显著影响</p><p>选用双侧检验, 选用$\alpha&#x3D;0.01$的统计显著水平</p><h4 id="导入数据-2"><a href="#导入数据-2" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;test.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">    Test    BeforeAfter</span><br><span class="line"><span class="number">0</span><span class="number">1</span><span class="number">30</span><span class="number">38</span></span><br><span class="line"><span class="number">1</span><span class="number">2</span><span class="number">35</span><span class="number">36</span></span><br><span class="line"><span class="number">2</span><span class="number">3</span><span class="number">33</span><span class="number">38</span></span><br><span class="line"><span class="number">3</span><span class="number">4</span><span class="number">32</span><span class="number">40</span></span><br><span class="line"><span class="number">4</span><span class="number">5</span><span class="number">29</span><span class="number">33</span></span><br><span class="line"><span class="number">5</span><span class="number">6</span><span class="number">32</span><span class="number">37</span></span><br><span class="line"><span class="number">6</span><span class="number">7</span><span class="number">35</span><span class="number">39</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-2"><a href="#数据可视化-2" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">bar_width = <span class="number">0.35</span></span><br><span class="line">index = np.arange(<span class="built_in">len</span>(data))</span><br><span class="line">ax.bar(index, data[<span class="string">&#x27;Before&#x27;</span>] , bar_width, label=<span class="string">&#x27;Before&#x27;</span>)</span><br><span class="line">ax.bar(index + bar_width, data[<span class="string">&#x27;After&#x27;</span>], bar_width, label=<span class="string">&#x27;After&#x27;</span>)</span><br><span class="line">ax.set_xticks(index + bar_width / <span class="number">2</span>)</span><br><span class="line">ax.set_xticklabels(data[<span class="string">&#x27;Test&#x27;</span>])</span><br><span class="line">ax.legend()</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic03.png" alt="statistic03"></p><h4 id="配对样本t检验"><a href="#配对样本t检验" class="headerlink" title="配对样本t检验"></a>配对样本t检验</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ttest_rel.html#scipy.stats.ttest_rel">scipy.stats.ttest_rel(a, b)</a> 检验不同条件是否对数据有显著影响. 它返回t值与p值.</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.ttest_rel(data[<span class="string">&#x27;Before&#x27;</span>], data[<span class="string">&#x27;After&#x27;</span>])</span><br><span class="line">Ttest_relResult(statistic=-<span class="number">5.400617248673217</span>, pvalue=<span class="number">0.0016629169382403571</span>)</span><br></pre></td></tr></table></figure><p>可以看到 $p&lt;0.01$, 说明统计结果达到显著意义, 接受$H_1$, 在$\alpha&#x3D;0.01$的统计显著水平下该实验对数据大小存在显著影响.</p><h2 id="卡方检验"><a href="#卡方检验" class="headerlink" title="卡方检验"></a>卡方检验</h2><p>卡方检验就是统计样本的实际观测值与理论推断值之间的偏离程度, 实际观测值与理论推断值之间的偏离程度就决定卡方值的大小, 如果卡方值越大, 二者偏差程度越大; 反之, 二者偏差越小; 若两个值完全相等时, 卡方值就为0, 表明理论值完全符合.</p><h3 id="Python实现卡方适合度检验"><a href="#Python实现卡方适合度检验" class="headerlink" title="Python实现卡方适合度检验"></a>Python实现卡方适合度检验</h3><p>实际执行多项式试验而得到的观察次数, 与虚无假设的期望次数相比较, 称为卡方适度检验, 即在于检验二者接近的程度, 利用样本数据以检验总体分布是否为某一特定分布的统计方法.</p><p>假设有这样一组数据(掷120次骰子的结果):</p><table><thead><tr><th>Point</th><th>Obs</th><th>Exp</th></tr></thead><tbody><tr><td>1</td><td>22</td><td>20</td></tr><tr><td>2</td><td>19</td><td>20</td></tr><tr><td>3</td><td>23</td><td>20</td></tr><tr><td>4</td><td>17</td><td>20</td></tr><tr><td>5</td><td>20</td><td>20</td></tr><tr><td>6</td><td>19</td><td>20</td></tr></tbody></table><p>假设:<br>$H_0$: 观察分布等于期望分布<br>$H_1$: 观察分布不等于期望分布</p><p>选用$\alpha&#x3D;0.05$的统计显著水平</p><h4 id="导入数据-3"><a href="#导入数据-3" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;point.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">    Point   Obs Exp</span><br><span class="line"><span class="number">0</span><span class="number">1</span><span class="number">22</span><span class="number">20</span></span><br><span class="line"><span class="number">1</span><span class="number">2</span><span class="number">19</span><span class="number">20</span></span><br><span class="line"><span class="number">2</span><span class="number">3</span><span class="number">23</span><span class="number">20</span></span><br><span class="line"><span class="number">3</span><span class="number">4</span><span class="number">17</span><span class="number">20</span></span><br><span class="line"><span class="number">4</span><span class="number">5</span><span class="number">20</span><span class="number">20</span></span><br><span class="line"><span class="number">5</span><span class="number">6</span><span class="number">19</span><span class="number">20</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-3"><a href="#数据可视化-3" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">bar_width = <span class="number">0.35</span></span><br><span class="line">ax.bar(data[<span class="string">&#x27;Point&#x27;</span>], data[<span class="string">&#x27;Obs&#x27;</span>] , bar_width, label=<span class="string">&#x27;Observe&#x27;</span>)</span><br><span class="line">ax.bar(data[<span class="string">&#x27;Point&#x27;</span>], data[<span class="string">&#x27;Exp&#x27;</span>], bar_width, fill=<span class="literal">False</span>, label=<span class="string">&#x27;Expectation&#x27;</span>)</span><br><span class="line">ax.legend()</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic04.png" alt="statistic04"></p><h4 id="卡方适合度检验"><a href="#卡方适合度检验" class="headerlink" title="卡方适合度检验"></a>卡方适合度检验</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.chisquare.html#scipy.stats.chisquare">scipy.stats.chisquare(f_obs, f_exp&#x3D;None)</a> 检验统计样本的实际观测值与理论推断值之间的偏离程度. 它返回卡方统计量与p值.</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.chisquare(data[<span class="string">&#x27;Obs&#x27;</span>], data[<span class="string">&#x27;Exp&#x27;</span>])</span><br><span class="line">Power_divergenceResult(statistic=<span class="number">1.2</span>, pvalue=<span class="number">0.9448773650021219</span>)</span><br></pre></td></tr></table></figure><p>附卡方鉴定表:</p><p><img src="/assets/blogImg/statistic/statistic05.png" alt="statistic05"></p><p>该样例自由度为$(6-1)\times(2-1)&#x3D;5$, $\alpha&#x3D;0.05$, 查表可得临界值为11.07, 可以看到 $1.2&lt;11.07$, 即统计量小于临界值, 故差异不显著, 接受$H_0$, 在$\alpha&#x3D;0.05$的统计显著水平下观察分布等于期望分布.</p><h3 id="Python实现卡方独立性检验"><a href="#Python实现卡方独立性检验" class="headerlink" title="Python实现卡方独立性检验"></a>Python实现卡方独立性检验</h3><p>卡方独立性检验是用来检验两个属性间是否独立. 一个变量作为行, 另一个变量作为列.</p><p>举个例子:<br>    检验满足感和年龄之间是否独立, 下面是一组虚构的满足感和年龄之间的数据:</p><table><thead><tr><th>Satisfaction\Age</th><th>Male</th><th>Female</th></tr></thead><tbody><tr><td>Yes</td><td>42</td><td>36</td></tr><tr><td>No</td><td>87</td><td>37</td></tr><tr><td>Don’t know</td><td>20</td><td>16</td></tr></tbody></table><p>假设:<br>$H_0$: 满足感和性别之间相互独立<br>$H_1$: 满足感和性别之间相互关联</p><p>选用$\alpha&#x3D;0.05$的统计显著水平</p><h4 id="导入数据-4"><a href="#导入数据-4" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;satisfaction_sex.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">Satisfaction\SexMaleFemale</span><br><span class="line"><span class="number">0</span>Yes<span class="number">42</span><span class="number">36</span></span><br><span class="line"><span class="number">1</span>No<span class="number">87</span><span class="number">37</span></span><br><span class="line"><span class="number">2</span>Don<span class="string">&#x27;t know2016</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-4"><a href="#数据可视化-4" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">bar_width = <span class="number">0.25</span></span><br><span class="line">index = np.arange(<span class="number">2</span>)</span><br><span class="line">ax.bar(index, data.values[<span class="number">0</span>][<span class="number">1</span>:] , bar_width, label=<span class="string">&#x27;Yes&#x27;</span>)</span><br><span class="line">ax.bar(index + bar_width, data.values[<span class="number">1</span>][<span class="number">1</span>:], bar_width, label=<span class="string">&#x27;No&#x27;</span>)</span><br><span class="line">ax.bar(index + bar_width + bar_width, data.values[<span class="number">2</span>][<span class="number">1</span>:] , bar_width, label=<span class="string">&quot;Don&#x27;t konw&quot;</span>)</span><br><span class="line">ax.set_xticks(index + bar_width)</span><br><span class="line">ax.set_xticklabels(data.columns[<span class="number">1</span>:])</span><br><span class="line">ax.legend()</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic06.png" alt="statistic06"></p><h4 id="卡方独立性检验"><a href="#卡方独立性检验" class="headerlink" title="卡方独立性检验"></a>卡方独立性检验</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.chi2_contingency.html#scipy.stats.chi2_contingency">scipy.stats.chi2_contingency(observed)</a> 对横竖两个列表的独立性进行检验. 它返回卡方统计量, p值, 自由度和预期频率.</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.chi2_contingency(data)</span><br><span class="line">(<span class="number">6.344442816506179</span>,</span><br><span class="line"> <span class="number">0.0419103943243306</span>,</span><br><span class="line"> <span class="number">2</span>,</span><br><span class="line"> array([[<span class="number">48.83193277</span>, <span class="number">29.16806723</span>],</span><br><span class="line">        [<span class="number">77.6302521</span> , <span class="number">46.3697479</span> ],</span><br><span class="line">        [<span class="number">22.53781513</span>, <span class="number">13.46218487</span>]]))</span><br></pre></td></tr></table></figure><p>该样例自由度为$(3-1)\times(2-1)&#x3D;2$, $\alpha&#x3D;0.05$, 查表可得临界值为5.99, 可以看到 $6.34&gt;5.99$, 即统计量大于临界值, 故差异显著, 拒绝$H_0$, 接受$$H_1$, 在$\alpha&#x3D;0.05$的统计显著水平下满足感和性别之间相互联系.</p><h3 id="Python实现卡方统一性检验"><a href="#Python实现卡方统一性检验" class="headerlink" title="Python实现卡方统一性检验"></a>Python实现卡方统一性检验</h3><p>检验两个或两个以上总体的某一特性分布, 也就是各“类别”的比例是否统一或相近, 一般称为卡方统一性检验或者卡方同质性检验.</p><p>举个例子:<br>    下面是A, B两个地区对满意度调查问卷的结果:</p><table><thead><tr><th>Satisfaction</th><th>A</th><th>B</th></tr></thead><tbody><tr><td>5</td><td>25</td><td>23</td></tr><tr><td>4</td><td>20</td><td>22</td></tr><tr><td>3</td><td>35</td><td>32</td></tr><tr><td>2</td><td>12</td><td>14</td></tr><tr><td>1</td><td>8</td><td>9</td></tr></tbody></table><p>假设:<br>$H_0$: A, B两地区的满意度比例相同<br>$H_1$: A, B两地区的满意度比例不同</p><p>选用$\alpha&#x3D;0.05$的统计显著水平</p><h4 id="导入数据-5"><a href="#导入数据-5" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;satisfaction_area.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">    SatisfactionAB</span><br><span class="line"><span class="number">0</span><span class="number">5</span><span class="number">25</span><span class="number">23</span></span><br><span class="line"><span class="number">1</span><span class="number">4</span><span class="number">20</span><span class="number">22</span></span><br><span class="line"><span class="number">2</span><span class="number">3</span><span class="number">35</span><span class="number">32</span></span><br><span class="line"><span class="number">3</span><span class="number">2</span><span class="number">12</span><span class="number">14</span></span><br><span class="line"><span class="number">4</span><span class="number">1</span><span class="number">8</span><span class="number">9</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-5"><a href="#数据可视化-5" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">bar_width = <span class="number">0.35</span></span><br><span class="line">index = np.arange(<span class="number">5</span>)</span><br><span class="line">ax.bar(index, data[<span class="string">&#x27;A&#x27;</span>] , bar_width, label=<span class="string">&#x27;A&#x27;</span>)</span><br><span class="line">ax.bar(index + bar_width, data[<span class="string">&#x27;B&#x27;</span>], bar_width, label=<span class="string">&#x27;B&#x27;</span>)</span><br><span class="line">ax.set_xticks(index + bar_width/<span class="number">2</span>)</span><br><span class="line">ax.set_xticklabels(data[<span class="string">&#x27;Satisfaction&#x27;</span>])</span><br><span class="line">ax.legend()</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic07.png" alt="statistic07"></p><h4 id="卡方统一性检验"><a href="#卡方统一性检验" class="headerlink" title="卡方统一性检验"></a>卡方统一性检验</h4><p>这里用的方法和卡方适合度检验一样, 就不再详细描述了, 直接展示代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.chisquare(data[<span class="string">&#x27;A&#x27;</span>], data[<span class="string">&#x27;B&#x27;</span>])</span><br><span class="line">Power_divergenceResult(statistic=<span class="number">1.0338066221218394</span>, pvalue=<span class="number">0.9046272037861618</span>)</span><br></pre></td></tr></table></figure><p>该样例自由度为$(5-1)\times(2-1)&#x3D;4$, $\alpha&#x3D;0.05$, 查表可得临界值为9.49, 可以看到 $1.03&lt;9.49$, 即统计量小于临界值, 故差异不显著, 接受$H_0$, 在$\alpha&#x3D;0.05$的统计显著水平下A, B两地区的满意度比例相同.</p><h2 id="方差分析"><a href="#方差分析" class="headerlink" title="方差分析"></a>方差分析</h2><p>方差分析(ANOVA)又称”变异数分析”或”F检验”, 是由R.A.Fister发明的, 用于两个及两个以上样本均数差别的显著性检验.</p><h3 id="Python实现单因素方差分析"><a href="#Python实现单因素方差分析" class="headerlink" title="Python实现单因素方差分析"></a>Python实现单因素方差分析</h3><p>单因素方差分析(One Way ANOVA)是指对单因素试验结果进行分析, 检验因素对试验结果有无显著性影响的方法.</p><p>例如:<br>    将抗生素注入人体会产生抗生素与血浆蛋白质结合的现象, 以致减少了药效. 下表列出了5种常用的抗生素注入到牛的体内时, 抗生素与血浆蛋白质结合的百分比. 现需要在显著性水平$\alpha &#x3D; 0.05$下检验这些百分比的均值有无显著的差异. 设各总体服从正态分布, 且方差相同.</p><table><thead><tr><th>青霉素</th><th>四环素</th><th>链霉</th><th>红霉素</th><th>氯霉素</th></tr></thead><tbody><tr><td>29.6</td><td>27.3</td><td>5.8</td><td>21.6</td><td>29.2</td></tr><tr><td>24.3</td><td>32.6</td><td>6.2</td><td>17.4</td><td>32.8</td></tr><tr><td>28.5</td><td>30.8</td><td>11.0</td><td>18.3</td><td>25.0</td></tr><tr><td>32.0</td><td>34.8</td><td>8.3</td><td>19.0</td><td>24.2</td></tr></tbody></table><p>　在这里, 试验的指标是抗生素与血浆蛋白质结合的百分比, 抗生素为因素, 不同的5种抗生素就是这个因素的五个不同的水平. 假定除抗生素这一因素外, 其余的一切条件都相同. 这就是单因素试验. 试验的目的是要考察这些抗生素与血浆蛋白质结合的百分比的均值有无显著的差异. 即考察抗生素这一因素对这些百分比有无显著影响. 这就是一个典型的单因素试验的方差分析问题.</p><p>假设:<br>$H_0$: 各个总体的均值相等<br>$H_1$: 各个总体的均值不全相等</p><h4 id="导入数据-6"><a href="#导入数据-6" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;medicine.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">    PenTetStrEryChl</span><br><span class="line"><span class="number">0</span><span class="number">29.6</span><span class="number">27.3</span><span class="number">5.8</span><span class="number">21.6</span><span class="number">29.2</span></span><br><span class="line"><span class="number">1</span><span class="number">24.3</span><span class="number">32.6</span><span class="number">6.2</span><span class="number">17.4</span><span class="number">32.8</span></span><br><span class="line"><span class="number">2</span><span class="number">28.5</span><span class="number">30.8</span><span class="number">11.0</span><span class="number">18.3</span><span class="number">25.0</span></span><br><span class="line"><span class="number">3</span><span class="number">32.0</span><span class="number">34.8</span><span class="number">8.3</span><span class="number">19.0</span><span class="number">24.2</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-6"><a href="#数据可视化-6" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">ax.boxplot([data[<span class="string">&#x27;Pen&#x27;</span>], data[<span class="string">&#x27;Tet&#x27;</span>], data[<span class="string">&#x27;Str&#x27;</span>], data[<span class="string">&#x27;Ery&#x27;</span>], data[<span class="string">&#x27;Chl&#x27;</span>]])</span><br><span class="line">ax.set_xticklabels([<span class="string">&#x27;青霉素&#x27;</span>, <span class="string">&#x27;四环素&#x27;</span>, <span class="string">&#x27;链霉&#x27;</span>, <span class="string">&#x27;红霉素&#x27;</span>, <span class="string">&#x27;氯霉素&#x27;</span>], fontproperties = <span class="string">&#x27;STSong&#x27;</span>)</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic08.png" alt="statistic08"></p><h4 id="单因素方差分析"><a href="#单因素方差分析" class="headerlink" title="单因素方差分析"></a>单因素方差分析</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.f_oneway.html#scipy.stats.f_oneway">scipy.stats.f_oneway(*args)</a> 单因素方差分析, 返回f值和p值.</p></blockquote><p>或者:</p><blockquote><p><a href="https://www.statsmodels.org/stable/generated/statsmodels.stats.anova.anova_lm.html#statsmodels.stats.anova.anova_lm">statsmodels.stats.anova.anova_lm(*args)</a>  一个或多个因素方差分析, 返回方差分析表</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> stats</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.f_oneway(data[<span class="string">&#x27;Pen&#x27;</span>], data[<span class="string">&#x27;Tet&#x27;</span>], data[<span class="string">&#x27;Str&#x27;</span>], data[<span class="string">&#x27;Ery&#x27;</span>], data[<span class="string">&#x27;Chl&#x27;</span>])</span><br><span class="line">F_onewayResult(statistic=<span class="number">40.88487732150424</span>, pvalue=<span class="number">6.739775552625242e-08</span>)</span><br></pre></td></tr></table></figure><p>或者:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> statsmodels.formula.api <span class="keyword">import</span> ols</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> statsmodels.stats.anova <span class="keyword">import</span> anova_lm</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data_melt = data.melt() <span class="comment"># 将数据翻转</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data_melt</span><br><span class="line">variablevalue</span><br><span class="line"><span class="number">0</span>Pen<span class="number">29.6</span></span><br><span class="line"><span class="number">1</span>Pen<span class="number">24.3</span></span><br><span class="line"><span class="number">2</span>Pen<span class="number">28.5</span></span><br><span class="line"><span class="number">3</span>Pen<span class="number">32.0</span></span><br><span class="line"><span class="number">4</span>Tet<span class="number">27.3</span></span><br><span class="line"><span class="number">5</span>Tet<span class="number">32.6</span></span><br><span class="line"><span class="number">6</span>Tet<span class="number">30.8</span></span><br><span class="line"><span class="number">7</span>Tet<span class="number">34.8</span></span><br><span class="line"><span class="number">8</span>Str<span class="number">5.8</span></span><br><span class="line"><span class="number">9</span>Str<span class="number">6.2</span></span><br><span class="line"><span class="number">10</span>Str<span class="number">11.0</span></span><br><span class="line"><span class="number">11</span>Str<span class="number">8.3</span></span><br><span class="line"><span class="number">12</span>Ery<span class="number">21.6</span></span><br><span class="line"><span class="number">13</span>Ery<span class="number">17.4</span></span><br><span class="line"><span class="number">14</span>Ery<span class="number">18.3</span></span><br><span class="line"><span class="number">15</span>Ery<span class="number">19.0</span></span><br><span class="line"><span class="number">16</span>Chl<span class="number">29.2</span></span><br><span class="line"><span class="number">17</span>Chl<span class="number">32.8</span></span><br><span class="line"><span class="number">18</span>Chl<span class="number">25.0</span></span><br><span class="line"><span class="number">19</span>Chl<span class="number">24.2</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>formula = <span class="string">&#x27;value~C(variable)&#x27;</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>model = ols(formula=formula, data=data_melt).fit()</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>anova_lm(model)</span><br><span class="line">    dfsum_sqmean_sqFPR(&gt;F)</span><br><span class="line">C(variable)<span class="number">4.0</span><span class="number">1480.8230</span><span class="number">370.205750</span><span class="number">40.884877</span><span class="number">6.739776e-08</span></span><br><span class="line">Residual<span class="number">15.0</span><span class="number">135.8225</span><span class="number">9.054833</span>NaNNaN</span><br></pre></td></tr></table></figure><p>$p&#x3D;6.739776e-08$小于0.05, 故拒绝$H_0$, 接受$H_1$, 即在$\alpha&#x3D;0.05$的统计显著水平下, 各个总体的均值不全相等.</p><h3 id="Python实现多因素方差分析"><a href="#Python实现多因素方差分析" class="headerlink" title="Python实现多因素方差分析"></a>Python实现多因素方差分析</h3><p>多因素方差分析法是一种统计分析方法是用来分析两个或以上因素的不同水平对结果是否有显著影响, 以及两因素之间是否存在交互效应.</p><p>例如:<br>    以下是虚构的施不同肥料比例对产量的影响:</p><table><thead><tr><th>N&#x2F;P</th><th>B1</th><th>B2</th><th>B3</th></tr></thead><tbody><tr><td>A1</td><td>5</td><td>4</td><td>3</td></tr><tr><td>A2</td><td>6</td><td>5</td><td>4</td></tr><tr><td>A3</td><td>9</td><td>6</td><td>3</td></tr><tr><td>A4</td><td>8</td><td>7</td><td>6</td></tr><tr><td>A5</td><td>4</td><td>3</td><td>2</td></tr></tbody></table><p>假设:</p><ul><li><p>要说明性别有无显著影响, 就是检验如下假设:<br>$H_0$: 施N肥不同水平下观测变量的总体均值无显著差异.<br>$H_1$: 施N肥不同水平下观测变量的总体均值存在显著差异.</p></li><li><p>要说明年龄有无显著影响, 就是检验如下假设:<br>$H_0$: 施P肥不同水平下观测变量的总体均值无显著差异.<br>$H_1$: 施P肥不同水平下观测变量的总体均值存在显著差异.</p></li><li><p>在有交互效应的双因素方差中, 要说明两个因素的交互效应是否显著, 还要检验第三组零假设和备择假设:<br>$H_0$: 施N肥和施P肥交互效应对观测变量的总体均值无显著差异.<br>$H_1$: 施N肥和施P肥交互效应对观测变量的总体均值存在显著差异.</p></li></ul><h4 id="导入数据-7"><a href="#导入数据-7" class="headerlink" title="导入数据"></a>导入数据</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pandas</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data = pandas.read_csv(<span class="string">&#x27;fertilization.csv&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">    N/PB1B2B3</span><br><span class="line"><span class="number">0</span>A1<span class="number">5</span><span class="number">4</span><span class="number">3</span></span><br><span class="line"><span class="number">1</span>A2<span class="number">6</span><span class="number">5</span><span class="number">4</span></span><br><span class="line"><span class="number">2</span>A3<span class="number">9</span><span class="number">6</span><span class="number">3</span></span><br><span class="line"><span class="number">3</span>A4<span class="number">8</span><span class="number">7</span><span class="number">6</span></span><br><span class="line"><span class="number">4</span>A5<span class="number">4</span><span class="number">3</span><span class="number">2</span></span><br></pre></td></tr></table></figure><h4 id="数据可视化-7"><a href="#数据可视化-7" class="headerlink" title="数据可视化"></a>数据可视化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">data = data.set_index(<span class="string">&#x27;N/P&#x27;</span>).stack().reset_index()  <span class="comment"># 将数据整理成行</span></span><br><span class="line">data.columns=[<span class="string">&#x27;N&#x27;</span>, <span class="string">&#x27;P&#x27;</span>, <span class="string">&#x27;Yield&#x27;</span>]  <span class="comment"># 重命名列名</span></span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">index = np.arange(<span class="number">15</span>)</span><br><span class="line">ax.bar(index , data[<span class="string">&#x27;Yield&#x27;</span>], label=<span class="string">&#x27;Yield&#x27;</span>)</span><br><span class="line">ax.set_xticks(index)</span><br><span class="line">index_label = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">15</span>):</span><br><span class="line">    index_label.append(data.loc[i][<span class="string">&#x27;N&#x27;</span>] +<span class="string">&#x27;\n&#x27;</span>+ data.loc[i][<span class="string">&#x27;P&#x27;</span>])</span><br><span class="line">ax.set_xticklabels(index_label)</span><br><span class="line">ax.legend()</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/statistic/statistic09.png" alt="statistic09"></p><h4 id="多因素方差分析"><a href="#多因素方差分析" class="headerlink" title="多因素方差分析"></a>多因素方差分析</h4><blockquote><p><a href="https://www.statsmodels.org/stable/generated/statsmodels.stats.anova.anova_lm.html#statsmodels.stats.anova.anova_lm">statsmodels.stats.anova.anova_lm(*args)</a>  一个或多个因素方差分析, 返回方差分析表</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> statsmodels.formula.api <span class="keyword">import</span> ols</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> statsmodels.stats.anova <span class="keyword">import</span> anova_lm</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>data</span><br><span class="line">NPYield</span><br><span class="line"><span class="number">0</span>A1B1<span class="number">5</span></span><br><span class="line"><span class="number">1</span>A1B2<span class="number">4</span></span><br><span class="line"><span class="number">2</span>A1B3<span class="number">3</span></span><br><span class="line"><span class="number">3</span>A2B1<span class="number">6</span></span><br><span class="line"><span class="number">4</span>A2B2<span class="number">5</span></span><br><span class="line"><span class="number">5</span>A2B3<span class="number">4</span></span><br><span class="line"><span class="number">6</span>A3B1<span class="number">9</span></span><br><span class="line"><span class="number">7</span>A3B2<span class="number">6</span></span><br><span class="line"><span class="number">8</span>A3B3<span class="number">3</span></span><br><span class="line"><span class="number">9</span>A4B1<span class="number">8</span></span><br><span class="line"><span class="number">10</span>A4B2<span class="number">7</span></span><br><span class="line"><span class="number">11</span>A4B3<span class="number">6</span></span><br><span class="line"><span class="number">12</span>A5B1<span class="number">4</span></span><br><span class="line"><span class="number">13</span>A5B2<span class="number">3</span></span><br><span class="line"><span class="number">14</span>A5B3<span class="number">2</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>formula = <span class="string">&#x27;Yield ~ N + P&#x27;</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>model = ols(formula=formula, data=data).fit()</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>anova_lm(model)</span><br><span class="line">dfsum_sqmean_sqFPR(&gt;F)</span><br><span class="line">N<span class="number">4.0</span><span class="number">30.0</span><span class="number">7.5</span><span class="number">9.375</span><span class="number">0.004106</span></span><br><span class="line">P<span class="number">2.0</span><span class="number">19.6</span><span class="number">9.8</span><span class="number">12.250</span><span class="number">0.003671</span></span><br><span class="line">Residual<span class="number">8.0</span><span class="number">6.4</span><span class="number">0.8</span>NaNNaN</span><br></pre></td></tr></table></figure><p>N $p&#x3D;0.0041$, P $p&#x3D;0.0037$均小于0.05, 故均拒绝$H_0$, 接受$H_1$, 即在$\alpha&#x3D;0.05$的统计显著水平下, N, P不同水平下观测变量的总体均值存在显著差异.</p><p>对于二因素有交互的方差分析, 还应在 formula 后加入<code>A:B</code> 以便显示交互结果.</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>写了这么多, 自己等于是把从前丢掉的重新又给捡起来, 后悔当初没认真学, 现在查个资料都难. 难免有很多错误, 希望大家不吝指出.</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>1. <a href="https://baijiahao.baidu.com/s?id=1636037420591254317&wfr=spider&for=pc">三种T检验的详细区分</a></em><br><em>2. <a href="https://baike.baidu.com/item/t%E6%A3%80%E9%AA%8C">t检验_百度百科</a></em><br><em>3. <a href="https://wiki.mbalib.com/wiki/%E5%8D%A1%E6%96%B9%E6%A3%80%E9%AA%8C">卡方检验 - MBA智库百科</a></em><br><em>4. <a href="https://wiki.mbalib.com/wiki/%E5%8D%95%E5%9B%A0%E7%B4%A0%E6%96%B9%E5%B7%AE%E5%88%86%E6%9E%90">单因素方差分析 - MBA智库百科</a></em><br><em>5. <a href="https://www.cnblogs.com/think-and-do/p/6509239.html">t分布，卡方x分布，F分布 - Thinkando - 博客园</a></em><br><em>6. <a href="https://baike.sbaidu.com/item/%E5%81%87%E8%AE%BE%E6%A3%80%E9%AA%8C">假设检验_百度百科</a></em></p>]]></content>
    
    
    <summary type="html">简单介绍如何使用Python进行t检验, 卡方检验和方差分析.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>Python之Scipy库学习(2)</title>
    <link href="https://zzz5.github.io/2020/06/04/python/scipy/python-scipy-02/"/>
    <id>https://zzz5.github.io/2020/06/04/python/scipy/python-scipy-02/</id>
    <published>2020-06-04T03:31:00.000Z</published>
    <updated>2020-07-07T14:27:12.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="scipy-stats-随机数和统计"><a href="#scipy-stats-随机数和统计" class="headerlink" title="scipy.stats 随机数和统计"></a>scipy.stats 随机数和统计</h2><p>scipy.stats 包含一些生成随机数和统计相关的工具.</p><h3 id="随机数"><a href="#随机数" class="headerlink" title="随机数"></a>随机数</h3><p>stats 已经实现了两个用于封装连续随机变量和离散随机变量的通用分布类. 使用这些类已经实现了80多个连续随机变量和10个离散随机变量.</p><p>这里我们主要讨论的是连续随机变量, 当然, 这些方法几乎也都可以应用于离散随机变量.</p><h4 id="正态分布"><a href="#正态分布" class="headerlink" title="正态分布"></a>正态分布</h4><p>正态分布的方法封装在norm类中</p><p><code>from scipy.stats import norm</code></p><p>正态分布的连续随机变量的主要方法如下:</p><table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>rvs(loc&#x3D;0, scale&#x3D;1, size&#x3D;1, random_state&#x3D;None)</td><td>随机变量(Random Variates)</td></tr><tr><td>pdf(x, loc&#x3D;0, scale&#x3D;1)</td><td>概率密度函数(Probability Density Function)</td></tr><tr><td>cdf(x, loc&#x3D;0, scale&#x3D;1)</td><td>累积分布函数(Cumulative Distribution Function)</td></tr><tr><td>sf(x, loc&#x3D;0, scale&#x3D;1)</td><td>残存函数(Survival Function)</td></tr><tr><td>ppf(q, loc&#x3D;0, scale&#x3D;1)</td><td>正态分布的累计分布函数的逆函数, 即下分位点(Percent Point Function)</td></tr><tr><td>isf(q, loc&#x3D;0, scale&#x3D;1)</td><td>逆残存函数(Inverse Survival Function)</td></tr><tr><td>stats(loc&#x3D;0, scale&#x3D;1, moments&#x3D;’mv’)</td><td>return 均值(‘m’), 方差(‘v’), 偏度(‘s’)或峰度(‘k’)</td></tr><tr><td>fit(data, a, loc&#x3D;0, scale&#x3D;1)</td><td>对一组数据进行正态分布的拟合.</td></tr></tbody></table><p><em>x为坐标横轴的位置, 一般用ndarry, q为概率</em></p><h4 id="其他随机数"><a href="#其他随机数" class="headerlink" title="其他随机数"></a>其他随机数</h4><p>等以后用到了再补充.</p><h3 id="百分位数"><a href="#百分位数" class="headerlink" title="百分位数"></a>百分位数</h3><p>中位数是有一半值在其上一半值在其下的值, 中数也被称为百分位数50, 因为50%的观察值在它之下：</p><p><code>stats.scoreatpercentile(data, 50)</code></p><p>同样，我们也能计算百分位数90:</p><p><code>stats.scoreatpercentile(data, 90)</code></p><h3 id="统计检验"><a href="#统计检验" class="headerlink" title="统计检验"></a>统计检验</h3><p>统计检验是一个决策指示器. 例如, 如果我们有两组观察值, 我们假设他们来自于高斯过程, 我们可以用T检验来决定这两组观察值是不是显著不同:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;a = np.random.normal(<span class="number">0</span>, <span class="number">1</span>, size=<span class="number">100</span>)</span><br><span class="line">&gt;&gt;&gt;b = np.random.normal(<span class="number">1</span>, <span class="number">1</span>, size=<span class="number">10</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>stats.ttest_ind(a, b)</span><br><span class="line">Ttest_indResult(statistic=-<span class="number">3.2279479468805157</span>, pvalue=<span class="number">0.0016513702871169557</span>)</span><br></pre></td></tr></table></figure><p>生成的结果由以下内容组成：</p><ul><li>T 统计值: 一个值, 符号与两个随机过程的差异成比例, 大小与差异的程度有关.</li><li>p 值: 两个过程相同的概率. 如果它接近1, 那么这两个过程几乎肯定是相同的. 越接近于0, 越可能这两个过程有不同的平均数.</li></ul><h2 id="scipy-interpolate-插值计算"><a href="#scipy-interpolate-插值计算" class="headerlink" title="scipy.interpolate 插值计算"></a>scipy.interpolate 插值计算</h2><p>scipy.interpolate 模块在拟合实验数据并估计未知点数值方面非常有用.</p><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html">interpolate.interp1d(x, y)</a></p></blockquote><p>根据给定的x轴和y轴数据返回一个拟合的function().</p><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> interpolate</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建一个随机函数</span></span><br><span class="line">measured_time = np.linspace(<span class="number">0</span>, <span class="number">1</span>, <span class="number">11</span>)</span><br><span class="line">noise = (np.random.rand(<span class="number">11</span>) - <span class="number">0.5</span>) * <span class="number">0.1</span></span><br><span class="line">measures = np.sin(<span class="number">2</span> * np.pi * measured_time) + noise</span><br><span class="line"></span><br><span class="line"><span class="comment"># 根据给定的x轴y轴的值可以创建一个线性插值函数, 该函数可以在需要的时候获取某些值.</span></span><br><span class="line">linear_interp = interpolate.interp1d(measured_time, measures)</span><br><span class="line">computed_time = np.linspace(<span class="number">0</span>, <span class="number">1</span>, <span class="number">50</span>)</span><br><span class="line">linear_results = linear_interp(computed_time)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 三次插值函数可通过给定 kind 关键字参数得到:</span></span><br><span class="line">cubic_interp = interpolate.interp1d(measured_time, measures, kind=<span class="string">&#x27;cubic&#x27;</span>)</span><br><span class="line">cubic_results = cubic_interp(computed_time)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绘图</span></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">plt.rcParams[<span class="string">&#x27;axes.unicode_minus&#x27;</span>]=<span class="literal">False</span></span><br><span class="line">ax.plot(measured_time, measures, <span class="string">&#x27;b-&#x27;</span>, label=<span class="string">&#x27;随机函数&#x27;</span>)</span><br><span class="line">ax.plot(computed_time, linear_results, <span class="string">&#x27;r--&#x27;</span>, label=<span class="string">&#x27;预测函数&#x27;</span>)</span><br><span class="line">ax.plot(computed_time, cubic_results, <span class="string">&#x27;y:&#x27;</span>, label=<span class="string">&#x27;三次插值函数&#x27;</span>)</span><br><span class="line">ax.legend(prop=&#123;<span class="string">&#x27;family&#x27;</span> : <span class="string">&#x27;STSong&#x27;</span>, <span class="string">&#x27;size&#x27;</span> : <span class="number">10</span>&#125;)</span><br><span class="line">ax.set_xlabel(<span class="string">&#x27;x&#x27;</span>)</span><br><span class="line">ax.set_ylabel(<span class="string">&#x27;y&#x27;</span>)</span><br></pre></td></tr></table></figure><p>结果如图:</p><p><img src="/assets/blogImg/scipy/scipy04.png" alt="scipy04"></p><p>scipy.interpolate.interp2d 和 scipy.interpolate.interp1d 较为相似, 但是其适用对象为二维数组.</p><h2 id="scipy-integrate-数值积分"><a href="#scipy-integrate-数值积分" class="headerlink" title="scipy.integrate 数值积分"></a>scipy.integrate 数值积分</h2><h3 id="求定积分"><a href="#求定积分" class="headerlink" title="求定积分"></a>求定积分</h3><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html">scipy.integrate.quad(func, a, b, args&#x3D;())</a>  计算 $\int_a^b,func,dx$ 的结果, args为func中的参数.</p></blockquote><p>这是integrate最常见的积分程序.</p><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> integrate</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">x</span>):</span><br><span class="line"><span class="keyword">return</span> np.sin(x) + np.cos(x)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>res, err = integrate.quad(f, <span class="number">0</span>, np.pi/<span class="number">2</span>)  <span class="comment"># 返回结果和误差</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>np.allclose(res, <span class="number">2</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>np.allclose(err, <span class="number">0</span>)</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><p>其他积分程序还有 fixed_quad, quadrature, romberg 等, 这里就不详细展开说了.</p><h3 id="常微分方程-ODE-求解"><a href="#常微分方程-ODE-求解" class="headerlink" title="常微分方程(ODE)求解"></a>常微分方程(ODE)求解</h3><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html">scipy.integrate.solve_ivp(fun, t_span, y0, args&#x3D;(), t_eval&#x3D;None)</a> </p></blockquote><ul><li>fun: 微分方程</li><li>t_span: t的范围</li><li>y0: 当t&#x3D;0时y的值, 为一维数组, 可以为一系列的值</li><li>args: fun除t, y的其他参数</li></ul><p>假设: $\frac{dy}{dt}&#x3D;-2y$ , 且 $y(t&#x3D;0)&#x3D;1$ , 求解.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> integrate</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 微分方程</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calc_derivative</span>(<span class="params">t, y</span>):</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">2</span> * y</span><br><span class="line"></span><br><span class="line"><span class="comment"># t_eval为 t 值</span></span><br><span class="line">t_eval = np.linspace(<span class="number">0</span>, <span class="number">10</span>, <span class="number">100</span>)</span><br><span class="line">yarr = integrate.solve_ivp(calc_derivative, (<span class="number">0</span>, <span class="number">10</span>), [<span class="number">1</span>], t_eval=t_eval)</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">ax.plot(yarr.t, yarr.y[<span class="number">0</span>], <span class="string">&#x27;b-&#x27;</span>)</span><br><span class="line">ax.set_xlabel(<span class="string">&#x27;x&#x27;</span>)</span><br><span class="line">ax.set_ylabel(<span class="string">&#x27;y&#x27;</span>)</span><br></pre></td></tr></table></figure><p>结果如图:</p><p><img src="/assets/blogImg/scipy/scipy05.png" alt="scipy05"></p><h2 id="scipy-signal-信号处理"><a href="#scipy-signal-信号处理" class="headerlink" title="scipy.signal 信号处理"></a>scipy.signal 信号处理</h2><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.detrend.html">scipy.signal.detrend(data) </a> 从信号中删除线性趋势</p></blockquote><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> signal</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">x = np.linspace(<span class="number">1</span>, <span class="number">10</span>, <span class="number">100</span>)</span><br><span class="line">noise = np.random.rand(<span class="number">100</span>)</span><br><span class="line">y = <span class="number">2</span> * x + noise</span><br><span class="line">detrend_noice = signal.detrend(y)</span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">ax.plot(x, y, <span class="string">&#x27;b-&#x27;</span>, label=<span class="string">&#x27;原函数&#x27;</span>)</span><br><span class="line">ax.plot(x, noise, <span class="string">&#x27;r--&#x27;</span>, label=<span class="string">&#x27;噪点&#x27;</span>)</span><br><span class="line">ax.plot(x, detrend_noice, <span class="string">&#x27;y:&#x27;</span>, label=<span class="string">&#x27;删除线性趋势&#x27;</span>)</span><br><span class="line">ax.legend(prop=&#123;<span class="string">&#x27;family&#x27;</span> : <span class="string">&#x27;STSong&#x27;</span>, <span class="string">&#x27;size&#x27;</span> : <span class="number">10</span>&#125;)</span><br><span class="line">ax.set_xlabel(<span class="string">&#x27;x&#x27;</span>)</span><br><span class="line">ax.set_ylabel(<span class="string">&#x27;y&#x27;</span>)</span><br></pre></td></tr></table></figure><p>结果如图:</p><p><img src="/assets/blogImg/scipy/scipy06.png" alt="scipy06"></p><p>其他函数这里暂不讨论…</p><h2 id="scipy-ndimage-图像处理"><a href="#scipy-ndimage-图像处理" class="headerlink" title="scipy.ndimage 图像处理"></a>scipy.ndimage 图像处理</h2><p>图像处理和分析通常被视为对二维值数组的操作. 然而, 有许多领域必须对高维图像进行分析, 医学成像和生物成像就是很好的例子. 由于其固有的多维特性, Numpy非常适合这类应用程序. scipy.ndimage 子包提供了许多常规图像处理和分析函数, 这些函数旨在与任意维数的数组一起操作. 该子包目前包括: 用于线性和非线性滤波, 二值图像形态学(binary morphology), B样条插值(B-spline interpolation)和对象测量的函数.</p><p>我暂时用不到…以后再写 <a href="http://codingdict.com/article/8262">其他教程</a></p><h2 id="scipy-cluster-聚类分析"><a href="#scipy-cluster-聚类分析" class="headerlink" title="scipy.cluster 聚类分析"></a>scipy.cluster 聚类分析</h2><h3 id="K-Means-聚类"><a href="#K-Means-聚类" class="headerlink" title="K-Means 聚类"></a>K-Means 聚类</h3><p>k-means 算法以k为参数, 把n个对象分成k个簇, 使簇内具有较高的相似度, 而簇间的相似度较低.</p><ol><li>随机选择k个点作为初始的聚类中心.</li><li>对于剩下的点, 根据其与聚类中心的距离, 将其归入最近的簇.</li><li>对每个簇, 计算所有点的均值作为新的聚类中心.</li><li>重复2, 3直到聚类中心不再发生改变.</li></ol><p>有下面例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> cluster</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 随机一批二维数据, 并打乱</span></span><br><span class="line">data = np.vstack((np.random.rand(<span class="number">20</span>,<span class="number">2</span>) + np.array([<span class="number">.5</span>,<span class="number">.5</span>]), np.random.rand(<span class="number">20</span>,<span class="number">2</span>)))</span><br><span class="line">np.random.shuffle(data)</span><br><span class="line"><span class="comment"># 在聚类之前必须使用以下代码来美白数据</span></span><br><span class="line">data_whitened = cluster.vq.whiten(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在我们使用以下代码计算三个群集的k均值</span></span><br><span class="line">centroids, _ = cluster.vq.kmeans(data_whitened, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用下面给出的代码将每个值分配给一个集群</span></span><br><span class="line">clx, _ = cluster.vq.vq(data_whitened, centroids)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绘制出结果图</span></span><br><span class="line">fig, ax = plt.subplots()</span><br><span class="line">clx_0 = data_whitened[np.where(clx == <span class="number">0</span>)]</span><br><span class="line">clx_1 = data_whitened[np.where(clx == <span class="number">1</span>)]</span><br><span class="line">clx_2 = data_whitened[np.where(clx == <span class="number">2</span>)]</span><br><span class="line">ax.scatter(centroids[:, <span class="number">0</span>], centroids[:, <span class="number">1</span>], c=<span class="string">&#x27;k&#x27;</span>, label=<span class="string">&quot;k均值&quot;</span>)</span><br><span class="line">ax.scatter(clx_0[:, <span class="number">0</span>], clx_0[:, <span class="number">1</span>], c=<span class="string">&#x27;r&#x27;</span>, label=<span class="string">&quot;聚类0&quot;</span>)</span><br><span class="line">ax.scatter(clx_1[:, <span class="number">0</span>], clx_1[:, <span class="number">1</span>], c=<span class="string">&#x27;b&#x27;</span>, label=<span class="string">&quot;聚类1&quot;</span>)</span><br><span class="line">ax.scatter(clx_2[:, <span class="number">0</span>], clx_2[:, <span class="number">1</span>], c=<span class="string">&#x27;g&#x27;</span>, label=<span class="string">&quot;聚类2&quot;</span>)</span><br><span class="line">ax.legend(prop=&#123;<span class="string">&#x27;family&#x27;</span> : <span class="string">&#x27;STSong&#x27;</span>, <span class="string">&#x27;size&#x27;</span> : <span class="number">10</span>&#125;)</span><br></pre></td></tr></table></figure><p>结果如图:</p><p><img src="/assets/blogImg/scipy/scipy07.png" alt="scipy07"></p><h3 id="Hierarchical-聚类"><a href="#Hierarchical-聚类" class="headerlink" title="Hierarchical 聚类"></a>Hierarchical 聚类</h3><p>层次聚类方法对给定的数据集进行层次的分解, 直到某种条件满足为止.<br>在已经得到距离值之后, 元素间可以被联系起来. 通过分离和融合可以构建一个结构. 传统上, 表示的方法是树形数据结构, 层次聚类算法, 要么是自底向上聚集型的, 即从叶子节点开始, 最终汇聚到根节点; 要么是自顶向下分裂型的, 即从根节点开始, 递归的向下分裂.</p><p>实例如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> cluster</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 随机一批二维数据, 并打乱</span></span><br><span class="line">data = np.vstack((np.random.rand(<span class="number">10</span>,<span class="number">2</span>) + np.array([<span class="number">.5</span>,<span class="number">.5</span>]), np.random.rand(<span class="number">10</span>,<span class="number">2</span>)))</span><br><span class="line">np.random.shuffle(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成点与点之间的距离矩阵,这里用的欧氏距离:</span></span><br><span class="line">data_dis = cluster.hierarchy.distance.pdist(data, <span class="string">&#x27;euclidean&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行层次聚类:</span></span><br><span class="line">hierarchical_cluster = cluster.hierarchy.linkage(data_dis,method=<span class="string">&#x27;average&#x27;</span>) </span><br><span class="line"></span><br><span class="line"><span class="comment"># 将层级聚类结果以树状图表示出来</span></span><br><span class="line">p = cluster.hierarchy.dendrogram(Z)</span><br></pre></td></tr></table></figure><p>输出结果如图:</p><p><img src="/assets/blogImg/scipy/scipy08.png" alt="scipy08"></p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>1. <a href="https://docs.scipy.org/doc/scipy/reference/index.html">SciPy &mdash; SciPy v1.4.1 Reference Guide</a></em><br><em>2. <a href="https://hal.inria.fr/hal-01206546">Varoquaux, Gaël, et al. Scipy Lecture Notes. 2015.</a></em></p>]]></content>
    
    
    <summary type="html">这里主要是对scipy库中的stats, interpolate, integrate, signal, ndimage, cluster模块作简单介绍.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="scipy" scheme="https://zzz5.github.io/tags/scipy/"/>
    
  </entry>
  
  <entry>
    <title>Python之Scipy库学习(1)</title>
    <link href="https://zzz5.github.io/2020/05/30/python/scipy/python-scipy-01/"/>
    <id>https://zzz5.github.io/2020/05/30/python/scipy/python-scipy-01/</id>
    <published>2020-05-30T02:06:00.000Z</published>
    <updated>2020-07-07T14:27:14.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="SciPy库简介"><a href="#SciPy库简介" class="headerlink" title="SciPy库简介"></a>SciPy库简介</h2><p>scipy库 中有许多科学计算常见问题的工具, 它的子模块对应于不同的应用, 比如插值, 积分, 优化, 图像处理, 统计和特殊功能等.<br>scipy 是Python科学计算环境的核心. 它被设计为利用 numpy 数组进行高效的运行, 从这个角度来讲, scipy和numpy是密不可分的.</p><h2 id="SciPy结构"><a href="#SciPy结构" class="headerlink" title="SciPy结构"></a>SciPy结构</h2><p>SciPy被组织成覆盖不同科学计算领域的子包. 下表汇总了这些信息:</p><table><thead><tr><th>子模块</th><th>描述</th></tr></thead><tbody><tr><td>scipy.cluster</td><td>向量计算 &#x2F; Kmeans</td></tr><tr><td>scipy.constants</td><td>物理和数学常量</td></tr><tr><td>scipy.fftpack</td><td>傅里叶变换</td></tr><tr><td>scipy.integrate</td><td>积分程序</td></tr><tr><td>scipy.interpolate</td><td>插值</td></tr><tr><td>scipy.io</td><td>数据输入和输出</td></tr><tr><td>scipy.linalg</td><td>线性代数程序</td></tr><tr><td>scipy.ndimage</td><td>n-维图像包</td></tr><tr><td>scipy.odr</td><td>正交距离回归</td></tr><tr><td>scipy.optimize</td><td>优化</td></tr><tr><td>scipy.signal</td><td>信号处理</td></tr><tr><td>scipy.sparse</td><td>稀疏矩阵</td></tr><tr><td>scipy.spatial</td><td>空间数据结构和算法</td></tr><tr><td>scipy.special</td><td>一些特殊数学函数</td></tr><tr><td>scipy.stats</td><td>统计</td></tr></tbody></table><p>SciPy 的子包需要分别导入, 例如:</p><p><code>from scipy import linalg, optimize</code></p><h2 id="scipy-io-数据的输入和输出"><a href="#scipy-io-数据的输入和输出" class="headerlink" title="scipy.io 数据的输入和输出"></a>scipy.io 数据的输入和输出</h2><p><a href="https://docs.scipy.org/doc/scipy/reference/tutorial/io.html">scipy.io</a> 最常用的主要有三个方法:</p><blockquote><p>loadmat(file_name[, mdict, appendmat])</p></blockquote><p>载入 MATLAB 的 .mat 文件.</p><blockquote><p>savemat(file_name, mdict[, appendmat, …])</p></blockquote><p>将字典和文件名保存到 MATLAB 的 .mat 文件.</p><blockquote><p>whosmat(file_name[, appendmat])</p></blockquote><p>列出 MATLAB 文件的一些属性.</p><p>例如：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> io <span class="keyword">as</span> sio</span><br><span class="line">temp_data = np.ones((<span class="number">3</span>, <span class="number">3</span>))</span><br><span class="line">sio.savemat(<span class="string">&#x27;file.mat&#x27;</span>, &#123;<span class="string">&#x27;temp_data&#x27;</span>: temp_data&#125;) <span class="comment"># mat格式文件输入要求格式为字典</span></span><br><span class="line">data = sio.loadmat(<span class="string">&#x27;file.mat&#x27;</span>, struct_as_record=<span class="literal">True</span>)</span><br><span class="line">info = sio.whosmat(<span class="string">&#x27;file.mat&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(data[<span class="string">&#x27;temp_data&#x27;</span>])</span><br><span class="line"><span class="built_in">print</span>(info)</span><br><span class="line"><span class="comment"># 输出的结果为:</span></span><br><span class="line"><span class="string">&#x27;temp_data&#x27;</span>: </span><br><span class="line">array([[<span class="number">0.30944564</span>, <span class="number">0.6779428</span> , <span class="number">0.18910781</span>, <span class="number">0.16907513</span>],</span><br><span class="line">       [<span class="number">0.84839293</span>, <span class="number">0.65481527</span>, <span class="number">0.29616549</span>, <span class="number">0.16536131</span>],</span><br><span class="line">       [<span class="number">0.77168659</span>, <span class="number">0.09888839</span>, <span class="number">0.95092472</span>, <span class="number">0.46103117</span>]])&#125;</span><br><span class="line">[(<span class="string">&#x27;temp_data&#x27;</span>, (<span class="number">3</span>, <span class="number">4</span>), <span class="string">&#x27;double&#x27;</span>)]</span><br></pre></td></tr></table></figure><p>关于 scipy.io 还有其他几种文件格式的载入与输出, 这里就不详细介绍了, 只列出来在这里, 详情可以查看 <a href="https://docs.scipy.org/doc/scipy/reference/io.html">官方文档对 scipy.io 的介绍</a>.</p><table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>readsav(file_name[, idict, python_dict, …])</td><td>读取 IDL 的 .sav 文件.</td></tr><tr><td>mminfo(source)</td><td>从类MM(Matrix Market)文件中返回大小和存储参数.</td></tr><tr><td>mmread(source)</td><td>读取类MM(Matrix Market)文件.</td></tr><tr><td>mmwrite(target, a[, comment, field, …])</td><td>将数组a写入类MM(Matrix Market)目标.</td></tr><tr><td>wavfile.read(filename[, mmap])</td><td>读取一个.wav的音轨文件.</td></tr><tr><td>wavfile.write(filename, rate, data)</td><td>将一个numpy数组写入.wav文件.</td></tr><tr><td>arff.loadarff(f)</td><td>读取一个 arff 文件.</td></tr><tr><td>netcdf_file(filename[, mode, mmap, version, …])</td><td>NetCDF数据的文件对象.</td></tr></tbody></table><p>载入和保存text, csv或其他numpy指定类型可以使用numpy包, 详见 <a href="https://zzz5.xyz/2019/04/22/python/numpy/python-numpy-03/">Python之Numpy库学习(3)</a>.</p><h2 id="scipy-special-一些特殊数学函数"><a href="#scipy-special-一些特殊数学函数" class="headerlink" title="scipy.special 一些特殊数学函数"></a>scipy.special 一些特殊数学函数</h2><p>scipy.special 子包的主要特点是定义了许多数学物理的特殊函数. 可用功能包括 airy, elliptic, bessel, gamma, beta, hypergeometric, parabolic cylinder, mathieu, spheroidal wave, struve, kelvin. 还有一些低级统计函数不在 scipy.special 模块中, 因为 scipy.stats 模块为这些函数提供了更简单的接口.<br>这些函数中的大多数可以接受数组参数并返回数组结果, 遵循与NumPy中的其他数学函数相同的广播规则. 其中许多函数还接受复数作为输入.<br>下面列取几个常用的函数:</p><ul><li>贝塞尔函数, 比如scipy.special.jv() (第n个整型顺序的贝塞尔函数)</li><li>椭圆函数 (scipy.special.ellipj() Jacobian椭圆函数, …)</li><li>Gamma 函数: scipy.special.gamma(), 也要注意 scipy.special.gammaln() 将给出更高准确数值的 Gamma的log。</li><li>Erf, 高斯曲线的面积：scipy.special.erf()</li></ul><p>详情可以查看 <a href="https://docs.scipy.org/doc/scipy/reference/special.html">官方文档对 scipy.special 的介绍</a>.</p><h2 id="scipy-linalg-线性代数操作"><a href="#scipy-linalg-线性代数操作" class="headerlink" title="scipy.linalg 线性代数操作"></a>scipy.linalg 线性代数操作</h2><p>scipy.linalg 是基于 ATLAS, LAPACK 和 BLAS 高效底层库构建的, 它具有非常高效的线性代数操作.<br>如果深入研究, 所有原始的LAPACK和BLAS库都可以直接使用, 这里我们只谈一些更易于使用的接口.</p><h3 id="scipy-linalg-vs-numpy-linalg"><a href="#scipy-linalg-vs-numpy-linalg" class="headerlink" title="scipy.linalg vs numpy.linalg"></a>scipy.linalg vs numpy.linalg</h3><p>scipy.linalg 包含所有 numpy.linalg 的功能, 并在此基础上有所扩展.<br>在 numpy 中进行线性代数操作得到的结果为 matrix类, 虽然 matrix类 简化了一些线代操作, 但所有的操作也可以使用 2-D ndarry 实现, 而且在实际操作中也容易混淆 matrix类 和 ndarry类. 而使用 scipy.linalg 可以同时应用于 matrix类 和 2-D ndarry 类.</p><h3 id="线代基础操作"><a href="#线代基础操作" class="headerlink" title="线代基础操作"></a>线代基础操作</h3><h4 id="求矩阵的逆"><a href="#求矩阵的逆" class="headerlink" title="求矩阵的逆"></a>求矩阵的逆</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.inv.html">scipy.linalg.inv(a)</a></p></blockquote><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> linalg</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = np.array([[<span class="number">1.</span>,<span class="number">2.</span>], [<span class="number">3.</span>,<span class="number">4.</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a</span><br><span class="line">array([[<span class="number">1.</span>, <span class="number">2.</span>],</span><br><span class="line">       [<span class="number">3.</span>, <span class="number">4.</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>linalg.inv(a)</span><br><span class="line">array([[-<span class="number">2.</span> ,  <span class="number">1.</span> ],</span><br><span class="line">       [ <span class="number">1.5</span>, -<span class="number">0.5</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a.dot(linalg.inv(a))</span><br><span class="line">array([[<span class="number">1.0000000e+00</span>, <span class="number">0.0000000e+00</span>],</span><br><span class="line">       [<span class="number">8.8817842e-16</span>, <span class="number">1.0000000e+00</span>]])</span><br></pre></td></tr></table></figure><h4 id="解线性方程组"><a href="#解线性方程组" class="headerlink" title="解线性方程组"></a>解线性方程组</h4><p>$$\begin{cases} x + 3y + 5z &#x3D; 10 \\ 2x + 5y + z &#x3D; 8 \\ 2x + 3y + 8z &#x3D; 3 \\ \end{cases}$$</p><p>该方程组可以转换为矩阵:</p><p>$$\begin{bmatrix} 1 &amp; 3 &amp; 5\\ 2 &amp; 5 &amp; 1 \\ 2 &amp; 3 &amp; 8 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \end{bmatrix} &#x3D; \begin{bmatrix} 10 \\ 8 \\ 3 \end{bmatrix}$$</p><p>即可以得出:</p><p>$$\begin{bmatrix} x \\ y \\ z \end{bmatrix} &#x3D; \begin{bmatrix} 1 &amp; 3 &amp; 5\\ 2 &amp; 5 &amp; 1 \\ 2 &amp; 3 &amp; 8 \end{bmatrix}^{-1}  \begin{bmatrix} 10 \\ 8 \\ 3 \end{bmatrix}$$</p><p>当使用 scipy.linalg 时, 就可以使计算变得非常简单.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> linalg</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = np.array([[<span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>], [<span class="number">2</span>,<span class="number">5</span>,<span class="number">1</span>],[<span class="number">2</span>,<span class="number">3</span>,<span class="number">8</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a</span><br><span class="line">array([[<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>],</span><br><span class="line">       [<span class="number">2</span>, <span class="number">5</span>, <span class="number">1</span>],</span><br><span class="line">       [<span class="number">2</span>, <span class="number">3</span>, <span class="number">8</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>b = np.array([[<span class="number">10</span>], [<span class="number">8</span>], [<span class="number">3</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>b</span><br><span class="line">array([[<span class="number">10</span>],</span><br><span class="line">       [ <span class="number">8</span>],</span><br><span class="line">       [ <span class="number">3</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>linalg.inv(a).dot(b)  <span class="comment"># 计算结果</span></span><br><span class="line">array([[-<span class="number">9.28</span>],</span><br><span class="line">       [ <span class="number">5.16</span>],</span><br><span class="line">       [ <span class="number">0.76</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a.dot(linalg.inv(a).dot(b))  <span class="comment"># 检验结果</span></span><br><span class="line">array([[<span class="number">10.</span>],</span><br><span class="line">       [ <span class="number">8.</span>],</span><br><span class="line">       [ <span class="number">3.</span>]])</span><br></pre></td></tr></table></figure><h4 id="计算行列式的值"><a href="#计算行列式的值" class="headerlink" title="计算行列式的值"></a>计算行列式的值</h4><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.det.html">scipy.linalg.det(a)</a></p></blockquote><p>给定一个矩阵:</p><p>$$A &#x3D; \begin{bmatrix} 1 &amp; 2 \\ 3 &amp; 4 \end{bmatrix}$$</p><p>计算它的行列式:</p><p>$$\mid A \mid  &#x3D; \begin{vmatrix} 1 &amp; 2 \\ 3 &amp; 4 \end{vmatrix} &#x3D; 1 \times 4 - 2 \times 3 &#x3D; -2$$</p><p>用 scipy.linalg.det() 函数计算矩阵的行列式:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> linalg</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>A = np.array([[<span class="number">1</span>,<span class="number">2</span>], [<span class="number">3</span>,<span class="number">4</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>A</span><br><span class="line">array([[<span class="number">1</span>, <span class="number">2</span>],</span><br><span class="line">       [<span class="number">3</span>, <span class="number">4</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>linalg.det(A)</span><br><span class="line">-<span class="number">2.0</span></span><br></pre></td></tr></table></figure><h4 id="计算矩阵的伴随矩阵"><a href="#计算矩阵的伴随矩阵" class="headerlink" title="计算矩阵的伴随矩阵"></a>计算矩阵的伴随矩阵</h4><p>求矩阵的伴随矩阵, 我们可以使用以下公式, 设 A 是一个行列式不为0的方阵: </p><p>$$A^* &#x3D; \mid A \mid A^{-1}$$</p><p>所以, 我们可以用 linalg.det(A).dot(linalg.inv(A)) 计算矩阵 A 的伴随矩阵. 很多矩阵的计算都可以用这种思路.</p><h4 id="求矩阵的特征值和特征向量"><a href="#求矩阵的特征值和特征向量" class="headerlink" title="求矩阵的特征值和特征向量"></a>求矩阵的特征值和特征向量</h4><p>对于矩阵 A 若: $A \zeta &#x3D; \lambda A$ , 则我们称 $\lambda$ 为 A 的特征值, $\zeta$ 为 A 的特征向量.</p><blockquote><p><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.eig.html">linalg.eig(A)</a>  会同时 return $\lambda$ 和 $\zeta$,<br><a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.eigvals.html">linalg.eigvals(A)</a>  只会 return $\lambda$.</p></blockquote><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> scipy <span class="keyword">import</span> linalg</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>A = np.array([[<span class="number">0</span>,<span class="number">2</span>,-<span class="number">2</span>], [<span class="number">2</span>,<span class="number">4</span>,<span class="number">4</span>], [-<span class="number">2</span>,<span class="number">4</span>,-<span class="number">3</span>]])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>A</span><br><span class="line">array([[ <span class="number">0</span>,  <span class="number">2</span>, -<span class="number">2</span>],</span><br><span class="line">       [ <span class="number">2</span>,  <span class="number">4</span>,  <span class="number">4</span>],</span><br><span class="line">       [-<span class="number">2</span>,  <span class="number">4</span>, -<span class="number">3</span>]])</span><br><span class="line">&gt;&gt;&gt;linalg.eigvals(A)</span><br><span class="line">array([-<span class="number">6.</span>+<span class="number">0.j</span>,  <span class="number">1.</span>+<span class="number">0.j</span>,  <span class="number">6.</span>+<span class="number">0.j</span>])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>linalg.eig(A)</span><br><span class="line">(array([-<span class="number">6.</span>+<span class="number">0.j</span>,  <span class="number">1.</span>+<span class="number">0.j</span>,  <span class="number">6.</span>+<span class="number">0.j</span>]),</span><br><span class="line">array([[-<span class="number">4.08248290e-01</span>,  <span class="number">8.94427191e-01</span>,  <span class="number">1.82574186e-01</span>],</span><br><span class="line">       [ <span class="number">4.08248290e-01</span>,  <span class="number">9.87581947e-17</span>,  <span class="number">9.12870929e-01</span>],</span><br><span class="line">       [-<span class="number">8.16496581e-01</span>, -<span class="number">4.47213595e-01</span>,  <span class="number">3.65148372e-01</span>]]))</span><br></pre></td></tr></table></figure><h4 id="其他矩阵操作"><a href="#其他矩阵操作" class="headerlink" title="其他矩阵操作"></a>其他矩阵操作</h4><p>其他矩阵操作可以查看 <a href="https://docs.scipy.org/doc/scipy/reference/sparse.linalg.html">官网关于linalg的文档</a>.</p><h2 id="scipy-fftpack-快速傅里叶变换"><a href="#scipy-fftpack-快速傅里叶变换" class="headerlink" title="scipy.fftpack 快速傅里叶变换"></a>scipy.fftpack 快速傅里叶变换</h2><p>关于傅里叶变换, 可以参考这篇文章 <a href="https://www.matongxue.com/madocs/619.html">如何理解傅立叶级数公式？</a></p><p>scipy.fftpack 模块包含了快速傅里叶变换的功能.</p><p>下面是一个噪声信号的例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">time_step = <span class="number">0.02</span></span><br><span class="line">period = <span class="number">5.</span></span><br><span class="line">time_vec = np.arange(<span class="number">0</span>, <span class="number">20</span>, time_step)</span><br><span class="line">sig = np.sin(<span class="number">2</span> * np.pi / period * time_vec) + <span class="number">0.5</span> * np.random.randn(time_vec.size)</span><br></pre></td></tr></table></figure><p>观察者不知道信号的频率, 只知道信号的采样间隙 sig 信号是来自真实函数的, 那么 傅里叶变换是对称的. <code>scipy.fftpack.fftfreq()</code> 函数会生成采样频率, <code>scipy.fftpack.fft()</code> 则用于进行快速傅里叶变化:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> fftpack</span><br><span class="line"></span><br><span class="line">sample_freq = fftpack.fftfreq(sig.size, d=time_step)</span><br><span class="line">sig_fft = fftpack.fft(sig)</span><br></pre></td></tr></table></figure><p>因为生成的幂是对称的, 寻找频率只需要使用频谱为正的部分:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pidxs = np.where(sample_freq &gt; <span class="number">0</span>)</span><br><span class="line">freqs = sample_freq[pidxs]</span><br><span class="line">power = np.<span class="built_in">abs</span>(sig_fft)[pidxs]</span><br></pre></td></tr></table></figure><p>信号频率可通过如下方式获得:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">freq = freqs[power.argmax()]</span><br></pre></td></tr></table></figure><p>滤去傅里叶变化后的信号中的高频噪声:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sig_fft[np.<span class="built_in">abs</span>(sample_freq) &gt; freq] = <span class="number">0</span></span><br></pre></td></tr></table></figure><p>去噪后信号可通过如下方式计算： scipy.fftpack.ifft() 方法:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">main_sig = fftpack.ifft(sig_fft)</span><br></pre></td></tr></table></figure><p>绘制成图:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">plt.figure()</span><br><span class="line">plt.plot(time_vec, sig)</span><br><span class="line">plt.plot(time_vec, main_sig, linewidth=<span class="number">3</span>)</span><br><span class="line">plt.xlabel(<span class="string">&#x27;Time [s]&#x27;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&#x27;Amplitude&#x27;</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/scipy/scipy01.png" alt="scipy01"></p><h2 id="scipy-optimize-优化及拟合"><a href="#scipy-optimize-优化及拟合" class="headerlink" title="scipy.optimize 优化及拟合"></a>scipy.optimize 优化及拟合</h2><p>scipy.optimize 模块提供了求解函数最小值, 曲线拟合等算法的实现.</p><h3 id="求函数的最小值"><a href="#求函数的最小值" class="headerlink" title="求函数的最小值"></a>求函数的最小值</h3><p>定义下面的函数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> x**<span class="number">2</span> + <span class="number">10</span>*np.sin(x)</span><br></pre></td></tr></table></figure><p>画出其图像:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">x = np.arange(-<span class="number">10</span>, <span class="number">10</span>, <span class="number">0.1</span>)</span><br><span class="line">plt.plot(x, f(x))</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/scipy/scipy02.png" alt="scipy02"></p><p>此函数有一个全局最小值, 在 x &#x3D; -1 附近, 还有一个局部最小值, 在 x &#x3D; 5 附近.</p><p>一个常用的求解此函数最小值的方法是确定初始点, 然后执行梯度下降算法. BFGS算法是一个很好的适用于此的方法:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>optimize.fmin_bfgs(f, -<span class="number">1</span>)</span><br><span class="line">Optimization terminated successfully.</span><br><span class="line">         Current function value: -<span class="number">7.945823</span></span><br><span class="line">         Iterations: <span class="number">3</span></span><br><span class="line">         Function evaluations: <span class="number">15</span></span><br><span class="line">         Gradient evaluations: <span class="number">5</span></span><br><span class="line">array([-<span class="number">1.30644012</span>])</span><br></pre></td></tr></table></figure><p>这个方法的缺陷在于有时候可能会被困在一个局部最小值, 而得不到全局的最小值. 这取决与初始点的选取:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>optimize.fmin_bfgs(f, <span class="number">5</span>)</span><br><span class="line">optimize.fmin_bfgs(f, <span class="number">5</span>)</span><br><span class="line">Optimization terminated successfully.</span><br><span class="line">         Current function value: <span class="number">8.315586</span></span><br><span class="line">         Iterations: <span class="number">5</span></span><br><span class="line">         Function evaluations: <span class="number">18</span></span><br><span class="line">         Gradient evaluations: <span class="number">6</span></span><br><span class="line">array([<span class="number">3.83746712</span>])</span><br></pre></td></tr></table></figure><p>如果我们不知道全局最小值的邻近数值, 就需要使用那些可以实现全局最优化的算法.比如 optimize.basinhopping() 包含一个求解局部最小值的算法和一个为该算法提供随机初始点的函数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>optimize.basinhopping(f, <span class="number">0</span>)</span><br><span class="line">                        fun: -<span class="number">7.945823375615284</span></span><br><span class="line"> lowest_optimization_result:       fun: -<span class="number">7.945823375615284</span></span><br><span class="line"> hess_inv: array([[<span class="number">0.08579758</span>]])</span><br><span class="line">      jac: array([<span class="number">0.</span>])</span><br><span class="line">  message: <span class="string">&#x27;Optimization terminated successfully.&#x27;</span></span><br><span class="line">     nfev: <span class="number">15</span></span><br><span class="line">      nit: <span class="number">3</span></span><br><span class="line">     njev: <span class="number">5</span></span><br><span class="line">   status: <span class="number">0</span></span><br><span class="line">  success: <span class="literal">True</span></span><br><span class="line">        x: array([-<span class="number">1.30644002</span>])</span><br><span class="line">                    message: [<span class="string">&#x27;requested number of basinhopping iterations completed successfully&#x27;</span>]</span><br><span class="line">      minimization_failures: <span class="number">0</span></span><br><span class="line">                       nfev: <span class="number">1497</span></span><br><span class="line">                        nit: <span class="number">100</span></span><br><span class="line">                       njev: <span class="number">499</span></span><br><span class="line">                          x: array([-<span class="number">1.30644002</span>])</span><br></pre></td></tr></table></figure><p>另外一个可用的, 但不怎么高效的全局最优化算法是 scipy.optimize.brute().</p><p>为了找到局部最小值, 可以把变量限制在区间 (0, 10) 中, 使用 scipy.optimize.fminbound():</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>optimize.fminbound(f, <span class="number">0</span>, <span class="number">10</span>)</span><br><span class="line"><span class="number">3.8374671194983834</span></span><br></pre></td></tr></table></figure><h3 id="寻找函数的零点"><a href="#寻找函数的零点" class="headerlink" title="寻找函数的零点"></a>寻找函数的零点</h3><p>例如, 求解 f(x) &#x3D; 0 的零点, 其中 f 是我们在上面用到的函数. 可以使用 scipy.optimize.fsolve():</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>optimize.fsolve(f, <span class="number">1</span>)</span><br><span class="line">array([<span class="number">0.</span>])</span><br></pre></td></tr></table></figure><p>从上面的图像中我们可以看出函数 f 包含两个零点. 第二个零点在 x &#x3D; -2.5 附近. 通过调整初始值, 我们可以找出精确解:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>optimize.fsolve(f, -<span class="number">2.5</span>)</span><br><span class="line">array([-<span class="number">2.47948183</span>])</span><br></pre></td></tr></table></figure><h3 id="曲线拟合"><a href="#曲线拟合" class="headerlink" title="曲线拟合"></a>曲线拟合</h3><p>假设我们现在有从函数 f 中采样得到的含有一些噪声的数据:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">xdata = np.arange(-<span class="number">10</span>, <span class="number">10</span>)</span><br><span class="line">ydata = f(xdata) + np.random.randn(xdata.size)</span><br></pre></td></tr></table></figure><p>我们已经知道了函数的形式是大概是:<br> $$ax^2 + b \sin(x) + c $$<br>但不知道每一项系数的大小. 我们可以使用最小二乘算法来进行曲线拟合得到系数的值. 首先定义需要进行拟合的函数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">f2</span>(<span class="params">x, a, b, c</span>):</span><br><span class="line">     <span class="keyword">return</span> a*x**<span class="number">2</span> + b*np.sin(x) + c</span><br></pre></td></tr></table></figure><p>接着使用 scipy.optimize.curve_fit() 来求解 a, b, c:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>params, params_covariance = optimize.curve_fit(f2, xdata, ydata, guess)</span><br><span class="line">&gt;&gt;&gt;params</span><br><span class="line">array([ <span class="number">0.98955234</span>, <span class="number">10.0579609</span> ,  <span class="number">0.15395008</span>])</span><br></pre></td></tr></table></figure><p>现在我们已经找到了函数 f 的最小值和零点, 并且对采自这个函数的数据进行了曲线拟合的实验. 我们可以把所有的结果呈现在同一张图像上: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">fig, ax = plt.subplots()</span><br><span class="line">plt.rcParams[<span class="string">&#x27;axes.unicode_minus&#x27;</span>]=<span class="literal">False</span>  <span class="comment"># 解决负号乱码问题</span></span><br><span class="line">ax.plot(x, f(x), <span class="string">&#x27;b-&#x27;</span>, label=<span class="string">&quot;f(x)&quot;</span>)</span><br><span class="line">ax.plot(x, f2(x, *params), <span class="string">&#x27;r--&#x27;</span>, label=<span class="string">&quot;拟合结果&quot;</span>, )</span><br><span class="line">xmins = np.array([optimize.fmin_bfgs(f, -<span class="number">1</span>), optimize.fmin_bfgs(f, <span class="number">5</span>)])</span><br><span class="line">ax.plot(xmins, f(xmins), <span class="string">&#x27;go&#x27;</span>, label=<span class="string">&quot;极小值&quot;</span>)</span><br><span class="line">roots = np.array([optimize.fsolve(f, <span class="number">1</span>), optimize.fsolve(f, -<span class="number">2.5</span>)])</span><br><span class="line">ax.plot(roots, f(roots), <span class="string">&#x27;kv&#x27;</span>, label=<span class="string">&quot;零点&quot;</span>)</span><br><span class="line">ax.legend(prop=&#123;<span class="string">&#x27;family&#x27;</span> : <span class="string">&#x27;STSong&#x27;</span>, <span class="string">&#x27;size&#x27;</span> : <span class="number">10</span>&#125;)</span><br><span class="line">ax.set_xlabel(<span class="string">&#x27;x&#x27;</span>)</span><br><span class="line">ax.set_ylabel(<span class="string">&#x27;f(x)&#x27;</span>)</span><br></pre></td></tr></table></figure><p><img src="/assets/blogImg/scipy/scipy03.png" alt="scipy03"></p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>1. <a href="https://docs.scipy.org/doc/scipy/reference/index.html">SciPy &mdash; SciPy v1.4.1 Reference Guide</a></em><br><em>2. <a href="https://hal.inria.fr/hal-01206546">Varoquaux, Gaël, et al. Scipy Lecture Notes. 2015.</a></em></p>]]></content>
    
    
    <summary type="html">这里主要是对scipy库中的io, special, linalg, fftpack, optimize模块作简单介绍.</summary>
    
    
    
    <category term="科学计算" scheme="https://zzz5.github.io/categories/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="scipy" scheme="https://zzz5.github.io/tags/scipy/"/>
    
  </entry>
  
  <entry>
    <title>Python设计模式(1)</title>
    <link href="https://zzz5.github.io/2020/05/28/python/others/python-designPatterns-01/"/>
    <id>https://zzz5.github.io/2020/05/28/python/others/python-designPatterns-01/</id>
    <published>2020-05-28T06:36:00.000Z</published>
    <updated>2020-07-10T06:16:14.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="设计模式简介"><a href="#设计模式简介" class="headerlink" title="设计模式简介"></a>设计模式简介</h2><h3 id="设计模式是什么"><a href="#设计模式是什么" class="headerlink" title="设计模式是什么"></a>设计模式是什么</h3><p>设计模式(Design pattern)代表了最佳的实践, 通常被有经验的面向对象的软件开发人员所采用. 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案. 这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的.</p><p>设计模式是一套被反复使用的, 多数人知晓的, 经过分类编目的, 代码设计经验的总结. 使用设计模式是为了重用代码, 让代码更容易被他人理解, 保证代码可靠性. 毫无疑问, 设计模式于己于人于系统都是多赢的. 设计模式使代码编制真正工程化. 设计模式是软件工程的基石, 如同大厦的一块块砖石一样, 项目中合理地运用设计模式可以完美地解决很多问题. 每种模式在现实中都有相应的原理来与之对应, 每种模式都描述了一个在我们周围不断重复发生的问题, 以及该问题的核心解决方案, 这也是设计模式能被广泛应用的原因.</p><h3 id="什么是GOF-四人帮-全拼-Gang-of-Four"><a href="#什么是GOF-四人帮-全拼-Gang-of-Four" class="headerlink" title="什么是GOF(四人帮, 全拼 Gang of Four)"></a>什么是GOF(四人帮, 全拼 Gang of Four)</h3><p>在 1994 年, 由 Erich Gamma, Richard Helm, Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名: 设计模式 - 可复用的面向对象软件元素) 的书, 该书首次提到了软件开发中设计模式的概念.</p><p>四位作者合称 GOF(四人帮, 全拼 Gang of Four). 他们所提出的设计模式主要是基于以下的面向对象设计原则:</p><ul><li>对接口编程而不是对实现编程。</li><li>优先使用对象组合而不是继承。</li></ul><h3 id="设计模式的使用"><a href="#设计模式的使用" class="headerlink" title="设计模式的使用"></a>设计模式的使用</h3><p>设计模式在软件开发中的两个主要用途.</p><h4 id="开发人员的共同平台"><a href="#开发人员的共同平台" class="headerlink" title="开发人员的共同平台"></a>开发人员的共同平台</h4><p>设计模式提供了一个标准的术语系统. 且具体到特定的情景. 例如, 单例设计模式意味着使用单个对象, 这样所有熟悉单例设计模式的开发人员都能使用单个对象, 并且可以通过这种方式告诉对方, 程序使用的是单例模式.</p><h4 id="最佳的实践"><a href="#最佳的实践" class="headerlink" title="最佳的实践"></a>最佳的实践</h4><p>设计模式已经经历了很长一段时间的发展, 它们提供了软件开发过程中面临的一般问题的最佳解决方案, 学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计.</p><h3 id="设计模式的类型"><a href="#设计模式的类型" class="headerlink" title="设计模式的类型"></a>设计模式的类型</h3><p>设计模式的类型大致可以分为三大类: 创建型模式(Creational Patterns), 结构型模式(Structural Patterns), 行为型模式(Behavioral Patterns). 另外还要一些其他设计模式, 我们后续也会介绍到.</p><h4 id="创建型模式-Creational-Patterns"><a href="#创建型模式-Creational-Patterns" class="headerlink" title="创建型模式(Creational Patterns)"></a>创建型模式(Creational Patterns)</h4><p>这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式, 而不是使用 new 等运算符直接实例化对象. 这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活.</p><table><thead><tr><th>模式</th><th>描述</th></tr></thead><tbody><tr><td>抽象工厂模式(Abstract Factory Pattern)</td><td>对特定工厂使用泛型函数</td></tr><tr><td>borg模式(Borg Pattern)</td><td>实例间状态共享的单例模式</td></tr><tr><td>建造者模式(Builder Pattern)</td><td>生成器对象接收参数并返回构造的对象，而不是使用多个构造函数</td></tr><tr><td>工厂模式(Factory Pattern)</td><td>委托专用函数&#x2F;方法来创建实例</td></tr><tr><td>缓式评估模式(Lazy Evaluation Pattern)</td><td>Python中延迟计算的属性模式</td></tr><tr><td>对象池模式(Pool Pattern)</td><td>预实例化并维护一组相同类型的实例</td></tr><tr><td>原型模式(Prototype Pattern)</td><td>为新实例使用原型的工厂和克隆(如果实例化成本较高)</td></tr><tr><td>单例模式(Singleton Pattern)</td><td>确保只有单个对象被创建。</td></tr></tbody></table><h4 id="结构型模式-Structural-Patterns"><a href="#结构型模式-Structural-Patterns" class="headerlink" title="结构型模式(Structural Patterns)"></a>结构型模式(Structural Patterns)</h4><p>这些设计模式关注类和对象的组合. 继承的概念被用来组合接口和定义组合对象获得新功能的方式.</p><table><thead><tr><th>模式</th><th>描述</th></tr></thead><tbody><tr><td>三层架构(3-tier Architecture)</td><td>数据, 业务逻辑, 呈现 分离(严格关系)</td></tr><tr><td>适配器模式(Adapter Pattern)</td><td>使一个接口适应另一个接口</td></tr><tr><td>桥接模式(Bridge Pattern)</td><td>提供抽象化和实现化之间的桥接结构，来实现二者的解耦</td></tr><tr><td>组合模式(Composite Pattern)</td><td>允许客户端统一处理各个对象和组合</td></tr><tr><td>装饰器模式(Decorator Pattern)</td><td>向一个现有的对象添加新的功能, 同时又不改变其结构</td></tr><tr><td>外观模式(Facade Pattern)</td><td>隐藏系统的复杂性, 并向客户端提供了一个客户端可以访问系统的接口</td></tr><tr><td>过滤器模式(Filter Pattern)</td><td>允许开发人员使用不同的标准来过滤一组对象</td></tr><tr><td>享元模式(Flyweight Pattern)</td><td>重用具有相似或相同状态的对象的现有实例</td></tr><tr><td>前端控制器(Front Controller)</td><td>进入应用程序的单个处理程序请求</td></tr><tr><td>MVC(Model View Controller)</td><td>模式, 视图, 控制 分离(非严格关系)</td></tr><tr><td>代理模式(Proxy Pattern)</td><td>对象将操作传递给其他对象</td></tr></tbody></table><h4 id="行为型模式-Behavioral-Patterns"><a href="#行为型模式-Behavioral-Patterns" class="headerlink" title="行为型模式(Behavioral Patterns)"></a>行为型模式(Behavioral Patterns)</h4><p>这些设计模式特别关注对象之间的通信.</p><table><thead><tr><th>模式</th><th>描述</th></tr></thead><tbody><tr><td>责任链模式(Chain of Responsibility Pattern)</td><td>应用一系列链式的处理程序来处理数据</td></tr><tr><td>Catalog</td><td>根据构造参数调用不同的专用方法</td></tr><tr><td>方法链(Chain Method)</td><td>继续回调下一个对象方法</td></tr><tr><td>命令模式(Command Pattern)</td><td>捆绑命令和参数以供稍后调用</td></tr><tr><td>迭代器模式(Iterator Pattern)</td><td>遍历容器并访问容器的元素</td></tr><tr><td>中介者模式(Mediator Pattern)</td><td>知道如何连接其他对象并充当代理的对象</td></tr><tr><td>备忘录模式(Memento Pattern)</td><td>保存一个对象的某个状态, 以便在适当的时候恢复对象</td></tr><tr><td>观察者模式(Observer Pattern)</td><td>提供回调以通知数据的更改</td></tr><tr><td>发布&#x2F;订阅模式(Publish&#x2F;Subscribe Pattern)</td><td>比观察者模式多一个中间人</td></tr><tr><td>注册模式(Registry Pattern)</td><td>跟踪给定类的所有子类</td></tr><tr><td>规约模式(Specification Pattern)</td><td>通过使用布尔逻辑将业务规则链接在一起，可以重新组合业务规则</td></tr><tr><td>状态模式(State Pattern)</td><td>类的行为是基于它的状态改变的</td></tr><tr><td>策略模式(Strategy Pattern)</td><td>一个类的行为或其算法可以在运行时更改</td></tr><tr><td>模板模式(Template Pattern)</td><td>一个抽象类公开定义了执行它的方法的模板, 它的子类可以按需要重写方法实现</td></tr><tr><td>访问者模式(Visitor Pattern)</td><td>使用了一个访问者类, 它改变了元素类的执行算法</td></tr></tbody></table><h4 id="其它模式-Others"><a href="#其它模式-Others" class="headerlink" title="其它模式(Others)"></a>其它模式(Others)</h4><table><thead><tr><th>模式</th><th>描述</th></tr></thead><tbody><tr><td>依赖注入(Dependency Injection)</td><td>依赖项注入的3种变体</td></tr><tr><td>委托模式(Delegation Pattern)</td><td>对象通过委托给第二个对象来处理请求</td></tr><tr><td>黑板模式(Blackboard Pattern)</td><td>架构模型, 集合不同子系统知识构建解决方案, AI方法-非四人帮模式</td></tr><tr><td>Graph Search</td><td>绘图算法-非四人帮模式</td></tr><tr><td>hsm</td><td>分层状态机-非四人帮模式</td></tr></tbody></table><h3 id="设计模式的六大原则"><a href="#设计模式的六大原则" class="headerlink" title="设计模式的六大原则"></a>设计模式的六大原则</h3><blockquote><p>1.开闭原则(Open Close Principle)<br>开闭原则的意思是：对扩展开放, 对修改关闭. 在程序需要进行拓展的时候, 不能去修改原有的代码, 实现一个热插拔的效果. 简言之, 是为了使程序的扩展性好, 易于维护和升级. 想要达到这样的效果, 我们需要使用接口和抽象类, 后面的具体设计中我们会提到这点.</p></blockquote><blockquote><p>2.里氏代换原则(Liskov Substitution Principle)<br>里氏代换原则是面向对象设计的基本原则之一.  里氏代换原则中说, 任何基类可以出现的地方, 子类一定可以出现. LSP 是继承复用的基石, 只有当派生类可以替换掉基类, 且软件单位的功能不受到影响时, 基类才能真正被复用, 而派生类也能够在基类的基础上增加新的行为. 里氏代换原则是对开闭原则的补充. 实现开闭原则的关键步骤就是抽象化, 而基类与子类的继承关系就是抽象化的具体实现, 所以里氏代换原则是对实现抽象化的具体步骤的规范.</p></blockquote><blockquote><p>3.依赖倒转原则(Dependence Inversion Principle)<br>这个原则是开闭原则的基础, 具体内容：针对接口编程, 依赖于抽象而不依赖于具体.</p></blockquote><blockquote><p>4.接口隔离原则(Interface Segregation Principle)<br>这个原则的意思是：使用多个隔离的接口, 比使用单个接口要好. 它还有另外一个意思是：降低类之间的耦合度. 由此可见, 其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想, 它强调降低依赖, 降低耦合.</p></blockquote><blockquote><p>5.迪米特法则, 又称最少知道原则(Demeter Principle)<br>最少知道原则是指：一个实体应当尽量少地与其他实体之间发生相互作用, 使得系统功能模块相对独立. </p></blockquote><blockquote><p>6.合成复用原则(Composite Reuse Principle)<br>合成复用原则是指：尽量使用合成&#x2F;聚合的方式, 而不是使用继承.</p></blockquote><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>1. <a href="https://www.runoob.com/design-pattern/design-pattern-tutorial.html">设计模式 | 菜鸟教程</a></em><br><em>2. <a href="https://github.com/faif/python-patterns">python-patterns</a></em></p>]]></content>
    
    
    <summary type="html">简单介绍python的各种设计模式.</summary>
    
    
    
    <category term="python" scheme="https://zzz5.github.io/categories/python/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="design pattern" scheme="https://zzz5.github.io/tags/design-pattern/"/>
    
  </entry>
  
  <entry>
    <title>发布Python包到PyPi</title>
    <link href="https://zzz5.github.io/2020/05/28/python/others/python-pipy/"/>
    <id>https://zzz5.github.io/2020/05/28/python/others/python-pipy/</id>
    <published>2020-05-28T02:00:00.000Z</published>
    <updated>2020-07-02T13:16:16.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="\assets\js\Meting.min.js"></script><h2 id="PyPI简介"><a href="#PyPI简介" class="headerlink" title="PyPI简介"></a>PyPI简介</h2><p>Python Package Index, 缩写为PyPI, 是Python的官方第三方软件存储库. PyPI主要以称为sdist(源代码发行版)或预编译“wheel”的归档形式托管Python包. PyPI推荐使用pip包管理器来下载第三方库. pip可正常工作在Windows, Mac OS, Unix&#x2F;Linux等操作系统上. 这里我们要说的是如何将python包发布到PyPi.</p><h2 id="规范项目结构"><a href="#规范项目结构" class="headerlink" title="规范项目结构"></a>规范项目结构</h2><p>这里建议按以下规格规范项目结构:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">myProject/</span><br><span class="line">  myproject/</span><br><span class="line">    __init__.py</span><br><span class="line">  tests/</span><br><span class="line">  setup.py</span><br><span class="line">  LICENSE.txt</span><br><span class="line">  requirements.txt</span><br><span class="line">  README.md</span><br></pre></td></tr></table></figure><p>详情可以转到 <a href="https://zzz5.xyz/2019/07/31/python/rules/python-rule-structure/">Python项目目录结构</a>.</p><h2 id="编辑-setup-py"><a href="#编辑-setup-py" class="headerlink" title="编辑 setup.py"></a>编辑 setup.py</h2><p>setup.py 是 setuptools 的构建脚本. 它能告诉 setuptools 有关你的包的信息(如名称和版本)以及要包括哪些代码文件.<br>打开setup.py并输入以下内容. 更改包名称以确保你有一个唯一的包名称, 不会与其他人上传的包名称冲突.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> setuptools</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;README.md&quot;</span>, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> fh:</span><br><span class="line">    long_description = fh.read()</span><br><span class="line"></span><br><span class="line">setuptools.setup(</span><br><span class="line">    name=<span class="string">&quot;example_name&quot;</span>,  <span class="comment"># 你的包名称</span></span><br><span class="line">    version=<span class="string">&quot;0.0.1&quot;</span>,  <span class="comment"># 你的包版本    </span></span><br><span class="line">    author=<span class="string">&quot;Example Author&quot;</span>,  <span class="comment"># 作者</span></span><br><span class="line">    author_email=<span class="string">&quot;author@example.com&quot;</span>,  <span class="comment"># 作者邮箱</span></span><br><span class="line">    description=<span class="string">&quot;A small example package&quot;</span>,  <span class="comment"># 一句话形容包</span></span><br><span class="line">    long_description=long_description,  <span class="comment"># 是对该包的详细说明. 详细描述一般是从readme.md加载的</span></span><br><span class="line">    long_description_content_type=<span class="string">&quot;text/markdown&quot;</span>,  <span class="comment"># readme的格式</span></span><br><span class="line">    url=<span class="string">&quot;https://github.com/pypa/sampleproject&quot;</span>,  <span class="comment"># 项目地址</span></span><br><span class="line">    packages=setuptools.find_packages(),  <span class="comment"># 是应该包含在发行包中的所有Python导入包的列表. 这样写是自动发现.</span></span><br><span class="line">    classifiers=[</span><br><span class="line">        <span class="string">&quot;Programming Language :: Python :: 3&quot;</span>,</span><br><span class="line">        <span class="string">&quot;License :: OSI Approved :: MIT License&quot;</span>,</span><br><span class="line">        <span class="string">&quot;Operating System :: OS Independent&quot;</span>,</span><br><span class="line">    ],</span><br><span class="line">    python_requires=<span class="string">&#x27;&gt;=3.6&#x27;</span>,  <span class="comment"># 需要的python版本</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>setup()有很多参数, 下面来详细介绍:</p><ul><li>name:  你的包名称. 可以包含字母, 数字, _ , 和 -. 必须没有和别人上传的包名称冲突.</li><li>version: 你的包版本, 可以在 PEP 440 上看到更多详细介绍.</li><li>author 和 author_email: 作者名和作者邮箱.</li><li>description: 一句话形容这个包.</li><li>long_description: 对该包的详细介绍, 这会展示在 PyPI 网页上. long description 默认会从 README.md 里加载.</li><li>long_description_content_type: 加载详细介绍的格式. 这里是 Markdown.</li><li>url: 项目的网址.</li><li>packages: 是应该包含在发行包中的所有Python导入包的列表. 除了手动列出各个依赖包外, 我们可以使用 find_packages() 去自动寻找依赖包列表.</li><li>classifiers: 关于你的包的额外参数. 在这个例子里, 这个包只能运行在Python3环境中, 使用的是 MIT 项目许可声明, 可以在任意操作系统运行. 关于 classifiers 更详细的介绍, 可以浏览 <a href="https://pypi.org/classifiers/">https://pypi.org/classifiers/</a> .</li></ul><h2 id="安装发布依赖包并打包你的项目"><a href="#安装发布依赖包并打包你的项目" class="headerlink" title="安装发布依赖包并打包你的项目"></a>安装发布依赖包并打包你的项目</h2><p>确保安装 setuptools 和 wheel 两个包.</p><p><code>python3 -m pip install --user --upgrade setuptools wheel</code></p><p>在 setup.py 所在的文件夹内运行以下命令:</p><p><code>python3 setup.py sdist bdist_wheel</code></p><p>这个命令会在 dist 目录创建两个文件:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">dist/</span><br><span class="line">  example_name-0.0.1-py3-none-any.whl</span><br><span class="line">  example_name-0.0.1.tar.gz</span><br></pre></td></tr></table></figure><p>tar.gz文件是源代码包, 而.whl文件是构建的发行版. 较新的pip版本优先安装已构建的发行版, 但如果需要, 将退回到源代码包. </p><h2 id="上传你的发行包到PyPI"><a href="#上传你的发行包到PyPI" class="headerlink" title="上传你的发行包到PyPI"></a>上传你的发行包到PyPI</h2><p>现在, 你应该创建一个PyPI账号, 以便能够安全地上传项目 <a href="https://pypi.org/account/register/">https://pypi.org/account/register/</a> .</p><p>你现在需要安装 twine 包来上传你的包. </p><p><code>python3 -m pip install --user --upgrade twine</code></p><p>安装完成后你可以用以下命令上传 dist 目录下的所有文件:</p><p><code>python3 -m twine upload dist/*</code></p><p>twine会默认上传到 <a href="https://pypi.org/">https://pypi.org/</a> .</p><p>接着会让你输入你的PyPI的账号和密码, 确认无误后就会上传到 PyPI 了.</p><p>之后可以用 <code>pip install your_package_name</code> 安装你上传的包了.</p><p>你在 <a href="https://pypi.org/">PyPI官网</a> 上登陆账号后, 也可以在线管理你的包.</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><em>1. <a href="https://packaging.python.org/tutorials/packaging-projects/">Python Packaging User Guide</a></em></p>]]></content>
    
    
    <summary type="html">发布Python包到PyPi的详细操作步骤.</summary>
    
    
    
    <category term="python" scheme="https://zzz5.github.io/categories/python/"/>
    
    
    <category term="python" scheme="https://zzz5.github.io/tags/python/"/>
    
    <category term="pypi" scheme="https://zzz5.github.io/tags/pypi/"/>
    
  </entry>
  
</feed>
