[GAS] 利用 YouBike 開放數據進行共享單車使用率分析

更新於 發佈於 閱讀時間約 16 分鐘
自製 - Ubike2.0數據分析

自製 - Ubike2.0數據分析


最近,我偶然發現了一個有趣的分享,有人利用政府提供的 YouBike 開放數據來解決自己在使用 YouBike 時遇到找不到車可以借的麻煩。這讓我靈光一閃:為什麼不利用這些數據做一點有趣的數據分析,來看看不同時間點的 YouBike 使用率呢?

接下來,我決定展開一個小小的實驗,使用 Google Apps Script 來分析這些公開的資料,從而深入了解城市中的共享單車系統。或許,我們可以發現一些出乎意料的使用趨勢,甚至預測何時能輕鬆找到一輛空閒的 YouBike。讓我們一起挖掘這些數據背後的秘密吧!


使用的技術

1. Google Sheet

2. Google Apps Script

3. Javascript

  1. YouBike2.0臺北市公共自行車即時資訊


Google Apps Script實作流程

1. 每10分鐘從 YouBike json檔擷取數據,將原始數據存儲在 "Raw Data" 表

// 抓取 YouBike 數據並存儲
function fetchAndStoreYouBikeData() {
    try {
    var response = UrlFetchApp.fetch(YOUBIKE_API_URL);
    var data = JSON.parse(response.getContentText());
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Raw Data');

    if (!sheet) {
      sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet('Raw Data');
      sheet.appendRow(['Timestamp', 'Station ID', 'Station Name', 'Total Bikes', 'Available Bikes', 'Available Spaces']);
    }

    var timestamp = new Date();
    var rowsAdded = 0;
    data.forEach(function(station) {
      var stationId = String(station.sno); // 將站點 ID 轉換為字串
      if (STATION_IDS.includes(stationId)) {
        sheet.appendRow([
          timestamp,
          stationId,
          station.sna,
          station.total,
          station.available_rent_bikes,
          station.available_return_bikes
        ]);

        rowsAdded++;
      } else {
        Logger.log('站點 ID 不匹配: ' + stationId);
      }
    });

    Logger.log('成功添加了 ' + rowsAdded + ' 行數據');

    // 如果沒有添加任何數據,記錄所有接收到的站點 ID

    if (rowsAdded === 0) {
      Logger.log('警告:沒有匹配的站點。接收到的站點 ID:' + data.map(station => station.sno).join(', '));
    }
  } catch (error) {
    Logger.log('錯誤:獲取或存儲 YouBike 數據時出錯 - ' + error.toString());
  }
}


2. 分析數據並在 "Analysis" 表中提供簡單的需求預測和供給建議

使用簡單的「線性回歸」來預測未來供需量
function analyzeDemandAndSupply() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Raw Data');
  var data = sheet.getDataRange().getValues(); 
  var analysisSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Analysis');

  if (!analysisSheet) {
    analysisSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet('Analysis');
    analysisSheet.appendRow(['Station ID', 'Station Name', 'Avg Available Bikes', 'Demand Prediction', 'Supply Recommendation']);
  }

  // 清空分析表格(保留表頭)
  analysisSheet.getRange(2, 1, analysisSheet.getLastRow(), 5).clear();
  var stationData = {};

  // 處理原始數據
  for (var i = 1; i < data.length; i++) {
    var stationId = String(data[i][1]); // 將站點 ID 轉換為字串
    if (STATION_IDS.includes(stationId)) {
      if (!stationData[stationId]) {
        stationData[stationId] = {
          name: data[i][2],
          availableBikes: [],
          totalBikes: data[i][3]
        };
      }
      stationData[stationId].availableBikes.push(data[i][4]);
    }
  }

  // 檢查是否有數據
  if (Object.keys(stationData).length === 0) {
    Logger.log('警告:沒有找到匹配的站點數據');
    return;
  }
 
  // 分析每個站點
  STATION_IDS.forEach(function(stationId) {
    var station = stationData[stationId];
    if (station && station.availableBikes.length > 0) {
      var avgAvailableBikes = average(station.availableBikes);
      var demandPrediction = predictDemand(station.availableBikes);
      var supplyRecommendation = recommendSupply(avgAvailableBikes, station.totalBikes)
      analysisSheet.appendRow([
        stationId,
        station.name,
        avgAvailableBikes,
        demandPrediction,
        supplyRecommendation
      ]);
    } else {
      Logger.log('警告:站點 ' + stationId + ' 沒有數據或可用自行車數據為空');
    }
  });
}


3. 針對每個出口站點建立分析圖表

// 需要一個函數來獲取站點名稱
function getStationNames() {
  var rawDataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Raw Data');
  var rawData = rawDataSheet.getDataRange().getValues();
  var stationNames = {};
 
  rawData.forEach(function(row) {
    if (row[1] && row[2]) {  // 確保站點 ID 和名稱都存在
      stationNames[String(row[1])] = row[2];
    }
  });
  return stationNames;
}



// 準備圖表資料
function prepareChartData() {
  var rawDataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Raw Data');
  var rawData = rawDataSheet.getDataRange().getValues();
  var stationNames = getStationNames();

  STATION_IDS.forEach(function(stationId) {
    var stationName = stationNames[stationId] || stationId;  // 如果找不到名稱,就使用 ID
    var chartDataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Chart Data ' + stationName);
    if (!chartDataSheet) {
      chartDataSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet('Chart Data ' + stationName);
    }

    // 清空圖表數據表格
    chartDataSheet.clear();

    // 添加表頭
    chartDataSheet.appendRow(['Timestamp', '使用率', '可租借的數量', '可還車的數量']);
   
    // 處理原始數據
    var stationData = rawData.filter(function(row) {
      return String(row[1]) === stationId;
    });

    // 將數據轉換為圖表格式
    stationData.forEach(function(row) {
      var timestamp = row[0];
      var availableBikes = row[4];
      var usingBike = row[5]; // 空車柱數量
      var usePercent = Math.round(usingBike / row[3] * 100)  // 可用車數 / 總車數 = 使用率
      chartDataSheet.appendRow([new Date(timestamp), usePercent, availableBikes, usingBike]);
    }); 

    // 創建圖表
    createChart(chartDataSheet, stationId, stationName);
  });
}



// createChart 函數

function createChart(sheet, stationId, stationName) {
  var charts = sheet.getCharts();

  // 如果已存在圖表,則刪除
  charts.forEach(function(chart) {
    sheet.removeChart(chart);
  });
 
  // 創建新圖表
  var chartBuilder = sheet.newChart();

  // 設置圖表類型為線圖
  chartBuilder.setChartType(Charts.ChartType.LINE);

  // 設置圖表標題,使用站點名稱
  chartBuilder.setOption('title', 'YouBike 站點 ' + stationName + ' 使用率分析');

  // 設置 X 軸
  chartBuilder.setOption('hAxis', {title: '時間'});
 
  // 設置 Y 軸
  chartBuilder.setOption('vAxis', {title: '使用率'});

  // 添加數據範圍
  chartBuilder.addRange(sheet.getRange("A2:B"));
  // 所有範圍:
  // chartBuilder.addRange(sheet.getDataRange());
 
  // 設置圖表位置
  chartBuilder.setPosition(1, 6, 0, 0);

  // 設置圖例
  chartBuilder.setOption('legend', {position: 'bottom'});
 
  // 設置線條顏色
  chartBuilder.setOption('series', {
    // 0: {color: 'blue', labelInLegend: '可租數量'},   // 可用車輛線條顏色
    // 1: {color: 'red', labelInLegend: '可還數量'},     // 空車率線條顏色
    0: {color: 'green', labelInLegend: '使用率'}     // 使用率線條顏色
  });

  // 添加圖表到工作表
  sheet.insertChart(chartBuilder.build());
}
此次針對公館捷運站4個不同出口的youbike站點來觀察不同時段的使用率

數據分析展示


可以得到幾個總結

  1. 公館站1號跟4號出口的使用率比較容易滿載,其原因與該站可租借的數量較少有直接的影響
  2. 公館站2號跟3號出口的可租借數量較高,因此即使使用量有時會升高,也蠻快就會恢復可租借的平衡
  3. 2號出口的使用率比3號出口的低,因此優選首選去租借ubike的站為2號出口



留言
avatar-img
留言分享你的想法!
相當實用啊!
Peter Lu-avatar-img
發文者
2024/08/15
樂風的民藝筆記 謝謝!
高吉米-avatar-img
2024/08/17
可以做成web app網頁方便觀看
avatar-img
欸! 是彼得的資料庫
62會員
45內容數
歡迎來到彼得的沙龍,在這裡,我將與你分享書籍精華的智慧、人際溝通的技巧、理財增值的秘訣,以及情緒管理的策略。不僅幫助你打好財務基礎,還能引領你在人生的每個環節中游刃有餘。如果你渴望成長,並追求更充實的生活,這裡就是你值得關注的空間。立即加入,與我一起探索成長的無限可能!
2025/04/01
什麼是 MCP?簡單來說,MCP (Model Context Protocol) 是一種讓 AI 變得更聰明的協議,它讓 AI 可以直接使用各種外部工具,例如你的檔案系統、Notion 等等,從而大幅提升 AI 的功能和效率。本文深入淺出地解釋 MCP 的三大組成架構,並透過實際案例和常見問題。
Thumbnail
2025/04/01
什麼是 MCP?簡單來說,MCP (Model Context Protocol) 是一種讓 AI 變得更聰明的協議,它讓 AI 可以直接使用各種外部工具,例如你的檔案系統、Notion 等等,從而大幅提升 AI 的功能和效率。本文深入淺出地解釋 MCP 的三大組成架構,並透過實際案例和常見問題。
Thumbnail
2025/03/30
在現代快節奏的生活中,「今天吃什麼」常常成為一個讓我頭疼的問題。每天面對眾多餐廳選擇,很容易陷入決策疲勞。為了解決這個日常煩惱,我設計了一個簡單的餐廳推薦系統,參考交友軟體的左右滑動機制,讓使用者能夠輕鬆選擇餐廳。 純前端技術(HTML, CSS, JS)搭配GAS
Thumbnail
2025/03/30
在現代快節奏的生活中,「今天吃什麼」常常成為一個讓我頭疼的問題。每天面對眾多餐廳選擇,很容易陷入決策疲勞。為了解決這個日常煩惱,我設計了一個簡單的餐廳推薦系統,參考交友軟體的左右滑動機制,讓使用者能夠輕鬆選擇餐廳。 純前端技術(HTML, CSS, JS)搭配GAS
Thumbnail
2025/03/20
在 Laravel 開發 API 時,直接在 Controller 內進行資料加工可能會讓程式碼變得雜亂且難以維護。因此,Laravel 提供 Resource (資源轉換器) 來解決這個問題,讓我們可以統一管理 API 的輸出格式,將模型model或模型集合collection轉換為適合 API
Thumbnail
2025/03/20
在 Laravel 開發 API 時,直接在 Controller 內進行資料加工可能會讓程式碼變得雜亂且難以維護。因此,Laravel 提供 Resource (資源轉換器) 來解決這個問題,讓我們可以統一管理 API 的輸出格式,將模型model或模型集合collection轉換為適合 API
Thumbnail
看更多
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
本文探討如何利用政府提供的YouBike開放數據進行數據分析,以揭示共享單車系統的使用趨勢。作者使用Google Apps Script分析不同時間點的YouBike使用率,並通過線性回歸預測供需平衡,以及針對特定站點的使用模式進行深入挖掘。最後總結了公館不同出口的使用情況,提供了最佳的租借選擇。
Thumbnail
本文探討如何利用政府提供的YouBike開放數據進行數據分析,以揭示共享單車系統的使用趨勢。作者使用Google Apps Script分析不同時間點的YouBike使用率,並通過線性回歸預測供需平衡,以及針對特定站點的使用模式進行深入挖掘。最後總結了公館不同出口的使用情況,提供了最佳的租借選擇。
Thumbnail
GoingBus提供串流影音平臺合租服務,可享有最優惠低廉的價格,立即開通帳號,提供全球支付功能,並提供無理由退款服務。使用指定優惠碼rapeseed首次訂閱可享有10%的折扣優惠。
Thumbnail
GoingBus提供串流影音平臺合租服務,可享有最優惠低廉的價格,立即開通帳號,提供全球支付功能,並提供無理由退款服務。使用指定優惠碼rapeseed首次訂閱可享有10%的折扣優惠。
Thumbnail
對於上班族來說,在處理文案、報告、新聞稿撰寫,或是資料彙整、簡報內容調整等,要是有一個好用小幫手可以支持,絕對能省不少時間與心力。特別在推出Chat GPT後,雖然有說對不少產品帶來衝擊,但也是工作上的好幫手,不過它的付費版卻不便宜....
Thumbnail
對於上班族來說,在處理文案、報告、新聞稿撰寫,或是資料彙整、簡報內容調整等,要是有一個好用小幫手可以支持,絕對能省不少時間與心力。特別在推出Chat GPT後,雖然有說對不少產品帶來衝擊,但也是工作上的好幫手,不過它的付費版卻不便宜....
Thumbnail
某天無意間得知了一個平台「GoingBus串流媒體平台帳號合租」,讓我一年省下4000多元的荷包,在這個物價齊漲的時代,能省下一點荷包真的是生活中的小確幸😍 到底什麼是GoingBus呢? 用一個例子來說明,應該有聽過『共乘』,是指多名乘客共同使用同一輛車通勤或出行,以提高交通工具的利用效
Thumbnail
某天無意間得知了一個平台「GoingBus串流媒體平台帳號合租」,讓我一年省下4000多元的荷包,在這個物價齊漲的時代,能省下一點荷包真的是生活中的小確幸😍 到底什麼是GoingBus呢? 用一個例子來說明,應該有聽過『共乘』,是指多名乘客共同使用同一輛車通勤或出行,以提高交通工具的利用效
Thumbnail
讓 GoingBus 平台直接幫你合租帳號 近十年訂閱制串流平台已經是每個人不可或缺的生活必需品了。現在你有更方便直接的選擇,就是直接使用共乘平台 GoingBus
Thumbnail
讓 GoingBus 平台直接幫你合租帳號 近十年訂閱制串流平台已經是每個人不可或缺的生活必需品了。現在你有更方便直接的選擇,就是直接使用共乘平台 GoingBus
Thumbnail
現代生活忙碌,時間寶貴,我們渴望享受各種優質的娛樂內容,但許多人望而卻步於單獨使用各式各樣媒體平臺。GoingBus串流媒體平臺提供多種優惠合租帳號服務,可節省高達50%費用,並提供即時交付、全天候客服支持、安全保障及無理由退款等多項優勢。
Thumbnail
現代生活忙碌,時間寶貴,我們渴望享受各種優質的娛樂內容,但許多人望而卻步於單獨使用各式各樣媒體平臺。GoingBus串流媒體平臺提供多種優惠合租帳號服務,可節省高達50%費用,並提供即時交付、全天候客服支持、安全保障及無理由退款等多項優勢。
Thumbnail
說起訂閱式內容服務,除了手機上的 Apple Music 之外,個人是沒有習慣使用的,不過,事情總是會有例外的,近期也出現了不少大魔王,不斷利用力道漸重的廣告鐵拳,打擊我僅存的耐心,直到⋯
Thumbnail
說起訂閱式內容服務,除了手機上的 Apple Music 之外,個人是沒有習慣使用的,不過,事情總是會有例外的,近期也出現了不少大魔王,不斷利用力道漸重的廣告鐵拳,打擊我僅存的耐心,直到⋯
Thumbnail
在忙碌的生活中,如何更有效率利用時間並節省開銷?GoingBus 提供了一個解答。透過低價合租,享受 GPT-plus、NETFLIX 等服務,省錢又方便。本文分享了 GoingBus 的使用方式以及注意事項。現在就上車,使用優惠碼「windbro」,首次訂閱就有額外 10% 折扣!
Thumbnail
在忙碌的生活中,如何更有效率利用時間並節省開銷?GoingBus 提供了一個解答。透過低價合租,享受 GPT-plus、NETFLIX 等服務,省錢又方便。本文分享了 GoingBus 的使用方式以及注意事項。現在就上車,使用優惠碼「windbro」,首次訂閱就有額外 10% 折扣!
Thumbnail
實際跑過>1,000趟外送後,對於Uber外送系統的使用上,也發現了一些有趣和特別的事,但寫在前面,這裡分享的還是小哥個人的主觀想法,提出一些現象或問題,也是希望這個系統更好,沒有任何不敬><,畢竟Uber系統賜我吃穿,小哥是相當感恩和推崇的^^。
Thumbnail
實際跑過>1,000趟外送後,對於Uber外送系統的使用上,也發現了一些有趣和特別的事,但寫在前面,這裡分享的還是小哥個人的主觀想法,提出一些現象或問題,也是希望這個系統更好,沒有任何不敬><,畢竟Uber系統賜我吃穿,小哥是相當感恩和推崇的^^。
Thumbnail
在繁忙的城市生活中,尋找停車位往往讓許多駕駛人感到頭痛。隨著科技的進步,現在有了一種新的解決方案:ParkNavi。這個APP應用程序旨在為駕駛人提供即時停車位資訊、預約系統、和多項個性化選項,徹底改善尋找停車位的過程。 即時停車位顯示與收費標準 ParkNavi利用最先進的定位技術,即時顯
Thumbnail
在繁忙的城市生活中,尋找停車位往往讓許多駕駛人感到頭痛。隨著科技的進步,現在有了一種新的解決方案:ParkNavi。這個APP應用程序旨在為駕駛人提供即時停車位資訊、預約系統、和多項個性化選項,徹底改善尋找停車位的過程。 即時停車位顯示與收費標準 ParkNavi利用最先進的定位技術,即時顯
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News