跳转到内容

运输部署案例集/2021/密西西比州

来自维基教科书,为开放世界提供开放书籍

密西西比州有轨电车

[编辑 | 编辑源代码]
比洛克西有轨电车,20世纪初

有轨电车(也常称为电车、无轨电车或电力铁路)是一种公共交通方式,它在街道上建造的铁轨上运行。与公交车不同,有轨电车被限制在现有的铁轨上,无法到达没有预先存在的铁轨的地方。此外,与其他在铁路网络上运行的公共交通系统(特别是火车和地铁系统)不同,有轨电车通常在街道景观上与混合交通一起运行,并与行人和车辆共享道路,而不是在地下深处或专用铁轨上孤立运行。虽然历史上,有轨电车使用过各种电源,但现在最常见的是依赖电力。

技术特征

[编辑 | 编辑源代码]

有轨电车的設計目標是服務兩種主要目的之一:運輸貨物和運輸乘客,從一個地點到另一個地點。與其他交通方式一樣,服務使用會產生少量費用,由網絡所有者收取。在美国以及世界各地,有轨电车网络通常由私营公司拥有和运营。毫无疑问,这些网络的建设目的是为了最大限度地提高公司的回报。因此,铁轨通常建在预计使用量最大的地区,以优化回报。这种激励措施通常是可取的,因为它允许网络为尽可能多的人提供可及性,从而创造健康的自然供求关系。

优点和缺点

[编辑 | 编辑源代码]

有轨电车的主要优点和缺点[1]总结在下表中

有轨电车的优缺点
优点 缺点
服务可靠且频繁,避免了交通拥堵 容量远低于其他固定轨道交通服务
停车距离相对较短,可以为人口稠密地区(如中央商务区)提供良好的可达性。路线通常也比较短(不到 10 英里) 短停车距离导致速度明显降低,因为这限制了加速到最大可能速度的能力
虽然没有高速铁路那么快,但有轨电车的运行速度仍然与其他交通方式相似 不适合作为长途服务,因为车辆速度是关键的关注领域
有轨电车路线清晰可见,易于理解和使用 有轨电车与其他交通方式共享道路,如果交通标志不足,可能会更加危险
由于其简单性,有轨电车吸引了游客和当地用户 有轨电车的速度、车厢大小和频率有限,因此无法提供足够的容量作为主要交通方式
可以为高容量、高速交通方式之间提供高效可靠的连接
建设成本明显低于其他固定轨道交通服务,如轻轨和单轨
有轨电车系统可以显着促进其邻近地区的经济发展
与其他能源来源相比,使用电力减少了交通运输对环境的影响

在有轨电车发展和广泛使用之前,陆地乘客运输最常见的方式是使用马拉车辆;其中最流行的是公共马车。这些通常由一个封闭的乘客车厢组成,车厢两侧有木质长凳。车厢由四个木制辐条车轮支撑,由一匹或两匹马牵引。驾驶员通常坐在车厢外的单独的前向长凳上。从 18 世纪到 19 世纪,公共马车在美国和欧洲都很流行。

然而,马拉公共马车效率低下。由于车轮与未铺砌地面的滚动阻力,拉动马车的力很大。拉动马车的马会很快疲劳,这使得公共马车的运营时间每天只有几个小时。即使这样,为了使这种服务能够按照时间表正常运行,公司必须为他们运营的每项服务饲养和照顾数十匹马。

因此,公共马车服务相对昂贵。在大多数城市,中下阶层因此通常选择步行作为主要的交通方式 [2]。然而,缺乏铺路意味着步行通勤经常是泥泞的,如果不是尘土飞扬的话。道路上也通常布满了拉动公共马车的马粪。这使得通勤成为一段不愉快的旅程。因此,人们一直渴望发展公共交通,因为对更好交通方式的需求一直存在。

有轨电车的发明

[编辑 | 编辑源代码]

有轨电车的发展和增长是几个世纪以来积累的技术进步融合的结果。它的成功主要归功于工业革命中铁器加工技术的结合、电力发动机的采用以及人类将各种想法结合在一起的创造力。

虽然早在 16 世纪就已使用铁轨,但它们最初用于从矿山中运输大量的矿石。早期发明的铁轨是用木头建造的,车轮也是用木头建造的。手推车本身是马拉的,或者由一根缆绳拉动。然而,直到 18 世纪,才将马拉手推车在铁路系统上的概念引入城市内部的交通方式。这种系统通过减少手推车车轮与铁轨之间的滚动阻力,显著提高了行驶的平稳性,从而解决了马匹疲劳的问题。因此,一辆更大的手推车能够由一匹马而不是两匹马拉动,就像公共马车一样。因此,公司能够以更低的成本运营交通服务,同时提供更舒适的服务。

18 世纪中期,蒸汽机的发明导致了被称为工业革命的爆炸式增长。在此期间,鼓风炉的发明极大地提高了钢铁产量。因此,在 18 世纪后期,开始使用铸铁制造铁板和轮子。通常在木轨的上表面固定一层铸铁,形成复合木/铁轨轨道。在轨道中使用铸铁是比使用木材更上一层楼。与木制的前身相比,它们可以承受更高的负荷,而且滚动阻力更小,从而进一步提高了效率和舒适度。然而,铸铁也存在相应的问题。它很脆,容易磨损。高流量和使用重负荷对轨道的持续使用会导致轨道损坏。因此,铸铁板需要经常更换,这也是一笔巨大的经济开支。

19 世纪初,一种新的金属加工工艺的发明解决了铸铁的脆性问题,这种工艺创造了一种被称为熟铁的延展性材料。这是另一个改进,因为熟铁的延展性使轨道使用寿命更长,提高了经济效率。然而,熟铁的柔软性仍然意味着轨道在需要维护、修理或更换之前,使用寿命不到 10 年。这是有轨电车网络发展的一个主要制约因素。

19 世纪中后期,贝塞麦法和露天炉相继发明。这两种技术的结合使钢材能够以非常低的成本高效地生产。在这种情况下,由于钢轨具有理想的性能,因此迅速采用钢轨。钢材强度高,耐用且成本低,这意味着其使用寿命比熟铁制成的钢轨长得多,而且价格相似。这导致了坚固、耐用且光滑的钢轨,在 19 世纪后期引发了铁路行业的爆炸式增长。许多铁路公司开始运营,在接下来的几十年里,数千英里的铁路轨道被建造。

人们曾尝试过利用不同的动力来源为有轨电车提供动力。然而,随着时间的推移,人们发现蒸汽机体积太大,无法安装在小型车辆上,柴油发动机则过于笨重,效率低下。因此,动力来源的选择迅速转向电力,因为电力体积小且效率高,尤其适合轻便的车辆,如电车。到 19 世纪初,大多数有轨电车从马拉的马车转换为电气化的铁路。

使用电气化的有轨电车意味着初始投资在合理范围内。该系统可以通过建造轨道、承载电力的架空线路以及最终建造带有电动机的车辆来建立。这导致了美国电车行业的快速发展。

诞生阶段

[编辑 | 编辑源代码]

市场发展

[编辑 | 编辑源代码]

密西西比州有轨电车的诞生阶段始于 19 世纪后期,采用马拉的马车在木制和铁制复合轨道上运行。它们取代了马拉的公共汽车 [3]。最早拥有这种系统的城市是格林维尔(1887 年)和纳切兹(1886 年),由私人拥有的公司格林维尔有轨电车公司和纳切兹有轨电车公司分别开发和拥有该系统 [3]。这些网络由私人拥有和维护,费用由公司自行承担。到 1894 年,格林维尔有轨电车公司已拥有 10 万美元的授权股本,并维护了 6 英里的轨道,拥有 15 匹马和 9 辆马车 [4]。另一方面,纳切兹有轨电车公司也发展到类似的规模,同样拥有 10 万美元的授权股本,并维护了 5 英里的轨道以及 20 匹马和 7 辆马车 [4]。

19 世纪后期,其他几个城市也有公司建造和维护马车系统,但由于客流量低,这些路线被废弃,运营这些系统的公司也被解散。这很可能是因为密西西比州是一个农村州,人口密度低,这使得有轨电车成为一项难以经营且经济上不可行的业务。因此,缺乏对该服务的自然需求,使得公司难以在较小的城市中运营。因此,只有较大的城市,如梅里迪安、格林维尔、纳切兹、杰克逊和湾港,拥有有轨电车系统。在任何时候,整个密西西比州最多只有 13 条有轨电车线路。

在广泛采用钢轨和电动车设计之后,美国的有轨电车行业迅速发展。这是一个功能发现和增强的过程,许多人将新技术视为增长机会。密西西比州也是如此。然而,由于建造这种系统需要大量的资本投资,大多数地方政府没有为建造提供资金。相反,他们将街道特许经营权授予私人公司,然后这些公司会获得一定时间的特许经营权,通常是 20 年。因此,那些认为这是一个有利可图的机会的人筹集了资金,并与地方政府签订了特许经营权。因此,新一轮铁路公司被特许经营。

格林维尔的原始马拉有轨电车系统于 1899 年停止运营,并在接下来的两年时间里开始建造新的电气化钢轨。新公司 Delta Light and Traction Co. 获准特许经营,此后收购了格林维尔之前的铁路公司,并垄断了格林维尔的照明和有轨电车行业。这条线路取得了巨大成功,并在接下来的十年里经历了增长。

政策制定

[编辑 | 编辑源代码]

与大多数其他州一样,在有轨电车的诞生阶段,管理有轨电车运营的立法是从之前的交通服务(即公共汽车和马拉的有轨电车服务)借鉴而来。特别是,道路使用权政策与马拉的有轨电车服务一致。这主要是由于马拉的有轨电车和电力有轨电车之间存在相似之处。

然而,政府还出台了更多政策,以降低其在公共基础设施方面的维护成本。私人公司需要为特许经营协议支付的价格涉及条款的协商,并且经常要求私人公司为扩展和改善公共道路的质量做出贡献 [2]。虽然私人公司经常反对这些政策,但最终还是在签署了特许经营协议之后,在勉强同意的情况下,这些政策被强加给了运营商。

与格林维尔类似的市场增长模式在密西西比州其他主要的有轨电车城市(包括梅里迪安、纳切兹和杰克逊)被观察到,这些城市出现了建造、维护和运营有轨电车系统的公司。很快,每个城市都有自己相应的私人公司,这些公司采用了运行照明和电力有轨电车服务的模式。这些私人公司规模相对较小,由于对更多服务的需求有限,因此没有扩展到城市以外。这些公司的规模较小,导致了对服务的简单直接管理,总体上导致了有轨电车系统短暂而成功 [4]。

然而,缺乏竞争和整个城市的垄断自然导致票价上涨。因此,随着系统的成熟,政府在特许经营协议中引入了锁定政策,以防止运营商将票价提高到 5 美分以上。所有同意特许经营权的运营商都必须无论市场情况如何,都将票价维持在 5 美分。因此,有轨电车系统得到了公众的认可,新服务的开通通常伴随着热烈的庆祝 [3]。

增长阶段

[编辑 | 编辑源代码]

公共部门贡献

[编辑 | 编辑源代码]

虽然有轨电车系统的发展规模小于当时美国其他城市,因为密西西比州的需求较小,但政府仍在其发展中发挥了关键作用。特别是,政府主要通过特许经营协议控制着系统的规划。通过将有轨电车的路径限制在某些路线,政府对该服务的城市规划部分拥有控制权。此外,通过引入上述政策(如该服务的最高票价),政府能够确保这些服务能被公众轻松获得。因此,这种规划指引着一条通往更高效的经济增长的道路。

私人部门贡献

[编辑 | 编辑源代码]

私营部门是電車服务的 主要贡献者。 私营公司建造、拥有、维护和运营所有系统,期望从服务中获利。 虽然他们必须根据特许经营协议中商定的政策运营,但私营公司一直在寻求提高效率和利润最大化。 这促使私营公司将電車服务扩展到私营公司认为有利可图的地区,同时也为私营公司提供了激励措施来提高其服务质量以增加客流量。 铁路公司因此会购买照明公司并在其沿线提供照明服务以吸引乘客。 结果,私营部门为公共交通网络基础设施、服务运营以及城市基础设施质量的提高做出了巨大贡献。

政策问题

[编辑 | 编辑源代码]

公共部门和私营部门之间最大的利益冲突在于与票价相关的政策。 管理机构希望以低成本获得便利的交通,而私营公司则希望将利润最大化。 虽然最初锁定政策(限制電車运营商可以收取的票价)并没有什么问题,但在 20 世纪通货膨胀开始出现后,它就成了一个问题。 结果,许多铁路公司不再具有经济可行性,很快就被其他形式的交通工具(如汽车)所取代。

成熟阶段

[编辑 | 编辑源代码]

由于密西西比州的人口密度低,電車网络的成熟阶段相对较早,大约在 1918 年左右。 大多数電車系统都没有经历过显着的增长,客流量多年来也没有改善。 因此,私营公司没有动力继续扩展其网络,因为这样做不会有利可图。

这进一步加剧了 5 美分最高票价的锁定政策。 随着通货膨胀的加剧,客流量的缺乏意味着私营部门的利润逐渐下降,最终,它们不再具有经济可行性。 福特汽车公司的流线型汽车制造的出现是将電車行业推向衰退阶段的最后推动。 到 1930 年,汽车行业发展迅速,電車系统迅速被更有效、更灵活且不需要庞大铁路基础设施的 新巴士所取代 [5]。 由于让路政策自其马车前辈以来一直没有改变,電車需要与其他车辆共用道路。 因此,包括汽车和巴士在内的其他形式的交通工具的增加,使得電車争夺让路权,并由于其缺乏灵活性而被淘汰。 结果,在 1930 年代到 1950 年代之间,超过一百个城市废弃了他们的電車系统,取而代之的是巴士和私家车 [5]。 密西西比州的所有城市都属于已经废弃電車系统的城市类别。 到 1930 年代中期,密西西比州再也看不到電車了 [3]。

重塑交通方式

[编辑 | 编辑源代码]

虽然電車早已被废弃,并被巴士等更灵活的服务所取代,但電車仍然具有一些优势。 因此,存在为现代使用重塑電車的机会,特别是在高度拥挤的区域内使用短程電車。 但是,必须相应地改变政策以适应这种情况的发生。 中央商务区的拥挤道路可以很容易地以低成本转换为電車系统。 但是,为了防止进一步的让路权冲突,有必要改造道路,以便汽车、巴士和電車之间没有冲突。

電車系统也不太可能对私营部门来说有利可图。 因此,公共部门的资金对于重新改造此类系统也是必要的。 虽然资本成本相对较高,但電車带来的经济效益和增长可能超过线路的成本。

定量分析

[编辑 | 编辑源代码]

对于密西西比州每个拥有電車系统的城市,都进行了定量分析,将密西西比州電車系统的轨道长度拟合到逻辑函数,以识别三个发展阶段:诞生阶段、增长阶段和成熟阶段。 数据收集自“McGraw 电气铁路手册 - 美国街铁路投资红皮书”,时间跨度为 1894 年至 1920 年,尽可能使用自动化的 Python 脚本。 然后手动检查数据,然后用于曲线拟合。 用于拟合数据的函数是

其中 代表 年的轨道长度(英里), 代表系统成熟阶段的饱和轨道长度,而 是使用线性形式的最小二乘回归拟合的参数

由于一些网络尚未进入成熟阶段,参数 是未知的。在这种情况下,我们迭代遍历一系列可能的数值,并最大化最小二乘回归中的相关性 。此优化问题使用 Python 中的暴力迭代方法解决。对于密西西比州研究期间的每个系统,都拟合了一个逻辑函数,尽管一些数据表明逻辑函数可能不是最佳拟合。

密西西比州 - 汇总

[编辑 | 编辑源代码]

将密西西比州所有系统的数据汇总在一起,以找到密西西比州 1894 年至 1920 年间每年的总轨道长度。当没有关于某个系统轨道长度变化的报告时,假设轨道没有发生变化,因此保持其先前报告的长度。以下是每年的汇总结果。

密西西比州有轨电车轨道长度的逻辑回归。
年份
1894 26.50 16.18
1895 26.50 18.68
1896 26.50 21.51
1897 23.50 24.67
1898 21.50 28.19
1899 28.00 32.07
1900 29.00 36.30
1901 32.00 40.87
1902 28.50 45.76
1903 36.00 50.92
1904 56.70 56.31
1905 49.75 61.86
1906 58.30 67.50
1907 82.30 73.16
1908 95.30 78.76
1909 95.70 84.22
1910 98.90 89.49
1911 106.40 94.49
1912 107.90 99.20
1913 106.40 103.58
1914 104.20 107.60
1915 104.20 111.27
1916 104.20 114.58
1917 123.51 117.54
1918 123.51 120.18
1919 123.51 122.51
1920 117.73 124.56

下表汇总了逻辑回归的系数。

137.4 0.16 1906.2 0.927

从逻辑回归中,其中 被优化,似乎逻辑函数似乎是数据的一个合理拟合。 值在 0.927 处相当高,这表明该曲线的拟合优度相当高。然而,话虽如此,记录数据和拟合线之间似乎仍然存在一些差异。在优化拟合优度后,发现密西西比州轨道饱和长度约为 137.4 英里。因此,在研究期之外的年份里,仍然有成长的空间。一些出生阶段的近似年份是 1882 年至 1898 年,增长阶段是 1898 年至 1917 年,成熟阶段是 1917 年至 1930 年,之后不久便开始下降。

为了完整起见,也对密西西比州的每个城市进行了逻辑回归分析。然而,一些城市,如纳切兹,表明逻辑函数可能不是最佳的拟合模型。然而,其他城市,如格林维尔、杰克逊和梅里迪安,表明逻辑函数是一个合理的拟合,因为拟合优度相对较高。

哥伦布

[编辑 | 编辑源代码]
哥伦布,密西西比州,有轨电车轨道长度的逻辑回归。

哥伦布似乎很早就经历了其出生阶段,在 1906 年之前。然而,直到 1906 年,增长才迅速扩大,并在 1910 年迅速进入其成熟和饱和阶段。

年份
1907 4.50 3.84
1908 5.60 5.40
1909 6.70 6.25
1910 6.70 6.61
1911 6.70 6.73
1914 5.00 6.80
1917 6.80 6.80
1918 6.80 6.80

格林维尔

[编辑 | 编辑源代码]
格林维尔,密西西比州,有轨电车轨道长度的逻辑回归。

格林维尔最初有一个马拉的有轨电车系统。然而,在引入电力有轨电车后,原来的系统被废除,铁路线被电气化。因此,电力有轨电车系统的出生阶段始于大约 1901 年,然后从 1904 年到 1911 年迅速扩张,之后网络饱和在约 7.75 英里长。

年份
1899 0.00 0.00
1900 0.00 0.00
1901 0.00 0.00
1902 2.00 0.00
1903 2.00 0.01
1904 2.00 0.04
1905 2.00 0.15
1906 4.30 0.53
1907 4.80 1.61
1908 5.00 3.78
1909 5.00 6.01
1910 5.00 7.17
1911 7.00 7.58
1914 7.00 7.75
1917 7.75 7.75
1918 7.75 7.75
1920 7.75 7.75

高尔夫港

[编辑 | 编辑源代码]
高尔夫港,密西西比州,有轨电车轨道长度的逻辑回归。

高尔夫港似乎也经历了从 1905 年的出生到 1906 年至 1910 年的增长阶段的快速扩张。高尔夫港的网络比密西西比州的大多数其他城市都大,并在 1911 年左右饱和在 30 英里。

年份
1905 1.75 9.75
1906 8.00 18.99
1907 23.00 25.82
1908 25.00 28.70
1909 25.00 29.63
1910 25.00 29.89
1911 30.00 29.97
1912 30.00 29.99
1913 30.00 30.00
1914 30.00 30.00
1917 30.00 30.00
1918 30.00 30.00
1920 30.00 30.00

杰克逊

[编辑 | 编辑源代码]
杰克逊,密西西比州,有轨电车轨道长度的逻辑回归。

杰克逊似乎也相当符合逻辑函数。出生阶段始于 1893 年左右,然后迅速进入其增长阶段,从 1900 年左右持续到 1910 年,之后网络趋于成熟。虽然数据仅限于 1920 年之前,但逻辑函数表明网络继续成熟,直到达到潜在的 15.6 英里的饱和长度。

年份
1894 1.50 1.87
1897 1.50 3.00
1898 1.50 3.47
1899 6.00 4.00
1900 6.00 4.58
1901 6.00 5.20
1902 6.00 5.86
1903 8.00 6.55
1904 5.20 7.27
1905 5.50 7.99
1906 5.50 8.71
1907 14.00 9.41
1908 12.00 10.09
1909 12.00 10.73
1910 12.00 11.32
1911 12.50 11.87
1912 14.00 12.37
1913 12.50 12.82
1914 14.00 13.21
1917 13.50 14.13
1918 13.50 14.35
1920 13.50 14.71

梅里迪安

[编辑 | 编辑源代码]
梅里迪安,密西西比州,有轨电车轨道长度的逻辑回归。

梅里迪安对逻辑函数表现出很高的拟合优度。网络在 1890 年左右诞生,然后从 1894 年到 1910 年进入其增长阶段,在此期间,轨道的长度扩展到大约 11 英里。然后网络进入其成熟阶段,最终饱和在约 14 英里长。

年份
1898 6.00 4.65
1899 6.00 5.16
1900 6.00 5.70
1901 6.00 6.25
1902 6.00 6.81
1903 6.00 7.38
1904 7.50 7.94
1905 7.50 8.48
1906 7.50 9.01
1907 9.00 9.52
1908 11.00 10.00
1909 11.50 10.44
1910 11.50 10.85
1911 11.50 11.23
1912 11.50 11.57
1913 11.50 11.88
1914 11.50 12.16
1917 13.12 12.81
1918 13.12 12.97
1920 13.12 13.24

纳切兹

[编辑 | 编辑源代码]
密西西比州纳切兹的有轨电车轨道长度的逻辑回归。

纳切兹是一个有趣的案例,它不太适合逻辑函数。拟合度很差,拟合参数不能很好地反映网络发展的三个阶段。然而,可以肯定地说,该网络在早期就成熟了,并保持在 7 英里的饱和长度。

年份
1897 6.00 6.81
1898 6.00 6.90
1899 6.00 6.94
1900 6.00 6.97
1901 4.00 6.98
1902 3.50 6.99
1903 7.00 6.99
1904 7.00 7.00
1905 7.00 7.00
1906 7.00 7.00
1907 7.00 7.00
1908 7.00 7.00
1909 7.00 7.00
1910 7.00 7.00
1911 7.00 7.00
1912 7.00 7.00
1913 7.00 7.00
1914 6.00 7.00
1917 7.00 7.00
1918 7.00 7.00

代码 - 刮取数据

[编辑 | 编辑源代码]

当数据文本文件可用时,运行以下代码以简化数据提取过程。这并不完美,因此,在提取数据后,需要一些手动工作来清理和检查数据。

import pandas as pd
import re
import us
import matplotlib.pyplot as plt

def read_file(year):
    out = None
    with open(
        f'McGraw electric railway manual - the red book of American street railway investment - {year}.txt',
        encoding="utf8"
    ) as f:
        out = pd.DataFrame(f)
        out['Year'] = year
        out[0] = out[0].apply(lambda x: x.strip())
    return out
    
final_data = None

regex_exp = re.compile(
    '.*(Miles[,]?[\ ]?of track|Miles[\ ]?of tracfc|Miles[\ ]?of tyack)' + # 1
    '([\ ]?\(?\S*\)?)?' + # 2
    '[,|\.]?' + 
    '(\ \(.*\)' + # 3
    '[,|\.]?)?' + 
    '[\ |\.]' + 
    '([0-9]*)' + # 4
    '\.?' + 
    '([0-9]*).*' # 5
)

for year in [1894, 1898, 1899, 1900, 1901, 1902]:
    df = read_file(year)

    companies = list(df.iloc[df[
        (df[0].str.lower().str.contains('co[\.]?[\ ]?—|co.$'))
    ].index][0])



    def get_company(x):
        if x in companies:
            return x
        else:
            return pd.np.nan

    df['Company'] = df[0].apply(get_company)
    df['Company'] = df['Company'].ffill()

    headings = df[
        (df[0].str.isupper() | df[0].str.islower()) &
        (df[0].str.contains('.*[,|\.].*\.$'))
    ].copy()

    next_heading = list(headings.index)
    next_heading.pop(0)
    next_heading.append(0)
    headings['Next'] = next_heading

    interest = headings[
        (headings[0].str.contains('MISS\.|HISS\.'))
    ]

    cities = []
    miles = []
    years = []
    companies = []

    for i, row in interest.iterrows(): # Iterate through all areas in the state
        strip_non_alpha = re.compile('[^a-zA-Z,\ ]')
        city = strip_non_alpha.sub('', row[0].strip().replace('\t', ' ')).strip().replace(',', '')
        query = df.iloc[i: row['Next'] + 1]
        sub_query = query[
            query[0].str.contains('Miles[,]? of t')
        ]

        if len(sub_query) == 0:
            cities.append(city)
            miles.append(-1)
            years.append(year)
            companies.append('Unknown')
            
            print(year, city)
            display(query)

        for i, data in sub_query.iterrows(): # Iterate through all the companies in the area
            try:
                mile = float('.'.join(regex_exp.match(data[0].replace('S', '5')).group(4, 5)))
            except:
                print(data[0])
            
            company = data['Company'].split('—')[0].strip()
            if len(company) > 50:
                company = company.split('.')[-2] + '.'
                
            cities.append(city.split(' ')[0])
            miles.append(mile)
            years.append(year)
            companies.append(company)

    result = pd.DataFrame({
        'year' : years,
        'city': cities,
        'miles' : miles,
        'company' : companies
    })
    
    if final_data is None:
        final_data = result
    else:
        try:
            final_data = final_data.append(result).reset_index(inplace=False, drop=True)
        except:
            display(result)
            
states = [str(i).upper() + '.' for i in us.states.STATES]

for year in range(1903, 1915):
    df = read_file(year)
    companies_list = list(df.iloc[df[
        (df[0].str.lower().str.contains('capital stock')) &
        (df[0].str.lower().str.contains('authorized'))
    ].index - 1][0])

    def get_company(x):
        if x in companies_list:
            return x
        else:
            return pd.np.nan

    df['Company'] = df[0].apply(get_company)
    df['Company'] = df['Company'].ffill()

    def get_state(x):
        if x in states:
            return x
        else:
            return pd.np.nan

    df['State'] = df[0].apply(get_state).ffill().fillna('')

    headings = df[
        (df[0].str.isupper()) &
        (df[0].str.contains('^[A-Z\ ]*\.$')) &
        (~df[0].isin(states)) &
        (
            (~df[0].str.contains('STATISTICS')) &
            (~df[0].str.contains('LIABILITIES')) &
            (~df[0].str.contains('ASSETS'))
        )
    ].copy()

    next_heading = list(headings.index)
    next_heading.pop(0)
    next_heading.append(0)
    headings['Next'] = next_heading

    interest = headings[
        (headings['State'].str.contains('MISSISSIPPI'))
    ]

    cities = []
    miles = []
    years = []
    companies = []

    for i, row in interest.iterrows(): # Iterate through all areas in the state
        strip_non_alpha = re.compile('[^a-zA-Z,\ ]')
        city = strip_non_alpha.sub('', row[0].strip().replace('\t', ' ')).strip().replace(',', '')
        query = df.iloc[i: row['Next'] + 1]
        
        sub_query = query[
            query[0].str.contains('Miles[\ ]?of')
        ]

        if len(sub_query) == 0:
            cities.append(city)
            miles.append(0)
            years.append(year)
            companies.append('Unknown')

            print(year, city)
            display(query)

        for i, data in sub_query.iterrows(): # Iterate through all the companies in the area
            try:
                mile = float(('.'.join(regex_exp.match(data[0].replace('S', '5').replace(' and ', '/')).group(4, 5))))
            except:
                print(city)
                print(year)
                print(data[0])
                mile = -1
                
            company = data['Company'].split('—')[0].strip()
            if len(company) > 50:
                company = company.split('.')[-2] + '.'

            cities.append(city)
            miles.append(mile)
            years.append(year)
            companies.append(company)

    result = pd.DataFrame({
        'year' : years,
        'city': cities,
        'miles' : miles,
        'company': companies
    })

    if final_data is None:
        final_data = result
    else:
        try:
            final_data = final_data.append(result).reset_index(inplace=False, drop=True)
        except:
            display(result)

代码 - 优化线性回归和绘图

[编辑 | 编辑源代码]
import numpy as np
import pandas as pd
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter, AutoMinorLocator)

df = pd.read_csv('data.csv')
mindex = []
for city in df['City'].unique():
    for year in range(df['Year'].min(), df['Year'].max() + 1):
        mindex.append((year, city))
df = df.groupby(['Year', 'City'], as_index=True).agg({
    'Miles' : 'sum'
})
df = df.reindex(mindex).unstack()

def autofit(S, plot=True, major_y=30, major_x=4, minor_y=15, minor_x=2, city=''):
    best_R2 = -1
    fit = None
    best_S_max = None
    
    for r in np.linspace(1 + 1e-6, 5, 1000):
        S_max = S.max() * r
        Y = np.log((S + 1e-6)/(S_max - S))
        R2 = np.corrcoef(Y.index, Y)[0][1]**2
        if R2 > best_R2:
            best_R2 = R2
            best_S_max = S_max
            b, bt0 = np.polyfit(Y.index, Y, deg=1)
            t0 = - bt0 / b
    
    S_t_fit = lambda t: best_S_max / (1 + np.exp(-b * (t - t0)))
    
    if plot:
        X = np.linspace(min(Y.index), max(Y.index), 100)
        S_fitted = S_t_fit(X)
        fig, ax = plt.subplots(figsize=(15, 8))
        ax.scatter(S.index, S, label='Recorded data')
        ax.plot(
            X, S_fitted, 
            label=f'$S(t) = \\dfrac{{{best_S_max:.1f}}}{{1 + \exp(-{b:.2f}(t - {t0:.1f}))}}$', 
            linestyle='--', color='r'
        )
        
        ax.set_ylabel(f'Total Track Length in {city.capitalize()} (Miles)')
        ax.set_xlabel('Year')
        ax.legend(loc='best', bbox_to_anchor=(0.5, 0., 0.4, 0.5))
        ax.set_title(f'A logistic function regression of the total track length across {city.capitalize()}')
        ax.xaxis.set_major_locator(MultipleLocator(major_x))
        ax.xaxis.set_minor_locator(MultipleLocator(minor_x))
        ax.yaxis.set_major_locator(MultipleLocator(major_y))
        ax.yaxis.set_minor_locator(MultipleLocator(minor_y))
        ax.grid(b=True, which='major', color='lightgray', linestyle='-')
        ax.grid(b=True, which='minor', color='lightgray', linestyle='--')
        
        ax.annotate(f'$R^2 = {best_R2:.3f}$', (min(X) + minor_x, min(S_fitted) + minor_y))
    
    out = pd.DataFrame({
        'S': S,
        'S_fit': S_t_fit(np.array(S.index)) 
    })
    return out

参考文献

[编辑 | 编辑源代码]

[1] Parsons Brinckerhoff. 西雅图有轨电车网络和可行性分析。西雅图交通部。2004 年

[2] 蒙罗·多德。精彩之旅 - 堪萨斯城的有轨电车 1870 - 1957 年。堪萨斯城之星图书。2002 年。

[3] 弗兰克·A·布鲁克斯二世。关于有轨电车的轶事 - 南方牵引 - 密西西比州的有轨电车旅行。1983 年。

[4] 麦格劳电气铁路手册 - 美国有轨电车投资红皮书。1894 年。

[5] 彼得·斯皮里特。为什么墨尔本保留了它的电车。城市和规划史的景观和生态。

华夏公益教科书