跳轉到內容

運輸部署案例集/2021/密西西比州

來自華夏公益教科書,為開放世界提供開放書籍

密西西比州有軌電車

[編輯 | 編輯原始碼]
比洛克西有軌電車,20世紀初

有軌電車(也常被稱為電車、拖車或電氣鐵路)是一種公共交通工具,在街道上建造的軌道上執行。與公共汽車不同,有軌電車被限制在現有的軌道上,無法到達沒有預先存在的軌道的區域。此外,與其他在鐵路網路上執行的公共交通系統(尤其是火車和地鐵系統)不同,有軌電車通常在街道景觀上與混合交通一起執行,並與行人和車輛(偶爾)共享道路,而不是在深地下或專用軌道上單獨執行。歷史上,有軌電車已在各種電源上執行,但現在最常依靠電力。

技術特點

[編輯 | 編輯原始碼]

有軌電車的設計目的是為了服務於兩種主要目的之一:運輸貨物和運輸乘客,從一個地點到另一個地點。與其他運輸方式一樣,使用該服務需要使用者支付少量費用,並支付給網路所有者。在美國以及世界各地,有軌電車網路通常由私人公司擁有和運營。毫無疑問,這些網路的建設目的是為了將公司的收益最大化。因此,軌道通常建在預計使用量最大的區域,以最佳化回報。這種激勵措施通常是可取的,因為它允許網路為儘可能多的人提供可達性,從而創造健康的自然供求關係。

優點和缺點

[編輯 | 編輯原始碼]

有軌電車的主要優點和缺點 [1] 總結在下表中

有軌電車的優點和缺點
優點 缺點
服務可靠且頻繁,避免了擁擠和交通繁忙區域的交通擁堵 容量遠低於其他固定軌道交通服務
停車距離相對較短,可以透過密集區域(如中央商務區)提供良好的可達性。路線通常也很短(不到 10 英里) 短停車距離導致速度明顯降低,因為它限制了加速到最大可能速度的能力
雖然不如高速鐵路快,但有軌電車的執行速度仍然與其他交通方式類似 不適合作為長途服務,因為車輛速度是關鍵的關注領域
有軌電車路線清晰可見、易於理解且可達 有軌電車與其他交通方式共享路權,如果交通標誌不足,可能會更危險
有軌電車因其簡單性而吸引了遊客和當地使用者 有軌電車受速度、車廂大小和頻率限制,因此無法提供足夠的容量作為主要交通方式
可以為容量更大、速度更高的交通方式提供高效可靠的連線
建築成本明顯低於其他固定軌道交通服務,如輕軌和單軌
有軌電車系統可以為其周邊社群的經濟發展做出重大貢獻
使用電力而不是其他能源來源,減少了交通對環境的影響

在有軌電車發展和廣泛使用之前,陸地乘客運輸最常見的方式是透過馬拉車廂進行的;其中最流行的是公共汽車。這些通常包括一個封閉的乘客艙,兩側有木製長凳。駕駛室由四個木製輻條輪支撐,由一匹或兩匹馬拉著。司機通常坐在駕駛室外的獨立的前向長凳上。公共汽車在美國和歐洲在 18 世紀到 19 世紀都很流行。

但是,馬拉公共汽車效率低下。由於車輪與未鋪砌地面之間的滾動阻力,拉動馬車所需的力很大。拉動馬車的馬匹會很快疲勞,限制了公共汽車每天的執行時間。即使這樣,為了使這樣的服務能夠按照時間表正常運營,公司必須為其運營的每項服務提供和照顧數十匹馬。

因此,公共汽車服務相對昂貴。在大多數城市,中下層階級因此經常選擇步行作為主要交通方式 [2]。然而,缺乏鋪砌意味著步行通勤通常泥濘不堪,如果不是塵土飛揚的話。道路上也經常佈滿馬匹拉動公共汽車留下的糞便。這使得通勤成為一次不愉快的旅程。因此,對公共交通增長的興趣一直存在,因為對更好交通工具的需求一直存在。

有軌電車的發明

[編輯 | 編輯原始碼]

有軌電車的發展和增長是幾個世紀以來積累的各種技術進步的融合的結果。它的成功主要歸功於工業革命的鋼鐵加工技術、電力發動機的採用以及人類將各種想法結合起來的獨創性。

儘管鐵路的歷史可以追溯到十六世紀,但最初它們被用來從礦山運送大量的礦石。早期發明的鐵路軌道是用木材建造的,就像手推車的輪子一樣。手推車本身是用馬拉或用纜繩拉動。然而,直到十八世紀,馬拉馬車在鐵路系統上的概念才作為城市內部的一種交通方式被引入。這種系統透過減少手推車車輪與軌道之間的滾動阻力,顯著提高了行駛的平穩性,解決了馬匹疲勞的問題。因此,一輛更大的馬車可以用一匹馬而不是兩匹馬來拉,就像公共汽車一樣。因此,公司能夠以更低的成本運營運輸服務,同時提供更舒適的服務。

在十八世紀中葉,蒸汽機的發明導致了被稱為工業革命的爆炸性增長。在此期間,鼓風爐的發明顯著提高了鋼鐵產量。因此,在十八世紀後期,鐵板和車輪開始使用鑄鐵製成。通常將一層鑄鐵固定在木製軌道的上表面,形成複合木/鐵軌道。在軌道中使用鑄鐵是比使用木材的一大進步。它們可以承受明顯更高的載荷,並且與木製的前身相比,滾動阻力更小,進一步提高了效率和舒適性。然而,鑄鐵也有其對應的一系列問題。它很脆,容易磨損。高流量和對軌道持續使用過載荷會導致軌道損壞。因此,鑄鐵板需要經常更換,這也使其成為一項重大的經濟開支。

在十九世紀初期,一種新的金屬加工工藝的發明解決了鑄鐵的脆性問題,這種工藝創造了一種稱為鍛鐵的延展性材料。這是一項改進,因為鍛鐵的延展性使軌道可以持續更長時間,使其更具經濟效益。然而,鍛鐵的柔軟性仍然意味著軌道在需要維護、修理或更換之前只能持續不到 10 年。這是限制有軌電車網路增長的一個關鍵因素。

在十九世紀中後期,貝塞麥法和露天爐被髮明出來。這兩種方法的結合使鋼材能夠以非常低的成本高效生產。在這種情況下,由鋼材製成的鐵路軌道被迅速採用,因為其具有理想的特性。鋼材的高強度以及其耐用性和低成本意味著它的使用壽命遠遠超過由鍛鐵製成的軌道,而且價格也相當。這帶來了堅固、持久且平滑的軌道,在十九世紀後期引發了鐵路行業的爆炸性增長。許多鐵路公司開始運營,並在接下來的幾十年裡修建了數千英里的鐵路軌道。

人們曾嘗試過使用不同的動力來源來驅動有軌電車。然而,隨著時間的推移,人們發現蒸汽機體積太大,無法裝入小型車輛,而柴油發動機又太重,因此效率低下。因此,首選的動力來源迅速轉向電力,因為它體積小,效率高,特別適用於有軌電車等輕型馬車。到十九世紀初,大多數有軌電車系統已從馬匹牽引的馬車轉換為電力驅動鐵路。

使用電力有軌電車意味著最初的投資是合理的。該系統可以透過修建鐵路軌道、輸送電力的架空線路以及最後修建車廂(車廂上裝有電動機)來建立。這導致美國有軌電車行業迅速擴張。

孕育階段

[edit | edit source]

市場發展

[edit | edit source]

密西西比州有軌電車的孕育階段始於十九世紀後期採用馬拉馬車沿著木製和鐵製複合軌道執行。這些馬車取代了馬拉公共汽車 [3]。最早使用這種系統的城市是格林維爾 (1887) 和納奇茲 (1886),私營公司格林維爾有軌電車公司和納奇茲有軌電車公司分別是該系統的開發商和所有者 [3]。這些網路由私人擁有和維護,由公司自行承擔費用。到 1894 年,格林維爾有軌電車公司擁有 100,000 美元的授權股本,並維護著 6 英里的軌道,配備 15 匹馬和 9 輛馬車 [4]。另一方面,納奇茲有軌電車公司也發展到類似的規模,也擁有 100,000 美元的授權股本,並維護著 5 英里的軌道,以及 20 匹馬和 7 輛馬車 [4]。

十九世紀後期,其他幾個城市也有公司建造和維護馬車系統,但由於客流量低,這些路線被廢棄,運營這些系統的公司也解散了。這可能是因為密西西比州是一個農村州,人口密度低,這使得有軌電車成為一項難以盈利且不可行的業務。因此,缺乏對服務的自然需求使得公司難以在較小的城市運營。因此,只有較大的城市,如梅里迪安、格林維爾、納奇茲、傑克遜和格爾夫波特才有有軌電車系統。在任何時候,整個密西西比州最多隻有 13 條有軌電車線路。

在鋼鐵軌道和電動車設計廣泛採用後,全美有軌電車行業迅速發展。這是一個功能發現和改進的過程,許多人將這項新技術視為發展的機會。密西西比州也是如此。然而,由於修建這些系統需要大量的資本投資,大多數地方政府並沒有為其建設提供資金。相反,他們將街道特許經營權授予私營公司,這些公司隨後將被特許經營一段時間,通常為 20 年。因此,那些將此視為盈利機會的人募集資金,並與地方政府組建了特許經營權。結果,新一輪的鐵路公司被特許成立。

格林維爾最初的馬拉有軌電車系統於 1899 年停運,並在兩年內開始修建新的電力鋼軌。新公司三角洲電力和牽引公司獲得特許經營權,隨後收購了格林維爾之前的鐵路公司,壟斷了格林維爾的照明和有軌電車行業。這條線路取得了巨大成功,並在接下來的十年裡不斷發展。

政策制定

[edit | edit source]

與大多數其他州一樣,管理有軌電車運營的法律是在孕育階段從之前的交通服務中借鑑來的,即公共汽車和馬拉有軌電車服務。特別是,道路通行權政策與馬拉有軌電車服務的政策一致。這主要是由於馬拉有軌電車和電力驅動有軌電車之間的相似性。

然而,政府進一步推出了更多政策來降低其公共基礎設施維護成本。私營公司需要為特許經營協議支付的價格涉及條款談判,並且通常要求私營公司為公共道路的擴建和質量提升做出貢獻 [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

程式碼 - 抓取資料

[編輯 | 編輯原始碼]

當資料可用為 txt 檔案時,執行以下程式碼可以簡化資料提取過程。這並不完美,因此在提取資料後,需要進行一些手動操作來清理和檢查資料。

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] Monroe Dodd. 一次輝煌的旅程 - 堪薩斯城有軌電車 1870-1957 年。堪薩斯城星報書籍。2002 年。

[3] Frank A. Brooks Jr. 關於有軌電車的軼事 - 南方牽引 - 在密西西比州乘坐有軌電車。1983 年。

[4] 麥格勞電力鐵路手冊 - 美國有軌電車投資紅皮書。1894 年。

[5] Peter Spearritt. 為什麼墨爾本保留了它的有軌電車。城市和規劃歷史的景觀和生態。

華夏公益教科書