2024-08-12|閱讀時間 ‧ 約 37 分鐘

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

自製 - 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號出口



分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.