<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Dataset on Abel</title>
    <link>https://chen-huaneng.github.io/categories/dataset/</link>
    <description>Recent content in Dataset on Abel</description>
    <generator>Hugo -- 0.148.2</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 12 Aug 2025 15:02:24 +0800</lastBuildDate>
    <atom:link href="https://chen-huaneng.github.io/categories/dataset/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>【Dataset】TSPDroneLIB by Bogyrbayeva et al. (2023)</title>
      <link>https://chen-huaneng.github.io/posts/2025/2025-08-12-tspdronelib/</link>
      <pubDate>Tue, 12 Aug 2025 15:02:24 +0800</pubDate>
      <guid>https://chen-huaneng.github.io/posts/2025/2025-08-12-tspdronelib/</guid>
      <description>&lt;h2 id=&#34;tspdronelib-by-bogyrbayeva-et-al-2023&#34;&gt;TSPDroneLIB by Bogyrbayeva et al. (2023)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/chkwon/TSPDroneLIB&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
TSPDroneLIB&lt;/a&gt; 仓库包含了用于 TSP-D 和 FSTSP 的数据集和相关链接。该仓库提到了 &lt;a href=&#34;https://chen-huaneng.github.io/2025/08/12/2025-08-12-2025-08-12-tsp-d-instances/&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
Dataset - TSP-D Instances by Bouman et al. (2018)&lt;/a&gt; 的数据集，另外包括了 &lt;a href=&#34;https://doi.org/10.1016/j.trc.2022.103981&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
Bogyrbayeva 等（2023）&lt;/a&gt;使用的数据集。相关的算法可以在 &lt;a href=&#34;https://github.com/chkwon/TSPDrone.jl&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
TSPDrone.jl&lt;/a&gt; 仓库中找到。有关数据集的字段说明可以在 &lt;a href=&#34;https://github.com/chkwon/TSPDroneLIB/blob/main/data/Bogyrbayeva/description.md&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
TSPDroneLIB/data/Bogyrbayeva/description.md&lt;/a&gt; 中找到。该数据集分类如下：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="tspdronelib-by-bogyrbayeva-et-al-2023">TSPDroneLIB by Bogyrbayeva et al. (2023)</h2>
<p><a href="https://github.com/chkwon/TSPDroneLIB"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSPDroneLIB</a> 仓库包含了用于 TSP-D 和 FSTSP 的数据集和相关链接。该仓库提到了 <a href="https://chen-huaneng.github.io/2025/08/12/2025-08-12-2025-08-12-tsp-d-instances/"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Dataset - TSP-D Instances by Bouman et al. (2018)</a> 的数据集，另外包括了 <a href="https://doi.org/10.1016/j.trc.2022.103981"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Bogyrbayeva 等（2023）</a>使用的数据集。相关的算法可以在 <a href="https://github.com/chkwon/TSPDrone.jl"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSPDrone.jl</a> 仓库中找到。有关数据集的字段说明可以在 <a href="https://github.com/chkwon/TSPDroneLIB/blob/main/data/Bogyrbayeva/description.md"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSPDroneLIB/data/Bogyrbayeva/description.md</a> 中找到。该数据集分类如下：</p>
<ul>
<li>
<p><code>Random：</code>包含了三个不同节点数量大小的数据集，分别为 $n = 20, 50, 100$，每个数据集包含了 100 个算例，每行表示包含横纵坐标的一个算例，遵循格式 $x_1,y_1,x_2,y_2,\dots,x_n,y_n$，即每行的每两个数一组组成一个节点的横纵坐标，同时 $x_1,y_1$ 表示仓库节点的横纵坐标。该数据集的生成方法为：在 $[1100]\times[1100]$ 的范围内，从均匀分布中随机抽取每个节点的横纵坐标，唯一的例外是仓库节点，其位置分布在 $[0,1]\times[0,1]$ 范围内，这意味着仓库总是位于角落。这种数据集的生成方法和 <a href="https://doi.org/10.1287/trsc.2017.0791"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Agatz 等（2018）</a>使用的数据集生成方法是一致的。</p>
</li>
<li>
<p><code>Amsterdam：</code>该数据集的数据格式和 <code>Random</code> 数据集相同，共有四种不同大小的算例，分别为 $n = 10, 20, 50, 100$。该数据集基于 <a href="https://doi.org/10.2139/ssrn.3480725"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Haider 等（2019）</a>研究中使用的电动汽车（electric vehicle, EV）停车位置数据集。这些位置反映了潜在顾客的位置，因为电动汽车通常停放在城市街道的路边充电器旁。为了适 <a href="https://doi.org/10.1016/j.trc.2022.103981"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Bogyrbayeva 等（2023）</a>的研究，该文章从整个 <code>Amsterdam</code> 数据集中随机选取仓库和客户节点来创建不同的问题算例。</p>
</li>
</ul>
<h3 id="tspdronejl-仓库中实现的-dps-算法和-drl-算法"><code>TSPDrone.jl</code> 仓库中实现的 DPS 算法和 DRL 算法</h3>
<p><a href="https://github.com/chkwon/TSPDrone.jl"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSPDrone.jl</a> 仓库如前所述，解决了单车辆配备单架无人机的 TSP-D，实现了 <a href="https://doi.org/10.1016/j.trc.2022.103981"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Bogyrbayeva 等（2023）</a>提出的 Divide-Partition-and-Search (DPS) 算法和 Deep Reinforcement Learning (DRL) 算法，其中 DPS 算法是基于  <a href="https://doi.org/10.1287/trsc.2017.0791"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Agatz 等（2018）</a>的 TSP-ep-all 算法和 <a href="https://doi.org/10.1287/ijoc.2018.0826"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Poikonen 等（2019）</a>的 divide-and-conquer 启发式算法开发的。</p>
<p>DPS 算法是 <a href="https://doi.org/10.1016/j.trc.2022.103981"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Bogyrbayeva 等（2023）</a>提出的一种基于分治策略的启发式算法，用于解决 TSP-D。其核心思想是将大规模问题分解为较小的子问题，并通过组合子问题的解来获得全局解。DPS 算法的主要步骤如下：</p>
<ol>
<li>
<p>Divide：将全部节点划分为多个子组，每个子组包含固定数量（由参数 $g$ 控制）的节点。例如，$g = 10$ 表示每个子组有 10 个节点。</p>
</li>
<li>
<p>Partition &amp; Search：在每个子组内，采用 <a href="https://doi.org/10.1287/trsc.2017.0791"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSP-ep-all 算法</a>进行分区。TSP-ep-all 算法通过如下步骤优化子组内的路径：</p>
<ol>
<li>
<p>生成初始 TSP 路径：使用 Concorde TSP Solver 得到卡车单独服务的初始路径。</p>
</li>
<li>
<p>动态规划分区：将路径中的节点划分为由卡车和无人机协同服务的子集，以最小化总时间。</p>
</li>
<li>
<p>局部搜索优化：进一步调整分区以提升解的质量。</p>
</li>
</ol>
</li>
<li>
<p>合并与全局优化：将所有子组的解合并为完整的路径，并进行全局优化（如调整子组边界或路径顺序）以消除局部最优的局限性。</p>
</li>
</ol>
<p>在 DPS 算法中，较大的子组 $g$（如 $g = 25$）会提升解的质量，但增加计算时间；较小的子组 $g$（如 $g = 10$）则相反。当 $g = N$（总节点数）时，DPS 退化为直接应用 TSP-ep-all 算法，不再划分子组。</p>
<p><code>TSPDrone.jl</code> 用 Julia 实现，初始 TSP 路径由 Concorde TSP Solver 生成，分区过程基于动态规划和局部搜索，使用该仓库 DPS 算法的步骤如下：</p>
<blockquote>
<p>学习 Julia 的课程参考 MIT 的 <a href="https://computationalthinking.mit.edu/"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Introduction to Computational Thinking</a></p></blockquote>
<ol>
<li>
<p>安装 <a href="https://julialang.org/"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Julia</a>，在命令行中输入 <code>julia</code> 进入 <code>Julia</code> 环境，输入命令安装必要的依赖：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-julia" data-lang="julia"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">]</span> <span class="n">add</span> <span class="n">https</span><span class="o">://</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">chkwon</span><span class="o">/</span><span class="n">TSPDrone</span><span class="o">.</span><span class="n">jl</span>
</span></span></code></pre></div></li>
<li>
<p>使用 DPS 算法需要提供顾客节点的 $x$ 和 $y$ 坐标，仓库的 $(x, y)$ 坐标需要是第一个元素，参数 <code>truck_cost_factor</code> 和参数 <code>drone_cost_factor</code> 分别代表卡车和无人机的成本因子，会乘以从横纵坐标中计算出来的欧氏距离来得到卡车和无人机的行驶成本。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-julia" data-lang="julia"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">using</span> <span class="n">TSPDrone</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">n</span> <span class="o">=</span> <span class="mi">10</span> 
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="n">rand</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="n">y</span> <span class="o">=</span> <span class="n">rand</span><span class="p">(</span><span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">truck_cost_factor</span> <span class="o">=</span> <span class="mf">1.0</span> 
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">drone_cost_factor</span> <span class="o">=</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">solve_tspd</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">truck_cost_factor</span><span class="p">,</span> <span class="n">drone_cost_factor</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nd">@show</span> <span class="n">result</span><span class="o">.</span><span class="n">total_cost</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nd">@show</span> <span class="n">result</span><span class="o">.</span><span class="n">truck_route</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nd">@show</span> <span class="n">result</span><span class="o">.</span><span class="n">drone_route</span><span class="p">;</span>
</span></span></code></pre></div><p>如果正常运行，会输出如下结果（根据随机数生成的结果可能会有所不同），其中节点 11 作为终止节点表示仓库节点（即终止节点的代号会在总的节点数量上 +1）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="ln">1</span><span class="cl">result.total_cost = 1.6022013835206805
</span></span><span class="line"><span class="ln">2</span><span class="cl">result.truck_route = [1, 4, 5, 2, 8, 6, 11]
</span></span><span class="line"><span class="ln">3</span><span class="cl">result.drone_route = [1, 9, 4, 10, 5, 7, 8, 3, 11]
</span></span></code></pre></div></li>
<li>
<p>或者也可以直接提供卡车和无人机的成本矩阵（即原本根据欧氏距离矩阵乘以成本因子得到的矩阵），同样，仓库节点被标记为节点 1：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-julia" data-lang="julia"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">using</span> <span class="n">TSPDrone</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">n</span> <span class="o">=</span> <span class="mi">10</span> 
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">dist_mtx</span> <span class="o">=</span> <span class="n">rand</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">dist_mtx</span> <span class="o">=</span> <span class="n">dist_mtx</span> <span class="o">+</span> <span class="n">dist_mtx</span><span class="o">&#39;</span> <span class="c"># symmetric distance only</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">truck_cost_mtx</span> <span class="o">=</span> <span class="n">dist_mtx</span> <span class="o">.*</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">drone_cost_mtx</span> <span class="o">=</span> <span class="n">truck_cost_mtx</span> <span class="o">.*</span> <span class="mf">0.5</span> 
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">solve_tspd</span><span class="p">(</span><span class="n">truck_cost_mtx</span><span class="p">,</span> <span class="n">drone_cost_mtx</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nd">@assert</span> <span class="n">size</span><span class="p">(</span><span class="n">truck_cost_mtx</span><span class="p">)</span> <span class="o">==</span> <span class="n">size</span><span class="p">(</span><span class="n">drone_cost_mtx</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
</span></span></code></pre></div></li>
<li>
<p>使用命令 <code>print_summary(result)</code> 可以输出结果总结：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="ln"> 1</span><span class="cl">julia&gt; print_summary(result)
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">Operation #1:
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  - Truck        = 0.17988883875173492 : [1, 3]
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  - Drone        = 0.11900891950265155 : [1, 4, 3]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  - Length       = 0.17988883875173492
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Operation #2:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  - Truck        = 0.4784476248243221 : [3, 9]
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  - Drone        = 0.27587675362585756 : [3, 7, 9]
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - Length       = 0.4784476248243221
</span></span><span class="line"><span class="ln">10</span><span class="cl">Operation #3:
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - Truck        = 0.445749847855226 : [9, 6]
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - Drone        = 0.48831605249544785 : [9, 10, 6]
</span></span><span class="line"><span class="ln">13</span><span class="cl">  - Length       = 0.48831605249544785
</span></span><span class="line"><span class="ln">14</span><span class="cl">Operation #4:
</span></span><span class="line"><span class="ln">15</span><span class="cl">  - Truck        = 0.9269158918021541 : [6, 5, 8, 11]
</span></span><span class="line"><span class="ln">16</span><span class="cl">  - Drone        = 0.8714473929102112 : [6, 2, 11]
</span></span><span class="line"><span class="ln">17</span><span class="cl">  - Length       = 0.9269158918021541
</span></span><span class="line"><span class="ln">18</span><span class="cl">Total Cost = 2.073568407873659
</span></span></code></pre></div></li>
<li>
<p>函数 <code>solve_tspd</code> 的可选参数包括：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-julia" data-lang="julia"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">n_groups</span><span class="o">::</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> 
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">method</span><span class="o">::</span><span class="kt">String</span> <span class="o">=</span> <span class="s">&#34;TSP-ep-all&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">flying_range</span><span class="o">::</span><span class="kt">Float64</span> <span class="o">=</span> <span class="n">MAX_DRONE_RANGE</span><span class="p">,</span> 
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">time_limit</span><span class="o">::</span><span class="kt">Float64</span> <span class="o">=</span> <span class="n">MAX_TIME_LIMIT</span>
</span></span></code></pre></div><ul>
<li>
<p><code>n_groups</code>：用于分治法的子组数量。例如，如果 $n = 100$ 且 <code>n_groups = 4</code>，则每组将有 25 个节点，然后将方法 <code>method</code> 应用于每个组。</p>
</li>
<li>
<p><code>method</code>：可以是以下的几种方法之一，<code>TSP-ep</code> 及其衍生方法（<code>TSP-ep-1p</code>、<code>TSP-ep-2p</code>、<code>TSP-ep-2opt</code> 和 <code>TSP-ep-all</code>）是基于 route-first，cluster-second 框架的启发式算法，由 <a href="https://doi.org/10.1287/trsc.2017.0791"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Agatz 等（2018）</a>提出，用于解决 TSP-D：</p>
<ul>
<li>
<p><code>TSP-ep</code> (Exact Partition)：使用 TSP 求解器（如 Concorde）生成最优 TSP 路径，然后以初始 TSP 路径为基础，通过精确划分算法（动态规划，时间复杂度为 $O(n^3)$）将 TSP 路径分割为卡车和无人机的协同路径。</p>
</li>
<li>
<p><code>TSP-ep-1p</code>：在 <code>TSP-ep</code> 的基础上，引入单点移动邻域搜索（One-Point Move），通过调整单个节点的位置优化路径。即先对初始路径进行 Exact Partition，然后遍历每个节点，尝试将其移动到路径中的其他位置，计算目标函数改进，然后接受最大的移动，迭代直至无法进一步优化。</p>
</li>
<li>
<p><code>TSP-ep-2p</code>：在 <code>TSP-ep</code> 的基础上，引入两点交换邻域搜索（Two-Point Swap），通过交换两个节点的位置优化路径。即先对初始路径进行 Exact Partition，然后遍历所有节点对，尝试交换两者的位置，计算目标函数改进，然后接受最大的交换，迭代直至无法进一步优化。</p>
</li>
<li>
<p><code>TSP-ep-2opt</code>：在 <code>TSP-ep</code> 的基础上，引入 2-opt 邻域搜索，通过反转路径中的子段优化路径。即先对初始路径进行 Exact Partition，然后遍历所有可能的路径子段，尝试反转子段并重新计算总时间，接受改进最大的反转操作，迭代直至无法进一步优化。</p>
</li>
<li>
<p><code>TSP-ep-all</code>：在 <code>TSP-ep</code> 的基础上，综合应用所有邻域搜索策略（<code>1p</code>、<code>2p</code>、<code>2opt</code>），通过多策略组合优化路径。即先对初始路径进行 Exact Partition，然后在每轮迭代中，尝试所有邻域操作（One-Point Move, Two-Point Swap, 2-opt），选择改进最大的操作，迭代直至无法进一步优化（表现最佳，但运行时间较长 $O(n^5)$）。</p>
</li>
</ul>
</li>
<li>
<p><code>flying_range</code>：无人机的飞行范围，默认值为 <code>Inf</code>。飞行范围与无人机成本矩阵中的值进行比较，即 <code>drone_cost_mtx</code> 或欧氏距离乘以 <code>drone_cost_factor</code>。</p>
</li>
<li>
<p><code>time_limit</code>：算法运行的总时间限制，以秒为单位。对于每个组，时间限制平均分配。例如，如果 <code>time_limit = 3600.0</code> 且 <code>n_groups = 5</code>，则每组的时间限制为 $3600/5=720$ 秒。</p>
</li>
</ul>
</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>【Dataset】TSP-D Instances by Bouman et al. (2018)</title>
      <link>https://chen-huaneng.github.io/posts/2025/2025-08-12-tsp-d-instances/</link>
      <pubDate>Tue, 12 Aug 2025 14:45:16 +0800</pubDate>
      <guid>https://chen-huaneng.github.io/posts/2025/2025-08-12-tsp-d-instances/</guid>
      <description>&lt;p&gt;Traveling Salesman Problem with Drones (TSP-D) 是经典 TSP 的拓展，它在 TSP 的基础上增加了无人机。无人机可以和车辆一起工作，或者自主起飞服务。根据无人机单次起飞降落过程中服务的顾客点数量的不同可以将问题分为单次起飞服务单个顾客点的和单次起飞服务多个顾客点。同样对无人机和车辆的会合点也有限制，即无人机只能在顾客节点或者仓库节点会合，因此会产生无人机和车辆之间互相等待的时间。TSP-D-Instances 就是用于 TSP-D 的数据集之一。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Traveling Salesman Problem with Drones (TSP-D) 是经典 TSP 的拓展，它在 TSP 的基础上增加了无人机。无人机可以和车辆一起工作，或者自主起飞服务。根据无人机单次起飞降落过程中服务的顾客点数量的不同可以将问题分为单次起飞服务单个顾客点的和单次起飞服务多个顾客点。同样对无人机和车辆的会合点也有限制，即无人机只能在顾客节点或者仓库节点会合，因此会产生无人机和车辆之间互相等待的时间。TSP-D-Instances 就是用于 TSP-D 的数据集之一。</p>
<p><a href="https://github.com/pcbouman-eur/TSP-D-Instances?tab=readme-ov-file"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSP-D-Instances</a> 仓库包含了用于 TSP-D 的二维数据集，即只有仓库和顾客节点的横纵坐标。在数据集中以符号<code>/*</code>开始，以符号<code>*/</code>结束的行是注释行，在读取数据时需要忽略。该数据集包含了 <a href="https://doi.org/10.1287/trsc.2017.0791"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Agatz 等（2018）</a>和 <a href="https://doi.org/10.1002/net.21864"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Bouman 等（2018）</a>所用的数据集。相关的解决代码（Java 实现）可以在 <a href="https://github.com/pcbouman-eur/Drones-TSP?tab=readme-ov-file"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Drones-TSP</a> 仓库中找到。在这个数据集中，两点之间的距离是欧几里得距离（Euclidean distance），即两点之间的距离是两点之间的直线距离。有关数据集字段说明可以参考数据集的注释和<a href="https://github.com/pcbouman-eur/TSP-D-Instances?tab=readme-ov-file"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
数据集仓库的说明</a>。该数据集分类如下（在所有的情况中，生成的第一个节点位置被选作仓库节点）：</p>
<ul>
<li>
<p><code>uniform</code>：每个节点的 $x$ 和 $y$ 坐标都是从取值范围为 $\{0,1,\dots,100\}$ 的独立均匀分布中随机生成的。</p>
</li>
<li>
<p><code>singlecenter</code>：对于每个位置，首先均匀地从区间 $[0,2\pi]$ 中抽取一个角度 $\alpha$，然后从一个均值为 0，标准差为 50 的正态分布中抽取一个距离 $r$，坐标 $(x,y)=(r\cdot \cos \alpha,r\cdot \sin\alpha)$，用这种方法生成的节点位置更有可能集中在中心点 $(0,0)$ 附近，比 <code>uniform</code> 的数据集更能模拟圆形城市中心的情况。</p>
</li>
<li>
<p><code>doublecenter</code>：生成方式和 <code>singlecenter</code> 类似，但在生成每个位置后，有 50% 的概率将其沿 $x$ 轴平移 200 个距离单位，这种方法生成的节点位置更有可能集中在两个中心点 $(0,0)$ 和 $(200,0)$ 附近，模拟了一个具有两个中心的城市的情况。</p>
</li>
<li>
<p><code>restricted</code>：在原有限制的基础上增加了一些额外的限制。</p>
<ul>
<li>
<p><code>maxradius</code>：增加了无人机不能飞行超过一定半径的限制。</p>
</li>
<li>
<p><code>novisit</code>：增加了无人机不能访问的顾客节点，比如以 <code>-novisit-20-rep_2.txt</code> 为后缀的文件表示有 20% 的顾客节点被随机选中用于表示无法被无人机访问的顾客节点，由于对于同一个数据来说，不同次数的随机生成会影响选中的顾客节点，因此 <code>rep_2</code> 表示第二次随机生成的数据。具体的不能被无人机访问的顾客节点由字段 <code>#NOVISIT</code> 表示，例如数据文件中的 <code>#NOVISIT 1</code> 表示第一个顾客节点不能被无人机访问，也即生成的节点数据中的第二行数据（因为默认生成的第一行数据是仓库节点）。</p>
</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>【Dataset】TSPLIB by Reinelt (1991)</title>
      <link>https://chen-huaneng.github.io/posts/2025/2025-08-12-tsplib/</link>
      <pubDate>Tue, 12 Aug 2025 14:32:03 +0800</pubDate>
      <guid>https://chen-huaneng.github.io/posts/2025/2025-08-12-tsplib/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
TSPLIB&lt;/a&gt; 是一个和 TSP 相关的数据集，包含了 Symmetric Traveling Salesman Problem (STSP)、Asymmetric Traveling Salesman Problem (ATSP)、Hamiltonian Cycle Problem (HCP)、Sequential Ordering Problem (SOP) 和 Capacitated Vehicle Routing Problem (CVRP) 的数据集，可以在 &lt;a href=&#34;http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/tsp95.pdf&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
tsp95.pdf&lt;/a&gt; 中查看有关数据集的完整说明文档。除了 HCP 以外，其他的问题都是定义在完全图上，且所有的距离都是以整数表示的。每个文件都包括说明部分和数据部分，说明部分包含了有关文件的格式和内容的信息。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
TSPLIB</a> 是一个和 TSP 相关的数据集，包含了 Symmetric Traveling Salesman Problem (STSP)、Asymmetric Traveling Salesman Problem (ATSP)、Hamiltonian Cycle Problem (HCP)、Sequential Ordering Problem (SOP) 和 Capacitated Vehicle Routing Problem (CVRP) 的数据集，可以在 <a href="http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/tsp95.pdf"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
tsp95.pdf</a> 中查看有关数据集的完整说明文档。除了 HCP 以外，其他的问题都是定义在完全图上，且所有的距离都是以整数表示的。每个文件都包括说明部分和数据部分，说明部分包含了有关文件的格式和内容的信息。</p>
<p><code>tsp95.pdf</code> 的内容如下：</p>
<iframe   src="/js/pdfjs/web/viewer.html?file=/pdf/tsp95.pdf" style='width:100%;height:800px'></iframe>
]]></content:encoded>
    </item>
    <item>
      <title>【Dataset】Amazon最后一公里物流配送数据集</title>
      <link>https://chen-huaneng.github.io/posts/2025/2025-08-12-amazon-delivery/</link>
      <pubDate>Tue, 12 Aug 2025 11:04:05 +0800</pubDate>
      <guid>https://chen-huaneng.github.io/posts/2025/2025-08-12-amazon-delivery/</guid>
      <description>&lt;h2 id=&#34;数据集来源及介绍&#34;&gt;数据集来源及介绍&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.kaggle.com/datasets/sujalsuthar/amazon-delivery-dataset&#34;
     
        target=&#34;_blank&#34; 
        rel=&#34;noopener nofollow noreferrer&#34; 
    &gt;
Amazon Delivery Dataset&lt;/a&gt; 是一个 Amazon 公司最后一公里物流运营情况的数据集，包含了超过 43632 次配送的多城市数据，数据字段包括订单详情、配送人员、天气、交通情况、配送仓库和配送地点的经纬度等信息。要将数据集转换为可以用于 TSP-D 的数据集，需要将数据集中的经纬度转换为欧几里得距离，即两点之间的直线距离，当然在此之前需要对原始数据集进行一些数据的预处理工作。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="数据集来源及介绍">数据集来源及介绍</h2>
<p><a href="https://www.kaggle.com/datasets/sujalsuthar/amazon-delivery-dataset"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Amazon Delivery Dataset</a> 是一个 Amazon 公司最后一公里物流运营情况的数据集，包含了超过 43632 次配送的多城市数据，数据字段包括订单详情、配送人员、天气、交通情况、配送仓库和配送地点的经纬度等信息。要将数据集转换为可以用于 TSP-D 的数据集，需要将数据集中的经纬度转换为欧几里得距离，即两点之间的直线距离，当然在此之前需要对原始数据集进行一些数据的预处理工作。</p>
<h2 id="数据预处理">数据预处理</h2>
<p>关于已知两点经纬度计算两点之间距离的方法，这里使用了 <a href="https://www.wikiwand.com/en/articles/Haversine_formula"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Haversine formula</a> ，但是要注意这个公式只是一个近似值，即假设地球是一个球体，而实际上地球是一个椭球体，不过对于不是精确到亚米级别的应用来说，这个公式的精度是足够的，误差在  $0.5\%$ 以内。如果需要更精确的方法可以参考 <a href="https://www.wikiwand.com/en/articles/Vincenty%27s_formulae"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Vincenty&rsquo;s formulae</a> 和 <a href="https://www.wikiwand.com/en/articles/Geographical_distance"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Geographical distance</a>。根据经纬度判断这个点是否在陆地的方法可以参考 Python 的库 <a href="https://pypi.org/project/global-land-mask/"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
global-land-mask</a>。</p>
<blockquote>
<p>在代码中使用的不是 $\arcsin$ 而是 $\arctan$，这是因为当 $\sin$ 值接近 1 时，直接使用 $\arcsin$ 可能导致精度问题，而 $\arctan$ 通过显式分离分子分母，可以使得计算更加稳定。$\arcsin$ 和 $\arctan$ 之间的转换可以参考<a href="https://zhuanlan.zhihu.com/p/111197233"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
实用反三角函数运算公式</a>。</p></blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">radians</span><span class="p">,</span> <span class="n">sin</span><span class="p">,</span> <span class="n">cos</span><span class="p">,</span> <span class="n">sqrt</span><span class="p">,</span> <span class="n">atan2</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">from</span> <span class="nn">global_land_mask</span> <span class="kn">import</span> <span class="n">globe</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># Haversine公式计算距离</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">def</span> <span class="nf">haversine</span><span class="p">(</span><span class="n">lat1</span><span class="p">,</span> <span class="n">lon1</span><span class="p">,</span> <span class="n">lat2</span><span class="p">,</span> <span class="n">lon2</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">R</span> <span class="o">=</span> <span class="mf">6371.393</span> <span class="c1"># 地球半径近似值</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">lat1_rad</span><span class="p">,</span> <span class="n">lon1_rad</span> <span class="o">=</span> <span class="n">radians</span><span class="p">(</span><span class="n">lat1</span><span class="p">),</span> <span class="n">radians</span><span class="p">(</span><span class="n">lon1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">lat2_rad</span><span class="p">,</span> <span class="n">lon2_rad</span> <span class="o">=</span> <span class="n">radians</span><span class="p">(</span><span class="n">lat2</span><span class="p">),</span> <span class="n">radians</span><span class="p">(</span><span class="n">lon2</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">dlon</span> <span class="o">=</span> <span class="n">lon2_rad</span> <span class="o">-</span> <span class="n">lon1_rad</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">dlat</span> <span class="o">=</span> <span class="n">lat2_rad</span> <span class="o">-</span> <span class="n">lat1_rad</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">a</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">dlat</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="n">cos</span><span class="p">(</span><span class="n">lat1_rad</span><span class="p">)</span><span class="o">*</span><span class="n">cos</span><span class="p">(</span><span class="n">lat2_rad</span><span class="p">)</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">dlon</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">return</span> <span class="n">R</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">atan2</span><span class="p">(</span><span class="n">sqrt</span><span class="p">(</span><span class="n">a</span><span class="p">),</span> <span class="n">sqrt</span><span class="p">(</span><span class="mi">1</span><span class="o">-</span><span class="n">a</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># 读取数据</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s1">&#39;amazon_delivery.csv&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># 步骤1: 筛选顾客节点&gt;=10的仓库</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="n">valid_warehouses</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">groupby</span><span class="p">([</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Store_Longitude&#39;</span><span class="p">])</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># 步骤2: 剔除顾客-仓库经纬度差&gt;=1的订单</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="n">valid_warehouses</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="p">[</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">]</span> <span class="o">-</span> <span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Drop_Latitude&#39;</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Store_Longitude&#39;</span><span class="p">]</span> <span class="o">-</span> <span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Drop_Longitude&#39;</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"># 步骤3: 计算距离并筛选&lt;=50公里的订单</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Distance&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="o">.</span><span class="n">apply</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="k">lambda</span> <span class="n">row</span><span class="p">:</span> <span class="n">haversine</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">],</span> <span class="n">row</span><span class="p">[</span><span class="s1">&#39;Store_Longitude&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">                          <span class="n">row</span><span class="p">[</span><span class="s1">&#39;Drop_Latitude&#39;</span><span class="p">],</span> <span class="n">row</span><span class="p">[</span><span class="s1">&#39;Drop_Longitude&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">axis</span><span class="o">=</span><span class="mi">1</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="n">valid_warehouses</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="p">[</span><span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Distance&#39;</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mi">50</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1"># 步骤4: 去重同一仓库下的重复顾客</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="n">valid_warehouses</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="o">.</span><span class="n">drop_duplicates</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="n">subset</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Store_Longitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Drop_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Drop_Longitude&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="c1"># 步骤5: 检查仓库是否在陆地</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1"># 提取唯一仓库坐标</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="n">warehouse_coords</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="p">[[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Store_Longitude&#39;</span><span class="p">]]</span><span class="o">.</span><span class="n">drop_duplicates</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="c1"># 使用global_land_mask检查陆地</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="n">warehouse_coords</span><span class="p">[</span><span class="s1">&#39;Is_Land&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">warehouse_coords</span><span class="o">.</span><span class="n">apply</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="k">lambda</span> <span class="n">row</span><span class="p">:</span> <span class="n">globe</span><span class="o">.</span><span class="n">is_land</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">],</span> <span class="n">row</span><span class="p">[</span><span class="s1">&#39;Store_Longitude&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="n">axis</span><span class="o">=</span><span class="mi">1</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="c1"># 合并陆地标记到原始数据</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="n">valid_warehouses</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="n">warehouse_coords</span><span class="p">[[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Store_Longitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Is_Land&#39;</span><span class="p">]],</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">    <span class="n">on</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Store_Longitude&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="n">how</span><span class="o">=</span><span class="s1">&#39;left&#39;</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="c1"># 步骤6: 剔除位于海里的仓库数据</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="n">final_data</span> <span class="o">=</span> <span class="n">valid_warehouses</span><span class="p">[</span><span class="n">valid_warehouses</span><span class="p">[</span><span class="s1">&#39;Is_Land&#39;</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">
</span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="c1"># 输出结果</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="n">final_data</span><span class="o">.</span><span class="n">to_excel</span><span class="p">(</span><span class="s1">&#39;amazon_delivery_filtered_data.xlsx&#39;</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="n">warehouse_stats</span> <span class="o">=</span> <span class="n">final_data</span><span class="o">.</span><span class="n">groupby</span><span class="p">([</span><span class="s1">&#39;Store_Latitude&#39;</span><span class="p">,</span> <span class="s1">&#39;Store_Longitude&#39;</span><span class="p">])</span><span class="o">.</span><span class="n">size</span><span class="p">()</span><span class="o">.</span><span class="n">reset_index</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">&#39;Count&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="n">warehouse_stats</span><span class="o">.</span><span class="n">sort_values</span><span class="p">(</span><span class="n">by</span><span class="o">=</span><span class="s1">&#39;Count&#39;</span><span class="p">,</span> <span class="n">ascending</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span><span class="o">.</span><span class="n">to_excel</span><span class="p">(</span><span class="s1">&#39;warehouse_stats.xlsx&#39;</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span></code></pre></div><p>首先删除不需要的数据列。接着通过将前面得到的数据导入到 <a href="https://www.google.com/maps/d/"
     
        target="_blank" 
        rel="noopener nofollow noreferrer" 
    >
Google Maps</a> 中，可以看到仓库数据大致可以聚类成 22 个簇，因此聚类时设置聚类数量为 22。然后将同一聚类的仓库节点和配送节点合并到同一个 Excel 文件中，因此总共会生成 22 个不同聚类的 Excel 文件。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">from</span> <span class="nn">sklearn.cluster</span> <span class="kn">import</span> <span class="n">KMeans</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 1. 读取Excel文件并提取需要的列</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">read_excel</span><span class="p">(</span><span class="s2">&#34;amazon_delivery_filtered_data.xlsx&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">filtered_df</span> <span class="o">=</span> <span class="n">df</span><span class="p">[[</span><span class="s2">&#34;Store_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Store_Longitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Drop_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Drop_Longitude&#34;</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 2. 提取唯一的仓库坐标用于聚类</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">store_coords</span> <span class="o">=</span> <span class="n">filtered_df</span><span class="p">[[</span><span class="s2">&#34;Store_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Store_Longitude&#34;</span><span class="p">]]</span><span class="o">.</span><span class="n">drop_duplicates</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 3. 使用KMeans进行聚类（已知聚类数=22）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">kmeans</span> <span class="o">=</span> <span class="n">KMeans</span><span class="p">(</span><span class="n">n_clusters</span><span class="o">=</span><span class="mi">22</span><span class="p">,</span> <span class="n">random_state</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">n_init</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">store_coords</span><span class="p">[</span><span class="s2">&#34;Cluster&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">kmeans</span><span class="o">.</span><span class="n">fit_predict</span><span class="p">(</span><span class="n">store_coords</span><span class="p">[[</span><span class="s2">&#34;Store_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Store_Longitude&#34;</span><span class="p">]])</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"># 4. 将聚类标签合并回原始数据</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">merged_df</span> <span class="o">=</span> <span class="n">filtered_df</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">store_coords</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">how</span><span class="o">=</span><span class="s2">&#34;left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">on</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;Store_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Store_Longitude&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"># 5. 创建保存结果的文件夹</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="n">output_dir</span> <span class="o">=</span> <span class="s2">&#34;clustered_nodes&#34;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">output_dir</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"># 6. 按聚类分组处理数据</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">for</span> <span class="n">cluster_id</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">22</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="c1"># 提取当前聚类的数据</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">cluster_data</span> <span class="o">=</span> <span class="n">merged_df</span><span class="p">[</span><span class="n">merged_df</span><span class="p">[</span><span class="s2">&#34;Cluster&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="n">cluster_id</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="c1"># 分离仓库节点和顾客节点</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="n">store_nodes</span> <span class="o">=</span> <span class="n">cluster_data</span><span class="p">[[</span><span class="s2">&#34;Store_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Store_Longitude&#34;</span><span class="p">]]</span><span class="o">.</span><span class="n">drop_duplicates</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">customer_nodes</span> <span class="o">=</span> <span class="n">cluster_data</span><span class="p">[[</span><span class="s2">&#34;Drop_Latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Drop_Longitude&#34;</span><span class="p">]]</span><span class="o">.</span><span class="n">drop_duplicates</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="c1"># 生成唯一ID</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="n">store_nodes</span><span class="p">[</span><span class="s2">&#34;ID&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;store_&#34;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">store_nodes</span><span class="p">))]</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="n">customer_nodes</span><span class="p">[</span><span class="s2">&#34;ID&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;customer_&#34;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">customer_nodes</span><span class="p">))]</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="c1"># 重命名列以匹配</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="n">store_nodes</span><span class="o">.</span><span class="n">rename</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;Store_Latitude&#34;</span><span class="p">:</span> <span class="s2">&#34;latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Store_Longitude&#34;</span><span class="p">:</span> <span class="s2">&#34;longitude&#34;</span><span class="p">},</span> <span class="n">inplace</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="n">customer_nodes</span><span class="o">.</span><span class="n">rename</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;Drop_Latitude&#34;</span><span class="p">:</span> <span class="s2">&#34;latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;Drop_Longitude&#34;</span><span class="p">:</span> <span class="s2">&#34;longitude&#34;</span><span class="p">},</span> <span class="n">inplace</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="c1"># 合并节点并整理列顺序</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="n">combined_nodes</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">concat</span><span class="p">([</span><span class="n">store_nodes</span><span class="p">,</span> <span class="n">customer_nodes</span><span class="p">],</span> <span class="n">ignore_index</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="n">combined_nodes</span> <span class="o">=</span> <span class="n">combined_nodes</span><span class="p">[[</span><span class="s2">&#34;ID&#34;</span><span class="p">,</span> <span class="s2">&#34;latitude&#34;</span><span class="p">,</span> <span class="s2">&#34;longitude&#34;</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="c1"># 输出每个聚类的节点数量</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">    <span class="n">total_nodes</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">store_nodes</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">customer_nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;聚类 </span><span class="si">{</span><span class="n">cluster_id</span><span class="si">}</span><span class="s2"> 有 </span><span class="si">{</span><span class="n">total_nodes</span><span class="si">}</span><span class="s2"> 个节点（仓库节点: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">store_nodes</span><span class="p">)</span><span class="si">}</span><span class="s2">，顾客节点: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">customer_nodes</span><span class="p">)</span><span class="si">}</span><span class="s2">）&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    
</span></span><span class="line"><span class="ln">53</span><span class="cl">    <span class="c1"># 保存到Excel文件</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="n">output_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">output_dir</span><span class="p">,</span> <span class="sa">f</span><span class="s2">&#34;cluster_</span><span class="si">{</span><span class="n">cluster_id</span><span class="si">}</span><span class="s2">.xlsx&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">    <span class="n">combined_nodes</span><span class="o">.</span><span class="n">to_excel</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
