diff --git a/Archives/Scripts/fetch_tables_1.py b/Archives/Scripts/fetch_tables_1.py deleted file mode 100644 index 30d3495b7..000000000 --- a/Archives/Scripts/fetch_tables_1.py +++ /dev/null @@ -1,40 +0,0 @@ -# (siggen) PS C:\Github\siggen> python .\Scripts\fetch_tables.py -import pyodbc -import os, json, csv -from tqdm import tqdm -from datetime import datetime - -starting_time = datetime.now() - -credentials_path = os.path.join(os.getcwd(), 'Scripts', 'credentials.json') -with open(credentials_path, 'r') as file: - credentials = json.load(file) - -DSNNAME = credentials["DSNNAME"] -DBUSER = credentials["DBUSER"] -DBPWD = credentials["DBPWD"] - -# 데이터베이스 연결 -cnxn = pyodbc.connect(f'DSN={DSNNAME};UID={DBUSER};PWD={DBPWD};charset=utf-8') -cursor = cnxn.cursor() - -schema = 'snits_siggen' -tables = ['inter_info', 'plan'] - -base_dir = os.path.join(os.getcwd(), 'Data', 'fetched_tables') - -for table in tables: - # 테이블 데이터 다운로드 - cursor.execute(f"SELECT * FROM {schema}.{table}") - - csv_file_path = os.path.join(base_dir, f"{table}.csv") - with open(csv_file_path, 'w', newline='', encoding='utf-8-sig') as csvfile: - csv_writer = csv.writer(csvfile) - columns = [column[0] for column in cursor.description] - csv_writer.writerow(columns) - for row in cursor.fetchall(): - csv_writer.writerow(row) - - cnxn.close() - -print("elapsed time :", datetime.now() - starting_time) \ No newline at end of file diff --git a/Archives/Scripts/generate_signals.ipynb b/Archives/Scripts/generate_signals.ipynb deleted file mode 100644 index 2206b1434..000000000 --- a/Archives/Scripts/generate_signals.ipynb +++ /dev/null @@ -1,1186 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import os\n", - "import sumolib\n", - "import copy\n", - "import json\n", - "from tqdm import tqdm\n", - "from datetime import datetime" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "m = 105\n", - "midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - "next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - "fmins = range(midnight, next_day, 300)\n", - "\n", - "# 현재시각\n", - "present_time = fmins[m]\n", - "sim_start = fmins[m] - 300\n", - "# network and dataframes\n", - "net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n", - "inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - "plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", - "match6 = pd.read_csv('../Intermediates/match6.csv', index_col=0)\n", - "match6 = match6[['node_id', 'phase_no', 'ring_type', 'inc_edge', 'out_edge']].reset_index(drop=True)\n", - "histid = pd.read_csv(f'../Intermediates/histid/histid_{present_time}.csv', index_col=0)\n", - "histid = histid.reset_index(drop=True).drop(columns=['inter_no'])\n", - "\n", - "# helper dictionaries and lists\n", - "inter_node_p = inter_node[inter_node.inter_type=='parent']\n", - "inter2node = dict(zip(inter_node_p['inter_no'], inter_node_p['node_id']))\n", - "node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", - "pa2ch = {'i0':['u00'], 'i1':[], 'i2':['u20'], 'i3':['c30', 'u30', 'u31', 'u32'], 'i6':['u60'], 'i7':[], 'i8':[], 'i9':[]}\n", - "node_ids = sorted(inter_node.node_id.unique())\n", - "parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n", - "nodes = [net.getNode(node_id) for node_id in node_ids]" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def attach_children(histid, match6, parent_ids, pa2ch):\n", - " '''\n", - " 자식교차로에 대한 진입·진출 엣지 정보를 붙여주는 함수\n", - "\n", - " input :\n", - " (1) histid\n", - " - 각 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n", - " - 부모교차로(주교차로)에 대해서만 값이 지정되어 있음\n", - " (2) match6\n", - " - (현시, 링)별 진입·진출엣지\n", - " - 자식교차로(유턴 및 연동교차로)에 대해서도 값이 지정되어 있음\n", - " (3) parent_ids : 부모교차로 목록\n", - " (4) pa2ch : 각 부모교차로id를 부모교차로가 포함하고 있는 자식교차로들의 id들의 리스트로 대응시키는 딕셔너리\n", - "\n", - " output : histids\n", - " - 모든(부모 및 자식) 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n", - " '''\n", - " new_histids = []\n", - " for parent_id in parent_ids:\n", - " for child_id in pa2ch[parent_id]:\n", - " new_histid = histid.copy()[histid.node_id==parent_id]\n", - " new_histid[['inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']] = np.nan\n", - " for i, row in new_histid.iterrows():\n", - " phas_A = row.phas_A\n", - " phas_B = row.phas_B\n", - " new_match = match6[match6.node_id==child_id]\n", - " Arow = new_match[(new_match.phase_no==phas_A) & (new_match.ring_type=='A')]\n", - " if ~ Arow[['inc_edge', 'out_edge']].isna().all().all():\n", - " inc_edge = Arow.iloc[0].inc_edge\n", - " out_edge = Arow.iloc[0].out_edge\n", - " new_histid.loc[i, ['inc_edge_A', 'out_edge_A']] = [inc_edge, out_edge]\n", - " Brow = new_match[(new_match.phase_no==phas_B) & (new_match.ring_type=='B')]\n", - " if ~ Brow[['inc_edge', 'out_edge']].isna().all().all():\n", - " inc_edge = Brow.iloc[0].inc_edge\n", - " out_edge = Brow.iloc[0].out_edge\n", - " new_histid.loc[i, ['inc_edge_B', 'out_edge_B']] = [inc_edge, out_edge]\n", - " new_histid.loc[i, 'node_id'] = child_id\n", - " new_histids.append(new_histid)\n", - " new_histids = pd.concat(new_histids)\n", - " histids = pd.concat([histid.copy(), new_histids])\n", - " histids = histids.sort_values(by=['start_unix', 'node_id', 'phas_A', 'phas_B']).reset_index(drop=True)\n", - " return histids\n", - "histids = attach_children(histid, match6, parent_ids, pa2ch)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def initialize_states(net, nodes, histids):\n", - " '''\n", - " 신호 초기화\n", - "\n", - " input :\n", - " (1) net : 네트워크\n", - " (2) nodes : 노드 목록\n", - " (3) histids : 모든 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n", - "\n", - " output : node2init\n", - " - 각 노드를 초기화된 신호로 맵핑하는 딕셔너리\n", - " - 초기화된 신호란, 우회전을 g로 나머지는 r로 지정한 신호를 말함.\n", - " '''\n", - " node2init = {}\n", - " for node in nodes:\n", - " node_id = node.getID()\n", - " conns = [(c.getJunctionIndex(), c) for c in node.getConnections()]\n", - " conns = [c for c in conns if c[0] >= 0]\n", - " conns = sorted(conns, key=lambda x: x[0])\n", - " state = []\n", - " for i, ci in conns:\n", - " if ci.getTLLinkIndex() < 0:\n", - " continue\n", - " are_foes = False\n", - " for j, cj in conns:\n", - " if ci.getTo() == cj.getTo():\n", - " continue\n", - " if node.areFoes(i, j):\n", - " are_foes = True\n", - " break\n", - " state.append('r' if are_foes else 'g')\n", - " node2init[node_id] = state\n", - "\n", - " # 어떤 연결과도 상충이 일어나지는 않지만, 신호가 부여되어 있는 경우에는 r을 부여\n", - " for _, row in histids.iterrows():\n", - " node_id = row['node_id']\n", - " inc_edge_A = row.inc_edge_A\n", - " inc_edge_B = row.inc_edge_B\n", - " out_edge_A = row.out_edge_A\n", - " out_edge_B = row.out_edge_B\n", - "\n", - " if pd.isna(inc_edge_A) or pd.isna(out_edge_A):\n", - " pass\n", - " else:\n", - " inc_edge_A = net.getEdge(inc_edge_A)\n", - " out_edge_A = net.getEdge(out_edge_A)\n", - " for conn in inc_edge_A.getConnections(out_edge_A):\n", - " index = conn.getTLLinkIndex()\n", - " if index >= 0:\n", - " node2init[node_id][index] = 'r'\n", - "\n", - " if pd.isna(inc_edge_B) or pd.isna(out_edge_B):\n", - " pass\n", - " else:\n", - " inc_edge_B = net.getEdge(inc_edge_B)\n", - " out_edge_B = net.getEdge(out_edge_B)\n", - " for conn in inc_edge_B.getConnections(out_edge_B):\n", - " index = conn.getTLLinkIndex()\n", - " if index >= 0:\n", - " node2init[node_id][index] = 'r'\n", - " return node2init\n", - "node2init = initialize_states(net, nodes, histids)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def assign_signals(histids, node2init, net):\n", - " '''\n", - " 진입·진출엣지를 신호문자열로 배정\n", - "\n", - " input :\n", - " (1) histids : 모든 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지\n", - " (2) node2init : 각 노드를 초기화된 신호로 맵핑하는 딕셔너리\n", - " (3) net : 네트워크\n", - "\n", - " output : sigtable\n", - " - 모든 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 신호문자열\n", - " - 황색 및 적색신호는 아직 반영되지 않았음.\n", - " '''\n", - " sigtable = histids.copy()\n", - " sigtable['init_state'] = sigtable['node_id'].map(node2init)\n", - " sigtable['state'] = sigtable['init_state'].map(lambda x:''.join(x))\n", - " for i, row in sigtable.iterrows():\n", - " node_id = row.node_id\n", - " inc_edge_A = row.inc_edge_A\n", - " inc_edge_B = row.inc_edge_B\n", - " out_edge_A = row.out_edge_A\n", - " out_edge_B = row.out_edge_B\n", - " state = copy.deepcopy(node2init)[node_id]\n", - " if pd.isna(inc_edge_A) or pd.isna(out_edge_A):\n", - " pass\n", - " else:\n", - " inc_edge_A = net.getEdge(inc_edge_A)\n", - " out_edge_A = net.getEdge(out_edge_A)\n", - " for conn in inc_edge_A.getConnections(out_edge_A):\n", - " index = conn.getTLLinkIndex()\n", - " if index >= 0:\n", - " state[index] = 'G'\n", - " sigtable.at[i, 'state'] = ''.join(state)\n", - "\n", - " if pd.isna(inc_edge_B) or pd.isna(out_edge_B):\n", - " pass\n", - " else:\n", - " inc_edge_B = net.getEdge(inc_edge_B)\n", - " out_edge_B = net.getEdge(out_edge_B)\n", - " for conn in inc_edge_B.getConnections(out_edge_B):\n", - " index = conn.getTLLinkIndex()\n", - " if index >= 0:\n", - " state[index] = 'G'\n", - " sigtable.at[i, 'state'] = ''.join(state)\n", - " sigtable = sigtable.dropna(subset='state')\n", - " sigtable = sigtable.reset_index(drop=True)\n", - " sigtable['phase_sumo'] = sigtable.groupby(['node_id', 'start_unix']).cumcount()\n", - " sigtable = sigtable[['node_id', 'start_unix', 'phase_sumo', 'duration', 'state']]\n", - " sigtable = sigtable.sort_values(by=['start_unix', 'node_id'])\n", - " sigtable['start_dt'] = sigtable['start_unix'].apply(lambda x:datetime.fromtimestamp(x))\n", - " return sigtable\n", - "sigtable = assign_signals(histids, node2init, net)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGvCAYAAAD7f7c5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/OQEPoAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA/GklEQVR4nO3de3xU9Z3/8fckIUMCJCFAEgIBKSAKWARRBLzLAq1VsbYoiy66rq4XfpbFUsXaKrYK1BZ1u6igFexDt1Z3pcUb3kDBB/dAFMRFVASLhKCQDOESQub7+yNmJGFOcgbmXGbm9Xw88nh0zvnOnM95zxfPp+fMmQkYY4wAAABckuZ1AQAAILXQfAAAAFfRfAAAAFfRfAAAAFfRfAAAAFfRfAAAAFfRfAAAAFfRfAAAAFdleF1AU+FwWF999ZXatWunQCDgdTkAAMAGY4z27dun4uJipaU1f27Dd83HV199pZKSEq/LAAAAx+HLL79U165dmx3ju+ajXbt2kuqLz8nJ8bgaJLzcXKmqyusq4BfMB8AxoVBIJSUlkeN4c3zXfDRcasnJyaH5QHwwj3A05gPgKDsfmeADpwAAwFU0HwAAwFU0HwAAwFU0HwAAwFU0HwAAwFU0HwAAwFU0HwAAwFU0HwAAwFW++5IxxE84bLRzS6X2h2rUJieozr3zlJbG7+VYIS/7yMo+srKPrGKTyHnRfCSpz9ZXaNlft2h/ZU1kWZu8oM69qrd6DizwsDJ/Ii/7yMo+srKPrGKT6HkFjDHG6yKOFgqFlJubq6qqKr5e/Th9tr5Ci+ZstFw/+t/7J8TkjItAQGphipOXfQmflY35EC8Jn5WLyCo2fs0rluM3Zz6STDhstOyvW5ods+yvW9T1lPyEOT13ItIDaaqrqbNcX5/XJ82+Rirl1ZxkyKql+RAvyZCVW8gqNnbyev+FLeoxoJOv8+LMR5LZsXmv/vbweq/L8I0fLrpHr43+rddlwCeYD0gVY/5joLr0ae/qNmM5fnO3S5LZH6ppeRAAIKn5/VjAZZck0yYnaGvcjyYOUHHvPGeL8YH0rFW66dHzLdd/taVSr/zXBy2+Tqrk1ZxkyKql+RAvyZCVW8gqNnbzsnss8ArNR5Lp3DtPbfKCjT4B3VTb9kGV9E2R66cmrLRguuXqkr755GVTUmTVwnyIl6TIyiVkFRu7eXX2eaPGZZckk5YW0LlX9W52zDlje/OP+FvkZR9Z2UdW9pFVbJIlLz5wmqSi3QPetn1Q54xNjHvA48bmrZXkZV9CZ+XirbZSgmflMrKKjR/ziuX4TfORxBL52+/iJoaDDXnZl7BZudx8SAmclQfIKjZ+y4vmA2jgwcEGPsZ8ABzDrbYAAMC3aD4AAICraD4AAICraD4AAICraD4AAICraD4AAICraD4AAICraD4AAICraD4AAICraD4AAICraD4AAICraD4AAICrMrwuwM9MXZ0OrC3Vkd27ldGpk7IHn6FAerrXZXmOXJpHPtGRizWysUY21hI5m5ibj6VLl+qhhx5SaWmpdu7cqQULFmjMmDFRx958882aM2eOHn74YU2aNOkES3VX6M03tevB6TpSXh5ZllFUpMK7pypn5EgPK/MWuTSPfKIjF2tkY41srCV6NjFfdtm/f78GDBig2bNnNztuwYIFWrlypYqLi4+7OK+E3nxTO342qdGbKklHdu3Sjp9NUujNNz2qzFvk0jzyiY5crJGNNbKxlgzZBIwx5rifHAhEPfOxY8cODRkyRG+88YYuueQSTZo0yfaZj1AopNzcXFVVVSknJ+d4Sztupq5On1484pg3NSIgZRQU6nuvvJwwp7fiwdTV6fNLfqQjFRXRB/g0l0CbNjL79zu+nUTNx2l+y8Wt+WCH37LxE7Kx1nI2AWUUFqrXO2+7nk0sx++4f+YjHA7r2muv1ZQpU9SvX78Wx9fU1KimpibyOBQKxbukmBxYW2rdeEiSqe8uPznzLPeKSgQ+zaV3erq2DDrD6zJ8m4/nXM7FN/PBDuaMNbKxZoyOlJfrwNpStRni33zifrfLzJkzlZGRodtvv93W+OnTpys3NzfyV1JSEu+SYnJk925Ptw8AwIny+7Esrmc+SktL9eijj2rdunUKBAK2njN16lRNnjw58jgUCnnagGR06mRrXMncOcoePNjhavzjwNq1+vKmf29xnN9yCbRpoz7rSh3fTqLm4zS/5eLWfLDDb9n4CdlYs5uN3WOZV+LafCxbtkwVFRXq1q1bZFldXZ3uuOMOPfLII/riiy+OeU4wGFQwGIxnGScke/AZyigq0pFdu6RoH4f59npam+HDU+paY5vhwxM2l0B2tuPbSOR8nOTHXNyYD3b4MRu/IBtrdrPJHuzvy4txvexy7bXX6sMPP1RZWVnkr7i4WFOmTNEbb7wRz005JpCersK7p377oMnZm28fF949NeUmPLk0j3yiIxdrZGONbKwlSzYxNx/V1dWRxkKStm7dqrKyMm3fvl0dOnRQ//79G/21atVKRUVF6tOnT7xrd0zOyJHq8ugjyigsbLQ8o7BQXR59JCHuoXYCuTSPfKIjF2tkY41srCVDNjHfavvuu+/qwgsvPGb5hAkTNH/+/GOWn3TSSQl1q+3REvnb45yUULkEAtFPTTooofJxkS9y8WA+2OGLbHyKbKz5LZtYjt8n9D0fTvBT84Ek4NODDTzCfAAcE8vxmx+WAwAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArsrwugA/qwvXaV3FOu0+sFudsjtpUMEgpafxa4rk0jzyiY5crJGNNbKxlsjZ0HxYeHvb25qxeoZ2HdgVWVaYXai7zrpLI7qP8LAyb5FL88gnOnKxRjbWyMZaomcTMMZfvy8dy0/yOuXtbW9r8ruTZdQ4moACkqRZF8xKiDc33hIyFxd/Qj0h83GBr3JxcT7Y4atsfIZsrPk1m1iO3zQfTdSF6zTqf0c16iabKswu1ILLFiTM6a14qAvXaczCMao4UGE5xo+5ZGW20cHD+x3fTqLm4zS/5eLWfLDDb9n4CdlYaymbgAIqzC7UoisXuZ4NzccJWFO+Rv/6xr+6vl04Y+2/bdTgp/p7XQZ8gvmAVPH0qKd1ZtGZrm4zluM3d7s0sfvAbq9LAADghPj9WMYHTpvolN3J1rjHLn5MZxSe4XA1/lG6q1S3vnNri+P8lkvmdW206p9XOb6dRM3HaX7Lxa35YIffsvETsrFmNxu7xzKv0Hw0MahgkAqzC1VxoOKYD/NI311PG1Y8LKWuNQ4rHpawuWS3ynZ8G4mcj5P8mIsb88EOP2bjF2RjzW42gwoGeVCdfVx2aSI9LV13nXWXpO8+Odyg4fGdZ92ZchOeXJpHPtGRizWysUY21pIlG5qPKEZ0H6FZF8xSQXZBo+WF2YUpfXsXuTSPfKIjF2tkY41srCVDNtzt0oxE/vY4JyVULh58r0NC5eMiX+Tis+/5aOCLbHyKbKz5LRtutQUa+PRgA48wHwDHcKstAADwLZoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgKpoPAADgqgyvC/C1cJ20bblUvUtqWyh1Hybxa4rk0hLyiY5crJGNNbKxlsDZxNx8LF26VA899JBKS0u1c+dOLViwQGPGjJEk1dbW6p577tFrr72mzz//XLm5uRoxYoRmzJih4uLieNfurE0LpUV3SqGvvluWUyyNnin1vcy7urxGLs0jn+jIxRrZWCMbawmeTcyXXfbv368BAwZo9uzZx6w7cOCA1q1bp1/96ldat26dXnrpJW3evFmXXeb/IBrZtFB64V8av6mSFNpZv3zTQm/q8hq5NI98oiMXa2RjjWysJUE2AWOMOe4nBwKNznxEs2bNGp111lnatm2bunXr1uJrhkIh5ebmqqqqSjk5Ocdb2vEL10mP9D/2TY0ISDmdpVtXJczprbgI10mzz5L27bQY4NNcgm2lmmrnt5Oo+TjNb7m4NR/s8Fs2fkI21mxlUyxN2uB6NrEcvx3/zEdVVZUCgYDy8vKirq+pqVFNTU3kcSgUcrqk5m1b3kzjIUmmfv2MEtdKSgw+zuVBP1zy83E+nvIgF1/MBzuYM9bIxpqRQjvqj2U9zvW6GEuO3u1y6NAh3XnnnRo3bpxlFzR9+nTl5uZG/kpKPJ5M1bu83T4AACfK58cyx8581NbWauzYsTLG6PHHH7ccN3XqVE2ePDnyOBQKeduAtC20N278/9R/sjhVbFsuPfeTlsf5LZdpbaW7mzuTFSeJmo/T/JaLW/PBDr9l4ydkY81uNnaPZR5xpPloaDy2bdumxYsXN3vtJxgMKhgMOlHG8ek+rP56WWinpGgfh/n2elrPi1LrWmPPixI3l8w2zm8jkfNxkh9zcWM+2OHHbPyCbKzZzcbnTVncL7s0NB5btmzR22+/rQ4dOsR7E85KS6+/VUmSFGiy8tvHo2ek3oQnl+aRT3TkYo1srJGNtSTJJubmo7q6WmVlZSorK5Mkbd26VWVlZdq+fbtqa2v1k5/8RGvXrtVzzz2nuro6lZeXq7y8XIcPH4537c7pe5k09s/1n6Y+Wk5x/fIEuIfaEeTSPPKJjlyskY01srGWBNnEfKvtu+++qwsvvPCY5RMmTNB9992nHj16RH3ekiVLdMEFF7T4+p7fanu0BP72OEclUi6BgHT8d5Mfn0TKx01+yMWL+WCHH7LxK7Kx5rNsYjl+n9D3fDjBV80HEp9fDzbwBvMBcEwsx29+WA4AALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALiK5gMAALgqw+sCUkFd2Gj11j2q2HdIBe1a66we+UpPC3hdli+RlX1kZV9DVmdLWvnZN2TVDOZVbPycl59ro/lw2KKNOzXt5U3aWXUosqxzbmvde2lfje7f2cPK/Ies7CMr+47OaqukcU+uJCsLzKvY+DkvP9cmcdnFUYs27tQtz65r9OZLUnnVId3y7Dot2rjTo8r8h6zsIyv7yMo+soqNn/Pyc20NOPPhkLqw0bSXN8lEWWckBSTdt3CThvfq6JvTYF6pCxvdu/AjR7LKknTw8JE4VOkPTmaVbMjKPrKKjZ/zslPbtJc36Z/6Fnn6XgaMMdFq9EwoFFJubq6qqqqUk5PjdTnHbcVn32jckyu9LiPlbZ35I/W48xWvy4BPMB+Aen+58WwN7dkhrq8Zy/Gbyy4Oqdh3qOVBAAB4wOtjFJddHFLQrrWtcfOvP1Nn9ch3uBp/W711j66bt6bFcceV1Uxp0/2jjrMy/3E0qyRDVvaRVWz8nJfd2uweo5xC8+GQs3rkq3Nua5VXHYp67S0gqSi3tc7t3Snlr6Ge27uTo1llZybPNHc6q2RCVvaRVWz8nJfd2rxuIrns4pD0tIDuvbSvpPo3+2gNj++9tC//kEVWsSAr+8jKPrKKjZ/z8nNtR6P5cNDo/p31+DWDVJTb+PRWUW5rPX7NIF/ca+0XZGUfWdlHVvaRVWz8nJefa2vA3S4u8PO3zPlN3LMKBCR/TfG4YV7ZF/mG014dtfLTr8mqGcyr2Pg5L7dri+X4TfOB5JbEzQeOA/MBcAy32gIAAN+i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK7K8LoAOKvOGK2srFbF4SMqyMzQ2XltlR7wxy8u+gk5xYa87CMr+8jKvkTPKubmY+nSpXrooYdUWlqqnTt3asGCBRozZkxkvTFG9957r5588klVVlZq+PDhevzxx9W7d+941g0bXt1dqXu27NDOmtrIss7BVvpt7y66pFOed4X5DDnFhrzsIyv7yMq+ZMgq5ssu+/fv14ABAzR79uyo63/3u9/pP//zP/XEE09o1apVatOmjUaNGqVDhw6dcLGw79Xdlfq3jV80mpySVF5Tq3/b+IVe3V3pTWE+Q06xIS/7yMo+srIvWbIKGGPMcT85EGh05sMYo+LiYt1xxx36+c9/LkmqqqpSYWGh5s+fr6uvvrrF1wyFQsrNzVVVVZVycnKOt7SUVmeMBq/YdMzkbBCQVBRspffO6pNQp+mOR3ZGhg4cORJ1XZ0xOm/VZpUfJic7kiGv5uZDPCVDVm4hK/vsZNU52Eprhvb1JKtYjt9x/czH1q1bVV5erhEjRkSW5ebmasiQIVqxYkXU5qOmpkY1NTWRx6FQKJ4lpaSVldWWjYckGUk7a2p18rKN7hXlkZ2Sei7dcFzPTaWc4iER8jqR+RBPiZCVX5CVfUbSVzW1WllZreHt23ldTrPierdLeXm5JKmwsLDR8sLCwsi6pqZPn67c3NzIX0lJSTxLSkkVh53/f3YAAH9KhGOA53e7TJ06VZMnT448DoVCNCAnqCDT3tv63Pd76Oy8tg5X473Pzjst6vKVldUa/+HWFp+fKjm1JFnyspoP8ZQsWbmBrOyzm5XdY4CX4lphUVGRJGnXrl3q3LlzZPmuXbt0+umnR31OMBhUMBiMZxkp7+y8tuocbKXymlpF+0BPw3XBC/JzUuIaapv09KjLL8jPIacYJEteVvMhnpIlKzeQlX12s0qEJi2ul1169OihoqIivfPOO5FloVBIq1at0tChQ+O5KTQjPRDQb3t3kVQ/GY/W8Pg3vbuk/D9kcooNedlHVvaRlX3JlFXMzUd1dbXKyspUVlYmqf5DpmVlZdq+fbsCgYAmTZqk3/72t1q4cKE2bNigf/mXf1FxcXGj7wKB8y7plKen+p+komCrRss7B1vpqf4nJcy94E4jp9iQl31kZR9Z2ZcsWcV8q+27776rCy+88JjlEyZM0Pz58yNfMjZ37lxVVlbqnHPO0WOPPaaTTz7Z1utzq218Jfq34J2wQECyMcVTPqcYJWxeNudDPCVsVh4gK/v8mFUsx+8T+p4PJ9B8IK48ONjAx5gPgGNiOX7zw3IAAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVNB8AAMBVGV4XgPgxpk6VlWtUU1OhYLBAeXlnKhBI97osXyAba2RjjWyiIxdrfs/GL/XRfCSJioo39MmW+1VTUx5ZFgwW6eTev1ZBwSgPK/Me2VgjG2tkEx25WPN7Nn6qL2CMv35fOpaf5EW9ioo3tGHjbZKavpUBSdJp/Wf7YuJ7wQQCWvxOT5HNsVJy3gQCko3/5KVkNjaQizW/Z+NGfbEcvznzkeCMqdMnW+7XsRNK3y4L6JMt9ys/f5ivTv25wZg61e8x2TRlTJ0++WSaUi2bNEnhugPNjknVbFpCLtb8no29+n6jTp1GuFYfZz4S3N69K7Vu/Xivy/Ctiy7+XIvf+Z7XZcAnmA+AtUEDn1P79mcf9/NjOX5zt0uCq6mp8LoEAEAScPN4wmWXBBcMFtgaN2DA02qfd6bD1fjL3so1ki5ocVyqZvPBB//a4rjky6aNLjh/Q7MjUjeb5pGLNb9nY7c+u8eTeKD5SHB5eWcqGCxSTc0uRb+eF1AwWKQO+eek3HXYDvnnfPu/AiKbxjrkn5Oy8yY9PbvZ9amcTXPIxZrfs7FbX56LjRGXXRJcIJCuk3v/uuFR07WSpJN7/yrl/mMgqck+k83RmDfWyCY6crHm92z8WB/NRxIoKBil0/rPVjBY2Gh5MFjk+e1dXgtIZGOBeWONbKIjF2t+z8Zv9XG3SxLxyzfX+cq33+tANtZSKhub3/PRIKWyiQG5WPN7Nk7WF8vxm+YDyS3Ggw2SHPMBcAy32gIAAN+i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK6i+QAAAK7K8LqARBcOh7Vt2zZVV1erbdu26t69u9LS6OkksrFCLtbIxhrZWCOb6PycS9ybj7q6Ot1333169tlnVV5eruLiYl133XW65557FAgE4r05T23atEmLFi1SKBSKLMvJydHo0aPVt29fDyvzHtlERy7WyMYa2Vgjm+j8nkvAmPj+vvSDDz6oWbNm6ZlnnlG/fv20du1aXX/99XrggQd0++23t/j8WH6S10ubNm3SCy+8YLl+7NixvniDveCrbHz0E+q+ysVnXMvGR/PBLuaNNbKJzqtcYjl+x/3Mx/Lly3X55ZfrkksukSSddNJJ+stf/qLVq1fHe1OeCYfDWrRoUbNjFi1apO9973u+OcXllnA4rNdff73ZMW5m00pS7eHDjm+nJX7LxU/czMYv88Eu5o01sonObi6nnHKKp7k4cuZj7ty5evPNN3XyySfrgw8+0MiRIzVr1iyNHz/+mPE1NTWqqamJPA6FQiopKfH1mY+tW7fqmWee8boM2HDvtGmadu+9XpcBn2A+APUmTJigHj16xPU1PT3zcddddykUCumUU05Renq66urq9MADD0RtPCRp+vTpmjZtWrzLcFR1dbXXJQAAcNy8Po7F/czH888/rylTpuihhx5Sv379VFZWpkmTJmnWrFmaMGHCMeOT+czH+PHj1b17dxcq8o9t27bpueeea3GcW9m0CgZVe9T88orfcvETN7Pxy3ywi3ljjWyis5tL0p35mDJliu666y5dffXVkqTTTjtN27Zt0/Tp06M2H8FgUMFgMN5lOKp79+7Kyclp9CnipnJyctSzZ8+UutYoST179vRdNpmZma5spzl+zMUv3M7GD/PBLuaNNbKJzm4uXjdkcX9HDhw4cMwbnZ6ernA4HO9NeSYtLU2jR49udszo0aNTasI3IJvoyMUa2VgjG2tkE12i5BL3rV966aV64IEH9Oqrr+qLL77QggULNGvWLF1xxRXx3pSn+vbtq7Fjxx5zaiknJydlb+9qQDbRkYs1srFGNtbIJrpEyCXun/nYt2+ffvWrX2nBggWqqKhQcXGxxo0bp1//+te2Tncmyvd8NPDzN8h5zRfZ+PB7HXyRi085no0P54NdzBtrZBOd27nEcvyOe/NxohKt+YDPJfDBBg5gPgCOieX4TWsIAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcleF1AW4xYaOarVUK7zustHaZCvbIVSAt4HVZjkv1/Q5KqvmsMuX2O5Xe71TcZ4n9Zr8Te79Tovk4uPFrVb78meqqDkeWpedmKu/Snsrq39HDypzFfh9WF0lfP7kh5fa7QbLvdyrus8R+s9+Jv99Jf9nl4Mav9c2zHzd60ySpruqwvnn2Yx3c+LVHlTmL/Wa/peTe71TcZ4n9Zr/rJfp+J/WZDxM2qnz5s2bH7F34mTJ75SX06aumTNho70L2Oxr2Ozn2+3j3OSDJHK5zuDrnpOJ7LbHfzal8+XO17tsh4fY7YIwxXhdxtFAopNzcXFVVVSknJ+eEXuvQZ5X6+skNcaoMiajLzPO0486lXpcBn2A+IBl1vPE0te6Z53UZMR2/k/qyS3jf4ZYHAQCQwBLxWJfUl13S2mXaGtfh+n4K9sh1uBr31Gyt0jfzPmpxHPudHFJxv497n2dKxfcPc7AyZ6Xiey2x3y2xe6zzk6RuPoI9cpWem3nMB3WOlp4bVOve7RPuellzWvduz35bYL+TY79PZJ8DmelOl+eYVHyvJfa7pf1OxIYrqS+7BNICyru0Z7Nj8i79XlJNVon9bg77nRxScZ8l9rs57HdiSermQ5Ky+ndUh2tOVXpu49NS6blBdbjm1IS9R7ol7Df7LSX3fqfiPkvsN/tdL9H3O6nvdjlasn07nF2pvt/BXu1V8+nelNvvVHq/Y9rnQEDy13/yjlsqvtcS++3n/Y7l+J0yzQdSVBIdbBAHzAfAMdxqCwAAfIvmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuCrD6wLgnHC4Tjs+/kjVlXvVNq+9upzaT2lp6V6X5VvkZR9Z2UdW9pFVbBI5L0eajx07dujOO+/U66+/rgMHDqhXr16aN2+eBg8e7MTmEMWWVcu1eP5cVe/5OrKsbX5HXXTdTeo9ZJiHlfkTedlHVvaRlX1kFZtEzytgTHx/X3rv3r0aOHCgLrzwQt1yyy3q1KmTtmzZop49e6pnz54tPj+Wn+RFdFtWLdfCWQ9arr9s8t0JMTnjwsZPqJOXfQmflY35EC8Jn5WLyCo2fs0rluN33M98zJw5UyUlJZo3b15kWY8ePeK9GVgIh+u0eP7cZscsnj9H3U4bkDCn505EhqQjhw5Zrg+H67R43pxmXyOV8mpOMmTV0nyIl2TIyi1kFRs7eS15Zq56njnE13nF/cxH3759NWrUKP3jH//Qe++9py5duujWW2/VjTfeGHV8TU2NampqIo9DoZBKSko483GcvvzoQ71w/91el+Ebk194VbPGXuJ1GfAJ5gNSxdhfP6iSft93dZuxnPmI+90un3/+uR5//HH17t1bb7zxhm655RbdfvvteuaZZ6KOnz59unJzcyN/JSUl8S4ppVRX7vW6BACAx/x+LIj7mY/MzEwNHjxYy5cvjyy7/fbbtWbNGq1YseKY8Zz5iC+7Zz5+fNd96npqfxcq8lZGVpaOHDxouf4fH2/USzPua/F1UiWv5iRDVi3Nh3hJhqzcQlaxsZuX3898xP0zH507d1bfvn0bLTv11FP1v//7v1HHB4NBBYPBeJeRsrqc2k9t8zs2+gR0U+06dFT3AQN9fT0wnlq1bm25rvuAgeRlU7Jk1dx8iJdkycoNZBUbu3l1ObWfi1XFLu6XXYYPH67Nmzc3WvbJJ5+oe/fu8d4UokhLS9dF193U7JgLJ9zEP+JvkZd9ZGUfWdlHVrFJlrziftllzZo1GjZsmKZNm6axY8dq9erVuvHGGzV37lyNHz++xedzq218RLsHvF2HjrpwQmLcAx43Nm+tJC/7EjorF2+1lRI8K5eRVWz8mFcsx++4Nx+S9Morr2jq1KnasmWLevToocmTJ1ve7dIUzUf8JPK338VNDAcb8rIvYbNyufmQEjgrD5BVbPyWl+fNx4mg+UBceXCwgY8xHwDHeHqrLQAAQHNoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKtoPgAAgKsyvC7Az8Jho51bKrU/VKM2OUF17p2ntLSA12V5jlyaRz7RkYs1srFGNtYSORuaDwufra/Qsr9u0f7KmsiyNnlBnXtVb/UcWOBhZd4il+aRT3TkYo1srJGNtUTPJmCMv35fOpaf5HXKZ+srtGjORsv1o/+9f0K8ufGWkLm4+BPqCZmPC3yVi4vzwQ5fZeMzZGPNr9nEcvzmzEcT4bDRsr9uaXbMsr9uUddT8hPm9FY81OfySbNj/JhLhqQjNXWObydR83Ga33Jxaz7Y4bds/IRsrNnJ5v0XtqjHgE6+zoYzH03s2LxXf3t4vevbhTNunXOxHvv3d7wuAz7BfECqGPMfA9WlT3tXtxnL8Zu7XZrYH6ppeRAAAD7m92MZl12aaJMTtDXuRxMHqLh3nrPF+MhXWyr1yn990OI43+UyR7rp0fMd30zC5uMw3+Xi0nyww3fZ+AjZWLObjd1jmVdoPpro3DtPbfKCjT5B3FTb9kGV9E2ta40lffMTNpdWwXTHt5HI+TjJj7m4MR/s8GM2fkE21uxm09nnTRmXXZpISwvo3Kt6NzvmnLG9U27Ck0vzyCc6crFGNtbIxlqyZMMHTi1Eu4e6bfugzhmbGPdQOyXhcnH51sqEy8clvsnFZ7faSj7KxofIxpofs4nl+E3z0YxE/vY4JyVULh4cbBIqHxf5IhcfNh+ST7LxKbKx5rdsaD6ABj492MAjzAfAMdxqCwAAfIvmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuIrmAwAAuCrD6wJSgamr04G1pTqye7cyOnVS9uAzFEhP97osXyIr+8jKvoassiUdWLWarJrBvIqNn/Pyc22ONx8zZszQ1KlT9bOf/UyPPPKI05vzndCbb2rXg9N1pLw8siyjqEiFd09VzsiRHlbmP2RlH1nZd3RWp0jaPmECWVlgXsXGz3n5uTbJ4csua9as0Zw5c/T973/fyc34VujNN7XjZ5MavfmSdGTXLu342SSF3nzTo8r8h6zsIyv7yMo+soqNn/Pyc20NAsYY48QLV1dXa9CgQXrsscf029/+VqeffrqtMx+hUEi5ubmqqqpSTk6OE6W5wtTV6dOLRxzz5kcEpIyCQn3vlZd9cxrMK6auTp9f8iMdqaiIPuAEsgq0aSOzf38cqvQHJ7NKNtGyOmXz/+n/+pxS/4CsIphXsfFzXi3XFlBGYaF6vfN23GuL5fjtWPMxYcIE5efn6+GHH9YFF1xg2XzU1NSopqYm8jgUCqmkpCThm4/9q1Zr+4QJXpeR8hodbJDymA9AvW7PPKM2Q86K62vG0nw48pmP559/XuvWrdOaNWtaHDt9+nRNmzbNiTI8dWT3bq9LAAAgKq+PUXFvPr788kv97Gc/01tvvaXWrVu3OH7q1KmaPHly5HHDmY9El9Gpk61xJXPnKHvwYIer8bcDa9fqy5v+vcVxx5VVmzbqs670OCvzH0ezSjJkZR9ZxcbPedmtze4xyilxbz5KS0tVUVGhQYMGRZbV1dVp6dKl+q//+i/V1NQo/ajrTMFgUMFgMN5leC578BnKKCrSkV27pGhXtr697tZm+PCUv4baZvhwR7MKZGfHoUp/cDqrZEJW9pFVbPycl93asgef4WpdTcX9bpeLL75YGzZsUFlZWeRv8ODBGj9+vMrKyho1HskskJ6uwrunfvsg0GRl/ePCu6fyD1lkFQuyso+s7COr2Pg5Lz/XdrS4Nx/t2rVT//79G/21adNGHTp0UP/+/eO9OV/LGTlSXR59RBmFhY2WZxQWqsujj/jiXmu/ICv7yMo+srKPrGLj57z8XFsDx+52OVpzd7s0lSy32h7Nz98y5zdxzyoQiH7qMQkwr+yLfMPp2UN0YOUqsmoG8yo2fs7L7dp8cavt8UrG5gMeSuLmA8eB+QA4JpbjNz8sBwAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXEXzAQAAXJXhdQGpqC5cp3UV67T7wG51yu6kQQWDlJ7mj19B9Buyig152UdW9pGVfWRlD82Hy97e9rZmrJ6hXQd2RZYVZhfqrrPu0ojuIzyszH/IKjbkZR9Z2UdW9pGVfQFj/PX70rH8JG+ieXvb25r87mQZNY48oIAkadYFs5ig34pbVinyE+rMLZsCAb39xVtkZRPzyj6yiu34TfPhkrpwnUb976hGHXFThdmFWnDZgpQ/RVcXrtOYhWNUcaDCcozdrLIy2+jg4f3xLtFX4plXssvKbKMRL1xEVjYwr+xrKauAAirMLtSiKxcldVY0Hz60pnyN/vWNf/W6jJTz4XUb9f35/b0uAz7BfICXnh71tM4sOtPrMhwTy/Gbu11csvvAbq9LAAB4iOPAd/jAqUs6ZXeyNe6xix/TGYVnOFyNv5XuKtWt79za4jhbWV3XRqv+eVWcKvOnuOaV7K5rY2sYWTGvYmE3K7vHgVRA8+GSQQWDVJhdqIoDFcd8IEn67prgsOJhSX1N0I5hxcPimlV2q2wnyvSNeOeVzIxEVjYxr+yzm9WggkEeVOdPXHZxSXpauu466y5J3336uUHD4zvPujPl/xFLZBUr8rIvIJGVTcwr+8gqdjQfLhrRfYRmXTBLBdkFjZYXZhemxG1YsSCr2JCXfWRlH1nZR1ax4W4XD/ANePadcFYp8j0fDZhbLThqPpCVfWRlXypnxa22QIMUaz7QAuYD4BhutQUAAL5F8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFxF8wEAAFyV4XUBcFi4Ttq2XKreJbUtlLoPk1LkFxZjQk6xIS/7yMo+srIvwbOKe/Mxffp0vfTSS/q///s/ZWVladiwYZo5c6b69OkT702hJZsWSovulEJffbcsp1gaPVPqe5l3dfkNOcWGvOwjK/vIyr4kyCrul13ee+893XbbbVq5cqXeeust1dbWauTIkdq/f3+8N4XmbFoovfAvjSenJIV21i/ftNCbuvyGnGJDXvaRlX1kZV+SZBUwxhgnN7B7924VFBTovffe03nnndfi+FAopNzcXFVVVSknJ8fJ0pJXuE56pP+xkzMiIOV0lm5dlVCn6Y5LsK1UUx19XbhOmn2WtG+nxZNTKCc7kiGv5uZDPCVDVm4hK/tsZVUsTdrgSVaxHL8d/8xHVVWVJCk/Pz/q+pqaGtXU1EQeh0Ihp0tKftuWN9N4SJKpXz+jxLWSPPVg8XE+McVyOmEJktdxz4d4SpCsfIGs7DNSaEf9MaDHuV4X0yxH73YJh8OaNGmShg8frv79+0cdM336dOXm5kb+SkqYYCesepfXFQAAvJIAxwBHL7vccsstev311/X++++ra9euUcdEO/NRUlLCZZcTsXWZ9MyPWh43/n/qPyGdzJo7zb5tufTcT1p+jVTIyY5kyMutyy7JkJVbyMo+u1lNeMWTMx++uOwyceJEvfLKK1q6dKll4yFJwWBQwWDQqTJSU/dh9df9QjslRestv70u2POi1LiGmtkm+vKeF5FTLJIlL6v5EE/JkpUbyMo+u1klQJMW98suxhhNnDhRCxYs0OLFi9WjR494bwItSUuvv+VKkhRosvLbx6Nn8A+ZnGJDXvaRlX1kZV8SZRX35uO2227Ts88+q//+7/9Wu3btVF5ervLych08eDDem0Jz+l4mjf1z/afEj5ZTXL88Qe4Fdxw5xYa87CMr+8jKviTJKu6f+QgEmnZj9ebNm6frrruuxedzq22cJfi34J2wQECyM8VTPadYJWpedudDPCVqVl4gK/t8mFUsx2/Hv+cjVjQfiCsvDjbwL+YD4JhYjt/8sBwAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHAVzQcAAHBVhtcF+Fld2Gj11j2q2HdIBe1a66we+UpPi/6rvamEXJpHPtGRizWysUY21hI5G5oPC4s27tS0lzdpZ9WhyLLOua1176V9Nbp/Zw8r8xa5NI98oiMXa2RjjWysJXo2AWP89fvSsfwkr1MWbdypW55dp6bBNPSTj18zKCHe3HhLyFxc/An1hMzHBb7KxcX5YIevsvEZsrHm12xiOX5z5qOJurDRtJc3HfOmSpJR/Zt738JNGt6rY8Kc3oqHurDRvQs/SrhcsiQdPHzE8e0kaj5O81subs0HO/yWjZ+QjTU72Ux7eZP+qW+Rr7PhzEcTKz77RuOeXOn6duGMrTN/pB53vuJ1GfAJ5gNSxV9uPFtDe3ZwdZuxHL+526WJin2HWh4EAICP+f1YxmWXJgratbY1bv71Z+qsHvkOV+Mfq7fu0XXz1rQ4zne5zJQ23T/K8c0kbD4O810uLs0HO3yXjY+QjTW72dg9lnmF5qOJs3rkq3Nua5VXHYp6TS0gqSi3tc7t3cnX19Pi7dzenRI2l+xM56d5IufjJD/m4sZ8sMOP2fgF2Vizm43fmzIuuzSRnhbQvZf2lfTdJ4cbNDy+99K+KTfhyaV55BMduVgjG2tkYy1ZsqH5iGJ0/856/JpBKsptfNqqKLd1St/eRS7NI5/oyMUa2VgjG2vJkA13uzQjkb89zkkJlYsH3+uQUPm4yBe5+Ox7Phr4IhufIhtrfssmluM3zQeSm08PNvAI8wFwDLfaAgAA36L5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArqL5AAAArvLHzzsepeELV0OhkMeVIGkwl3A05gPgiIbjtp0vTvdd87Fv3z5JUklJiceVIGnk5npdAfyE+QA4at++fcpt4d+Z737bJRwO66uvvlK7du0UCCTvjweFQiGVlJToyy+/5DdsHELGziJfZ5Gv88g4vowx2rdvn4qLi5WW1vynOnx35iMtLU1du3b1ugzX5OTkMOkdRsbOIl9nka/zyDh+Wjrj0YAPnAIAAFfRfAAAAFfRfHgkGAzq3nvvVTAY9LqUpEXGziJfZ5Gv88jYO777wCkAAEhunPkAAACuovkAAACuovkAAACuovkAAACuovk4AUuXLtWll16q4uJiBQIB/e1vf7Mce/PNNysQCOiRRx5ptHzPnj0aP368cnJylJeXpxtuuEHV1dWNxnz44Yc699xz1bp1a5WUlOh3v/udA3vjT3Yy/vjjj3XZZZcpNzdXbdq00Zlnnqnt27dH1h86dEi33XabOnTooLZt2+rKK6/Url27Gr3G9u3bdckllyg7O1sFBQWaMmWKjhw54vTuea6lfKurqzVx4kR17dpVWVlZ6tu3r5544olGY8jX2vTp03XmmWeqXbt2Kigo0JgxY7R58+ZGY+KV37vvvqtBgwYpGAyqV69emj9/vtO757mW8t2zZ4/+3//7f+rTp4+ysrLUrVs33X777aqqqmr0OuTrAYPj9tprr5lf/vKX5qWXXjKSzIIFC6KOe+mll8yAAQNMcXGxefjhhxutGz16tBkwYIBZuXKlWbZsmenVq5cZN25cZH1VVZUpLCw048ePNxs3bjR/+ctfTFZWlpkzZ46De+YfLWX86aefmvz8fDNlyhSzbt068+mnn5q///3vZteuXZExN998sykpKTHvvPOOWbt2rTn77LPNsGHDIuuPHDli+vfvb0aMGGHWr19vXnvtNdOxY0czdepUt3bTMy3le+ONN5qePXuaJUuWmK1bt5o5c+aY9PR08/e//z0yhnytjRo1ysybN89s3LjRlJWVmR/+8IemW7duprq6OjImHvl9/vnnJjs720yePNls2rTJ/PGPfzTp6elm0aJFru6v21rKd8OGDebHP/6xWbhwofn000/NO++8Y3r37m2uvPLKyGuQrzdoPuLEqvn4xz/+Ybp06WI2btxounfv3qj52LRpk5Fk1qxZE1n2+uuvm0AgYHbs2GGMMeaxxx4z7du3NzU1NZExd955p+nTp49j++JX0TK+6qqrzDXXXGP5nMrKStOqVSvz4osvRpZ9/PHHRpJZsWKFMab+AJyWlmbKy8sjYx5//HGTk5PTKPdkFy3ffv36mfvvv7/RskGDBplf/vKXxhjyjVVFRYWRZN577z1jTPzy+8UvfmH69evXaFtXXXWVGTVqlNO75CtN843mhRdeMJmZmaa2ttYYQ75e4bKLg8LhsK699lpNmTJF/fr1O2b9ihUrlJeXp8GDB0eWjRgxQmlpaVq1alVkzHnnnafMzMzImFGjRmnz5s3au3ev8zvhY+FwWK+++qpOPvlkjRo1SgUFBRoyZEijSwelpaWqra3ViBEjIstOOeUUdevWTStWrJBUn/Fpp52mwsLCyJhRo0YpFArpo48+cm1//GjYsGFauHChduzYIWOMlixZok8++UQjR46URL6xajjdn5+fLyl++a1YsaLRazSMaXiNVNE0X6sxOTk5ysio/2kz8vUGzYeDZs6cqYyMDN1+++1R15eXl6ugoKDRsoyMDOXn56u8vDwy5uh/FJIijxvGpKqKigpVV1drxowZGj16tN58801dccUV+vGPf6z33ntPUn1GmZmZysvLa/TcwsJCMrbhj3/8o/r27auuXbsqMzNTo0eP1uzZs3XeeedJIt9YhMNhTZo0ScOHD1f//v0lxS8/qzGhUEgHDx50Ynd8J1q+TX399df6zW9+o5tuuimyjHy94btftU0WpaWlevTRR7Vu3ToFAgGvy0lK4XBYknT55ZfrP/7jPyRJp59+upYvX64nnnhC559/vpflJYU//vGPWrlypRYuXKju3btr6dKluu2221RcXHzM/xNE82677TZt3LhR77//vtelJKWW8g2FQrrkkkvUt29f3Xfffe4Wh2Nw5sMhy5YtU0VFhbp166aMjAxlZGRo27ZtuuOOO3TSSSdJkoqKilRRUdHoeUeOHNGePXtUVFQUGdP0k+8NjxvGpKqOHTsqIyNDffv2bbT81FNPjdztUlRUpMOHD6uysrLRmF27dpFxCw4ePKi7775bs2bN0qWXXqrvf//7mjhxoq666ir9/ve/l0S+dk2cOFGvvPKKlixZoq5du0aWxys/qzE5OTnKysqK9+74jlW+Dfbt26fRo0erXbt2WrBggVq1ahVZR77eoPlwyLXXXqsPP/xQZWVlkb/i4mJNmTJFb7zxhiRp6NChqqysVGlpaeR5ixcvVjgc1pAhQyJjli5dqtra2siYt956S3369FH79u3d3SmfyczM1JlnnnnMrYuffPKJunfvLkk644wz1KpVK73zzjuR9Zs3b9b27ds1dOhQSfUZb9iwoVEj+NZbbyknJ+eYxiaV1NbWqra2Vmlpjf8zkZ6eHjnrRL7NM8Zo4sSJWrBggRYvXqwePXo0Wh+v/IYOHdroNRrGNLxGsmopX6n+jMfIkSOVmZmphQsXqnXr1o3Wk69HPP7Aa0Lbt2+fWb9+vVm/fr2RZGbNmmXWr19vtm3bFnV807tdjKm/1XbgwIFm1apV5v333ze9e/dudKttZWWlKSwsNNdee63ZuHGjef755012dnbK3GrbUsYvvfSSadWqlZk7d67ZsmVL5Ba4ZcuWRV7j5ptvNt26dTOLFy82a9euNUOHDjVDhw6NrG+41W7kyJGmrKzMLFq0yHTq1CklbgVtKd/zzz/f9OvXzyxZssR8/vnnZt68eaZ169bmsccei7wG+Vq75ZZbTG5urnn33XfNzp07I38HDhyIjIlHfg23gk6ZMsV8/PHHZvbs2SlxK2hL+VZVVZkhQ4aY0047zXz66aeNxhw5csQYQ75eofk4AUuWLDGSjvmbMGFC1PHRmo9vvvnGjBs3zrRt29bk5OSY66+/3uzbt6/RmA8++MCcc845JhgMmi5dupgZM2Y4tEf+YyfjP/3pT6ZXr16mdevWZsCAAeZvf/tbo9c4ePCgufXWW0379u1Ndna2ueKKK8zOnTsbjfniiy/MD37wA5OVlWU6duxo7rjjjsiteMmspXx37txprrvuOlNcXGxat25t+vTpY/7whz+YcDgceQ3ytRYtW0lm3rx5kTHxym/JkiXm9NNPN5mZmeZ73/teo20kq5bytZrfkszWrVsjr0O+7gsYY4yz51YAAAC+w2c+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACAq2g+AACw4YEHHtCwYcOUnZ19zC8RWwkEAlH/HnroociYPXv2aPz48crJyVFeXp5uuOEGVVdXR329Tz/9VO3atWt2+88//7wCgYDGjBnTaPlLL72kkSNHqkOHDgoEAiorK7O1D03Nnj1bp556qrKystSnTx/9+c9/jvk1aD4AAPjWBRdcoPnz50ddd/jwYf30pz/VLbfcYvv1du7c2ejv6aefViAQ0JVXXhkZM378eH300Ud666239Morr2jp0qW66aabjnmt2tpajRs3Tueee67l9r744gv9/Oc/jzpm//79OuecczRz5kzb9Tf1+OOPa+rUqbrvvvv00Ucfadq0abrtttv08ssvx/ZCXn/FKgAAfnH++ee3+NXp8+bNM7m5ucf1+pdffrm56KKLIo83bdpkJJk1a9ZElr3++usmEAiYHTt2NHruL37xC3PNNddYbv/IkSNm2LBh5qmnnjITJkwwl19+edQatm7daiSZ9evXH7Nu79695oYbbjAdO3Y07dq1MxdeeKEpKyuLrB86dKj5+c9/3ug5kydPNsOHD7ex99/hzAcAAC7YtWuXXn31Vd1www2RZStWrFBeXp4GDx4cWTZixAilpaVp1apVkWWLFy/Wiy++qNmzZ1u+/v3336+CgoJGrx+rn/70p6qoqNDrr7+u0tJSDRo0SBdffLH27NkjSaqpqTnml4GzsrK0evXqRr++3hKaDwAAXPDMM8+oXbt2+vGPfxxZVl5eroKCgkbjMjIylJ+fr/LycknSN998o+uuu07z589XTk5O1Nd+//339ac//UlPPvnkcdf3/vvva/Xq1XrxxRc1ePBg9e7dW7///e+Vl5en//mf/5EkjRo1Sk899ZRKS0tljNHatWv11FNPqba2Vl9//bXtbdF8AABS1oMPPqi2bdtG/pYtW6abb7650bLt27fHZVtPP/20xo8ff8yZg5bceOON+ud//medd955Udfv27dP1157rZ588kl17NjxuOv74IMPVF1drQ4dOjTa/61bt+qzzz6TJP3qV7/SD37wA5199tlq1aqVLr/8ck2YMEGSlJZmv6XIOO4qAQBIcDfffLPGjh0beTx+/HhdeeWVjc5OFBcXn/B2li1bps2bN+uvf/1ro+VFRUWqqKhotOzIkSPas2ePioqKJNVfclm4cKF+//vfS5KMMQqHw8rIyNDcuXM1aNAgffHFF7r00ksjrxEOhyXVn0XZvHmzevbs2WKN1dXV6ty5s959991j1jXcXZOVlaWnn35ac+bM0a5du9S5c2fNnTtX7dq1U6dOnWznQfMBAEhZ+fn5ys/PjzzOyspSQUGBevXqFdft/OlPf9IZZ5yhAQMGNFo+dOhQVVZWqrS0VGeccYak+mYjHA5ryJAhkuo/F1JXVxd5zt///nfNnDlTy5cvV5cuXZSVlaUNGzY0et177rlH+/bt06OPPqqSkhJbNQ4aNEjl5eXKyMjQSSed1OzYVq1aqWvXrpLqb+390Y9+xJkPAADibfv27dqzZ4+2b9+uurq6yPdk9OrVS23btpUknXLKKZo+fbquuOKKyPNCoZBefPFF/eEPfzjmNU899VSNHj1aN954o5544gnV1tZq4sSJuvrqqyNnXE499dRGz1m7dq3S0tLUv3//yLKj/7f03ZmKo5c31P7VV19JkjZv3iyp/uxLUVGRRowYoaFDh2rMmDH63e9+p5NPPllfffWVXn31VV1xxRUaPHiwPvnkE61evVpDhgzR3r17NWvWLG3cuFHPPPNMTFnymQ8AAGz49a9/rYEDB+ree+9VdXW1Bg4cqIEDB2rt2rWRMZs3b1ZVVVWj5z3//PMyxmjcuHFRX/e5557TKaecoosvvlg//OEPdc4552ju3Llxr3/hwoUaOHCgLrnkEknS1VdfrYEDB+qJJ56QVP+FaK+99prOO+88XX/99Tr55JN19dVXa9u2bSosLJQk1dXV6Q9/+IMGDBigf/qnf9KhQ4e0fPnyFs+UNBUwxpi47h0AAEAzOPMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABcRfMBAABc9f8BT+V5YpO7bx8AAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "k = 0\n", - "for node_id, group in sigtable.groupby('node_id'):\n", - " k += 1\n", - " plt.plot(group.start_unix.unique(), [k] * len(group.start_unix.unique()), marker='o')\n", - " plt.axvline(present_time - 300, c='r', linewidth=.5)\n", - " plt.axvline(present_time, c='r', linewidth=.5)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'c30': 4,\n", - " 'i0': 3,\n", - " 'i1': 4,\n", - " 'i2': 4,\n", - " 'i3': 4,\n", - " 'i6': 4,\n", - " 'i7': 4,\n", - " 'i8': 4,\n", - " 'i9': 4,\n", - " 'u00': 3,\n", - " 'u20': 4,\n", - " 'u30': 4,\n", - " 'u31': 4,\n", - " 'u32': 4,\n", - " 'u60': 4}" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# node2num_cycles : A dictionary that maps a node_id to the number of cycles\n", - "def get_node2num_cycles(plan, node_ids):\n", - " Aplan = plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']]\n", - " grouped = Aplan.groupby('inter_no')\n", - " df = grouped.agg({'cycle': 'min'}).reset_index()\n", - " df = df.rename(columns={'cycle': 'min_cycle'})\n", - " df['num_cycle'] = 300 // df['min_cycle'] + 2\n", - " inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle']))\n", - " node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids}\n", - " with open('../Intermediates/node2numcycles.json', 'w') as file:\n", - " json.dump(node2numcycles, file, indent=4)\n", - " return node2numcycles\n", - "node2num_cycles = get_node2num_cycles(plan, node_ids)\n", - "node2num_cycles" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def set_timepoints(sigtable, node2num_cycles, present_time):\n", - " offsets = {}\n", - " Sigtable = []\n", - " sim_start = present_time - 300\n", - " for node_id, group in sigtable.groupby('node_id'):\n", - " lsbs = group[group['start_unix'] < sim_start]['start_unix'].max() # the last start_unix before sim_start\n", - " offsets[node_id] = lsbs - sim_start\n", - " group = group[group.start_unix >= lsbs]\n", - " start_unixes = np.array(group.start_unix)\n", - " start_unixes = np.sort(np.unique(start_unixes))[:node2num_cycles[node_id]]\n", - " group = group[group.start_unix.isin(start_unixes)]\n", - " Sigtable.append(group)\n", - " Sigtable = pd.concat(Sigtable)\n", - " return Sigtable, offsets\n", - "Sigtable, offsets = set_timepoints(sigtable, node2num_cycles, present_time)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'c30': 4,\n", - " 'i0': 3,\n", - " 'i1': 4,\n", - " 'i2': 4,\n", - " 'i3': 4,\n", - " 'i6': 4,\n", - " 'i7': 4,\n", - " 'i8': 4,\n", - " 'i9': 4,\n", - " 'u00': 3,\n", - " 'u20': 4,\n", - " 'u30': 4,\n", - " 'u31': 4,\n", - " 'u32': 4,\n", - " 'u60': 4}" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "node2num_cycles" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 c30 4\n", - "2 i0 3\n", - "3 i1 4\n", - "4 i2 4\n", - "5 i3 4\n", - "6 i6 4\n", - "7 i7 4\n", - "8 i8 4\n", - "9 i9 4\n", - "10 u00 3\n", - "11 u20 4\n", - "12 u30 4\n", - "13 u31 4\n", - "14 u32 4\n", - "15 u60 4\n" - ] - }, - { - "data": { - "image/png": "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8GVf1GLfvn1mw4YNZsOGDUaSmTlzptmwYYPZvn272bdvn7njjjvMypUrzbZt28yHH35oBg4caHr16mUOHDgQWUdeXp4ZMGCAWb16tfn4449Nr169zLXXXhu5v7S01GRlZZnrr7/ebNy40bz22msmPT3dzJkzJxEvOWZ1bRtjjHnzzTdNixYtzNy5c83WrVvN008/bVJTU83y5csj67j55pvNCSecYBYtWmTWrVtnhg4daoYOHRq5/9ChQ6Zfv35m5MiRpqCgwCxcuNB07tzZTJ06tdlfb0PVt32MMaasrMykp6eb2bNn17oOr753zj//fHPaaaeZxYsXm6+//tq89NJLplWrVuaZZ56JrMPL753XX3/dLF682Hz11Vfmr3/9q+nevbu54oorotbh1u1zyy23mGAwaJYsWWIKCwsjfxUVFZE2Trz2r7/+2qSnp5s777zTfPHFF2bWrFkmNTXVLFy4sFlfb0PFsn0KCwvNhg0bzHPPPWckmWXLlpkNGzaY7777LtKmOfY9FChNdPg0rKP/xo8fbyoqKszIkSNN586dTYsWLUz37t3NjTfeGHXqmjHGfPfdd+baa681bdu2NYFAwPziF78w+/bti2rzySefmHPOOcf4/X7TtWtXM3369OZ8mY1S17Y57IUXXjAnn3yyadWqlenfv7/561//GrWO77//3tx6662mffv2Jj093Vx++eWmsLAwqs0333xjLrnkEtO6dWvTqVMn85vf/MZUVlY2x0tskli2z5w5c0zr1q1NaWlprevw6nunsLDQTJgwweTk5JhWrVqZ3r17myeeeMJUV1dH1uHl985TTz1lunXrZlq0aGFOOOEEc++99x5zarBbt09t20WSeemllyJtnHrtixcvNmeeeaZp2bKlOemkk6Kew1axbJ8HHnig3jbNse/x/dBhAAAAa/AdFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYB0KFAAAYvDII49o2LBhSk9PP+bXkI/H5/PV+vf4449H2uzZs0fjxo1TIBBQRkaGbrjhBpWXl9e6vi+//FLt2rWr8/lfe+01+Xw+jR07Nmr5m2++qZEjR6pjx47y+XwqKCiI6TUcbdasWTr11FPVunVr9e7dW3/84x8btZ76UKAAAPCDCy64QPPmzav1voMHD+rnP/+5brnllpjXV1hYGPX34osvyufz6corr4y0GTdunD7//HN98MEHevvtt7Vs2TLddNNNx6yrsrJS1157rc4999zjPt8333yjO+64o9Y2+/fv1znnnKMZM2bE3P+jzZ49W1OnTtWDDz6ozz//XA899JAmTpyot956q9HrPK7GXzAXAAB3Of/88+u9ZP1LL71kgsFgo9Y/ZswYc9FFF0Vub9q0yUgya9eujSx79913jc/nMzt37ox67G9/+1tz3XXXHff5Dx06ZIYNG2aef/55M378eDNmzJha+7Bt2zYjyWzYsOGY+/bu3WtuuOEG06lTJ9OuXTtz4YUXmoKCgsj9Q4cONXfccUfUY6ZMmWKGDx8ew6tvGI6gAADQDIqLi/XOO+/ohhtuiCxbuXKlMjIyNHjw4MiyESNGKCUlRatXr44sW7Rokd544w3NmjXruOt/+OGHlZmZGbX+hvr5z3+ukpISvfvuu8rPz9fAgQN18cUXa8+ePZKkcDisVq1aRT2mdevWWrNmTdQvFzuBAgUAgGbw8ssvq127drriiisiy4qKipSZmRnVLi0tTR06dFBRUZEk6bvvvtOECRM0b948BQKBWtf98ccf64UXXtBzzz3X6P59/PHHWrNmjd544w0NHjxYvXr10u9//3tlZGTof/7nfyRJo0aN0vPPP6/8/HwZY7Ru3To9//zzqqys1D//+c9GP3dtKFAAAJ716KOPqm3btpG/5cuX6+abb45atmPHDkee68UXX9S4ceOOOQJRnxtvvFH/9m//pvPOO6/W+/ft26frr79ezz33nDp16tTo/n3yyScqLy9Xx44do17/tm3b9NVXX0mS7rvvPl1yySX6yU9+ohYtWmjMmDEaP368JCklxdmSIs3RtQEAkERuvvlmXXXVVZHb48aN05VXXhl1lCMnJ6fJz7N8+XJt3rxZf/nLX6KWZ2dnq6SkJGrZoUOHtGfPHmVnZ0uqmd5ZsGCBfv/730uSjDGqrq5WWlqa5s6dq4EDB+qbb77RpZdeGllHdXW1pJqjMZs3b1bPnj3r7WN5ebm6dOmiJUuWHHPf4bOGWrdurRdffFFz5sxRcXGxunTporlz56pdu3bq3LlzzNsjFhQoAADP6tChgzp06BC53bp1a2VmZurkk0929HleeOEFDRo0SP37949aPnToUJWWlio/P1+DBg2SVFOQVFdXa8iQIZJqvqdSVVUVeczf/vY3zZgxQytWrFDXrl3VunVrffbZZ1Hrvffee7Vv3z499dRTys3NjamPAwcOVFFRkdLS0nTiiSfW2bZFixbq1q2bpJrTmn/2s59xBAUAgETYsWOH9uzZox07dqiqqipyHZGTTz5Zbdu2lST16dNH06ZN0+WXXx55XCgU0htvvKEnnnjimHWeeuqpysvL04033qhnn31WlZWVmjRpkq655prIkZtTTz016jHr1q1TSkqK+vXrF1l25L+lH494HLn8cN937dolSdq8ebOkmqM42dnZGjFihIYOHaqxY8fqscce0ymnnKJdu3bpnXfe0eWXX67Bgwdry5YtWrNmjYYMGaK9e/dq5syZ2rhxo15++eXGbNI68R0UAABicP/992vAgAF64IEHVF5ergEDBmjAgAFat25dpM3mzZtVVlYW9bjXXntNxhhde+21ta731VdfVZ8+fXTxxRfrX/7lX3TOOedo7ty5jvd/wYIFGjBggEaPHi1JuuaaazRgwAA9++yzkmouKvf3v/9d5513nn7xi1/olFNO0TXXXKPt27crKytLklRVVaUnnnhC/fv3109/+lMdOHBAK1asqPeIS2P4jDHG8bUCAAA0AUdQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdShQAACAdf5/efe8nZG0xXQAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "k = 0\n", - "for node_id, group in Sigtable.groupby('node_id'):\n", - " k += 1\n", - " print(k, node_id, node2num_cycles[node_id])\n", - " plt.plot(group.start_unix.unique(), [k] * len(group.start_unix.unique()), marker='o')\n", - " plt.axvline(present_time - 300, c='r', linewidth=.5)\n", - " plt.axvline(present_time, c='r', linewidth=.5)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
node_idstart_unixphase_sumodurationstatestart_dt
0i71704411320044GGrggGG2024-01-05 08:35:20
1i71704411320144rrrggrr2024-01-05 08:35:20
2i71704411320226GGrggGG2024-01-05 08:35:20
3i71704411320326rrrggrr2024-01-05 08:35:20
4c301704411350038rrrrrr2024-01-05 08:35:50
.....................
410u201704412330113ggrggg2024-01-05 08:52:10
411u201704412330238ggGggg2024-01-05 08:52:10
412u201704412330318ggrggg2024-01-05 08:52:10
413i91704412340026GGGG2024-01-05 08:52:20
414i91704412340164rrrr2024-01-05 08:52:20
\n", - "

415 rows × 6 columns

\n", - "
" - ], - "text/plain": [ - " node_id start_unix phase_sumo duration state start_dt\n", - "0 i7 1704411320 0 44 GGrggGG 2024-01-05 08:35:20\n", - "1 i7 1704411320 1 44 rrrggrr 2024-01-05 08:35:20\n", - "2 i7 1704411320 2 26 GGrggGG 2024-01-05 08:35:20\n", - "3 i7 1704411320 3 26 rrrggrr 2024-01-05 08:35:20\n", - "4 c30 1704411350 0 38 rrrrrr 2024-01-05 08:35:50\n", - ".. ... ... ... ... ... ...\n", - "410 u20 1704412330 1 13 ggrggg 2024-01-05 08:52:10\n", - "411 u20 1704412330 2 38 ggGggg 2024-01-05 08:52:10\n", - "412 u20 1704412330 3 18 ggrggg 2024-01-05 08:52:10\n", - "413 i9 1704412340 0 26 GGGG 2024-01-05 08:52:20\n", - "414 i9 1704412340 1 64 rrrr 2024-01-05 08:52:20\n", - "\n", - "[415 rows x 6 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
node_idstart_unixphase_sumodurationstatestart_dt
66c301704411509038rrrrrr2024-01-05 08:38:29
67c301704411509139GGGGGG2024-01-05 08:38:29
68c301704411509241GGGGGG2024-01-05 08:38:29
69c301704411509321GGGGGG2024-01-05 08:38:29
128c301704411670038rrrrrr2024-01-05 08:41:10
.....................
247u601704411970024ggggggggr2024-01-05 08:46:10
248u601704411970119ggggggggr2024-01-05 08:46:10
249u601704411970239ggggggggG2024-01-05 08:46:10
250u601704411970365ggggggggr2024-01-05 08:46:10
251u601704411970423ggggggggr2024-01-05 08:46:10
\n", - "

238 rows × 6 columns

\n", - "
" - ], - "text/plain": [ - " node_id start_unix phase_sumo duration state start_dt\n", - "66 c30 1704411509 0 38 rrrrrr 2024-01-05 08:38:29\n", - "67 c30 1704411509 1 39 GGGGGG 2024-01-05 08:38:29\n", - "68 c30 1704411509 2 41 GGGGGG 2024-01-05 08:38:29\n", - "69 c30 1704411509 3 21 GGGGGG 2024-01-05 08:38:29\n", - "128 c30 1704411670 0 38 rrrrrr 2024-01-05 08:41:10\n", - ".. ... ... ... ... ... ...\n", - "247 u60 1704411970 0 24 ggggggggr 2024-01-05 08:46:10\n", - "248 u60 1704411970 1 19 ggggggggr 2024-01-05 08:46:10\n", - "249 u60 1704411970 2 39 ggggggggG 2024-01-05 08:46:10\n", - "250 u60 1704411970 3 65 ggggggggr 2024-01-05 08:46:10\n", - "251 u60 1704411970 4 23 ggggggggr 2024-01-05 08:46:10\n", - "\n", - "[238 rows x 6 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "display(sigtable)\n", - "display(Sigtable)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def assign_red_yellow(Sigtable):\n", - " '''\n", - " 적색, 황색신호를 반영한 신호문자열 배정\n", - "\n", - " input : Sigtable\n", - " - 모든 교차로에 대한 (시작유닉스, 세부현시번호)별 현시시간, 신호문자열, 진입·진출엣지\n", - " * 세부현시란 오버랩을 반영한 현시번호를 뜻함.\n", - "\n", - " output : SIGTABLE\n", - " - 모든 교차로에 대한 (시작유닉스, 녹황적세부현시번호)별 현시시간, (황·적색신호가 포함된) 신호문자열\n", - " * 녹황적세부현시번호란 세부현시번호에 r, g, y 옵션까지 포함된 현시번호를 뜻함.\n", - " '''\n", - " SIGTABLE = []\n", - " for node_id, group in Sigtable.groupby('node_id'):\n", - " new_rows_list = []\n", - " for i in range(1, len(group)):\n", - " prev_row = group.iloc[i-1:i].copy()\n", - " next_row = group.iloc[i:i+1].copy()\n", - " new_rows = pd.concat([prev_row, prev_row, next_row]).reset_index(drop=True)\n", - " new_rows.loc[0, 'phase_sumo'] = str(prev_row.phase_sumo.iloc[0]) + '_g'\n", - " new_rows.loc[0, 'duration'] = new_rows.loc[0, 'duration'] - 5\n", - " new_rows.loc[1, 'phase_sumo'] = str(prev_row.phase_sumo.iloc[0]) + '_y'\n", - " new_rows.loc[1, 'duration'] = 4\n", - " yellow_state = ''\n", - " red_state = ''\n", - " for a, b in zip(prev_row.state.iloc[0], next_row.state.iloc[0]):\n", - " if a == 'G' and b == 'r':\n", - " yellow_state += 'y'\n", - " red_state += 'r'\n", - " else:\n", - " yellow_state += a\n", - " red_state += a\n", - " new_rows.loc[2, 'phase_sumo'] = str(next_row.phase_sumo.iloc[0]) + '__r'\n", - " new_rows.loc[2, 'duration'] = 1\n", - " new_rows.loc[1, 'state'] = yellow_state\n", - " new_rows.loc[2, 'state'] = red_state\n", - " new_rows_list.append(new_rows)\n", - " next_row['phase_sumo'] = str(next_row.phase_sumo.iloc[0]) + '_g'\n", - " next_row['duration'] -= 5\n", - " # next_row.loc['duration'] -= 5\n", - " new_rows_list.append(next_row)\n", - " new_rows = pd.concat(new_rows_list)\n", - " SIGTABLE.append(new_rows)\n", - " SIGTABLE = pd.concat(SIGTABLE).sort_values(by=['node_id', 'start_unix', 'phase_sumo']).reset_index(drop=True)\n", - " return SIGTABLE\n", - "SIGTABLE = assign_red_yellow(Sigtable)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1577804400\n", - "2145884400\n", - "4021\n", - "4021\n" - ] - } - ], - "source": [ - "max_unix, min_unix = int(datetime(2020, 1, 1).timestamp()), int(datetime(2038, 1, 1).timestamp())\n", - "print(max_unix)\n", - "print(min_unix)\n", - "history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", - "K = 0\n", - "for _, row in history.iterrows():\n", - " unixbool = min_unix <= row['end_unix'] <= max_unix\n", - " print(min_unix, row['end_unix'], max_unix)\n", - " if not unixbool:\n", - " K += 1\n", - "print(K)\n", - "print(len(history))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
node_idstart_unixphase_sumodurationstatestart_dt
100i117044117401__r1grrGGGrgrr2024-01-05 08:42:20
101i117044117401_g88grrGGGGgrr2024-01-05 08:42:20
102i117044117401_y4grryyyygrr2024-01-05 08:42:20
103i117044117402__r1grrrrrrgrr2024-01-05 08:42:20
104i117044117402_g35grrrrrrgGG2024-01-05 08:42:20
105i117044117402_y4grrrrrrgyy2024-01-05 08:42:20
106i117044119100__r1grrrrrrgrr2024-01-05 08:45:10
107i117044119100_g32gGGGGGrgrr2024-01-05 08:45:10
108i117044119100_y4gyyGGGrgrr2024-01-05 08:45:10
109i117044119101__r1grrGGGrgrr2024-01-05 08:45:10
110i117044119101_g88grrGGGGgrr2024-01-05 08:45:10
111i117044119101_y4grryyyygrr2024-01-05 08:45:10
112i117044119102__r1grrrrrrgrr2024-01-05 08:45:10
113i117044119102_g35grrrrrrgGG2024-01-05 08:45:10
114i117044119102_y4grrrrrrgyy2024-01-05 08:45:10
115i117044120800__r1grrrrrrgrr2024-01-05 08:48:00
116i117044120800_g32gGGGGGrgrr2024-01-05 08:48:00
117i117044120800_y4gyyGGGrgrr2024-01-05 08:48:00
118i117044120801__r1grrGGGrgrr2024-01-05 08:48:00
119i117044120801_g88grrGGGGgrr2024-01-05 08:48:00
\n", - "
" - ], - "text/plain": [ - " node_id start_unix phase_sumo duration state start_dt\n", - "100 i1 1704411740 1__r 1 grrGGGrgrr 2024-01-05 08:42:20\n", - "101 i1 1704411740 1_g 88 grrGGGGgrr 2024-01-05 08:42:20\n", - "102 i1 1704411740 1_y 4 grryyyygrr 2024-01-05 08:42:20\n", - "103 i1 1704411740 2__r 1 grrrrrrgrr 2024-01-05 08:42:20\n", - "104 i1 1704411740 2_g 35 grrrrrrgGG 2024-01-05 08:42:20\n", - "105 i1 1704411740 2_y 4 grrrrrrgyy 2024-01-05 08:42:20\n", - "106 i1 1704411910 0__r 1 grrrrrrgrr 2024-01-05 08:45:10\n", - "107 i1 1704411910 0_g 32 gGGGGGrgrr 2024-01-05 08:45:10\n", - "108 i1 1704411910 0_y 4 gyyGGGrgrr 2024-01-05 08:45:10\n", - "109 i1 1704411910 1__r 1 grrGGGrgrr 2024-01-05 08:45:10\n", - "110 i1 1704411910 1_g 88 grrGGGGgrr 2024-01-05 08:45:10\n", - "111 i1 1704411910 1_y 4 grryyyygrr 2024-01-05 08:45:10\n", - "112 i1 1704411910 2__r 1 grrrrrrgrr 2024-01-05 08:45:10\n", - "113 i1 1704411910 2_g 35 grrrrrrgGG 2024-01-05 08:45:10\n", - "114 i1 1704411910 2_y 4 grrrrrrgyy 2024-01-05 08:45:10\n", - "115 i1 1704412080 0__r 1 grrrrrrgrr 2024-01-05 08:48:00\n", - "116 i1 1704412080 0_g 32 gGGGGGrgrr 2024-01-05 08:48:00\n", - "117 i1 1704412080 0_y 4 gyyGGGrgrr 2024-01-05 08:48:00\n", - "118 i1 1704412080 1__r 1 grrGGGrgrr 2024-01-05 08:48:00\n", - "119 i1 1704412080 1_g 88 grrGGGGgrr 2024-01-05 08:48:00" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "SIGTABLE[100:120]" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def make_tl_file(SIGTABLE, offsets, present_time):\n", - " strings = ['\\n']\n", - " for node_id, group in SIGTABLE.groupby('node_id'):\n", - " strings.append(f' \\n')\n", - " for i, row in group.iterrows():\n", - " duration = row.duration\n", - " state = row.state\n", - " strings.append(f' \\n')\n", - " strings.append(' \\n')\n", - " strings.append('')\n", - " strings = ''.join(strings)\n", - " # 저장\n", - " path_output = f'../Results/sn_{present_time}.add.xml'\n", - " with open(path_output, 'w') as f:\n", - " f.write(strings)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def generate_signals(m):\n", - " midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - " next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - " fmins = range(midnight, next_day, 300)\n", - "\n", - " # 현재시각\n", - " present_time = fmins[m]\n", - " sim_start = fmins[m] - 300\n", - " \n", - " # network and dataframes\n", - " net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n", - " inter_node = pd.read_csv('../data/tables/inter_node.csv', index_col=0)\n", - " plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", - " match6 = pd.read_csv('../Intermediates/match6.csv', index_col=0)\n", - " match6 = match6[['node_id', 'phase_no', 'ring_type', 'inc_edge', 'out_edge']].reset_index(drop=True)\n", - " histid = pd.read_csv(f'../Intermediates/histid/histid_{present_time}.csv', index_col=0)\n", - " histid = histid.reset_index(drop=True).drop(columns=['inter_no'])\n", - " \n", - " # helper dictionaries and lists\n", - " inter_node_p = inter_node[inter_node.inter_type=='parent']\n", - " inter2node = dict(zip(inter_node_p['inter_no'], inter_node_p['node_id']))\n", - " node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", - " pa2ch = {'i0':['u00'], 'i1':[], 'i2':['u20'], 'i3':['c30', 'u30', 'u31', 'u32'], 'i6':['u60'], 'i7':[], 'i8':[], 'i9':[]}\n", - " node_ids = sorted(inter_node.node_id.unique())\n", - " parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n", - " nodes = [net.getNode(node_id) for node_id in node_ids]\n", - "\n", - " # histids\n", - " histids = attach_children(histid, match6, parent_ids, pa2ch)\n", - "\n", - " # node2init\n", - " node2init = initialize_states(net, nodes, histids)\n", - "\n", - " # sigtable\n", - " sigtable = assign_signals(histids, node2init, net)\n", - "\n", - " with open('../Intermediates/node2numcycles.json', 'r') as file:\n", - " node2numcycles = json.load(file)\n", - "\n", - " # Sigtable\n", - " Sigtable, offsets = set_timepoints(sigtable, node2num_cycles, present_time)\n", - "\n", - " # SIGTABLE\n", - " SIGTABLE = assign_red_yellow(Sigtable)\n", - "\n", - " make_tl_file(SIGTABLE, offsets, present_time)\n", - " print(f'A signal file (add.xml) has been created for the timeslot between {datetime.fromtimestamp(sim_start)} and {datetime.fromtimestamp(present_time)} ({sim_start} ~ {present_time})')" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "A signal file (add.xml) has been created for the timeslot between 2024-01-05 08:40:00 and 2024-01-05 08:45:00 (1704411600 ~ 1704411900)\n", - "A signal file (add.xml) has been created for the timeslot between 2024-01-05 08:45:00 and 2024-01-05 08:50:00 (1704411900 ~ 1704412200)\n" - ] - } - ], - "source": [ - "generate_signals(105)\n", - "generate_signals(106)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "rts", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Archives/Scripts/generate_signals_2.py b/Archives/Scripts/generate_signals_2.py deleted file mode 100644 index 56e01acc5..000000000 --- a/Archives/Scripts/generate_signals_2.py +++ /dev/null @@ -1,826 +0,0 @@ -# (siggen) PS C:\Github\snits_siggen> python .\Scripts\generate_signals.py -import pandas as pd -import numpy as np -import os, sys -import json -import copy -from tqdm import tqdm -import sumolib, traci -from datetime import datetime -import time - -class SignalGenerator(): - def __init__(self): - # 루트폴더 지정 - self.path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - with open(os.path.join(self.path_root, 'Scripts', 'config.json'), 'r') as config_file: - config = json.load(config_file) - # 주요 폴더 경로 지정 - self.paths = config['paths'] - self.path_data = os.path.join(self.path_root, *self.paths['data']) - self.path_intermediates = os.path.join(self.path_root, *self.paths['intermediates']) - self.path_results = os.path.join(self.path_root, *self.paths['results']) - self.path_tables = os.path.join(self.path_root, *self.paths['tables']) - self.path_networks = os.path.join(self.path_root, *self.paths['networks']) - self.path_scripts = os.path.join(self.path_root, *self.paths['scripts']) - # 이슈사항 목록 - self.issues = [] - - self.midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp()) - self.next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp()) - self.fsecs = range(self.midnight, self.next_day, 5) # fsecs : unix time by Five SECondS - self.fmins = range(self.midnight, self.next_day, 300) # fmins : unix time by Five MINuteS - - self.present_time = datetime.now().replace(month=1, day=5).timestamp() - self.present_time = max([fmin for fmin in list(self.fmins) if fmin <= self.present_time]) - - self.adder = 600 - - # 1. 데이터 준비 - def prepare_data(self): - print("1. 데이터를 준비합니다.") - self.load_networks() - self.load_tables() - # self.check_networks() - # self.check_tables() - self.prepare_auxiliaries() - - # 1-1. 네트워크 불러오기 - def load_networks(self): - self.net = sumolib.net.readNet(os.path.join(self.path_networks, 'sn.net.xml')) - print("1-1. 네트워크가 로드되었습니다.") - - # 1-2. 테이블 불러오기 - def load_tables(self): - # 모든 컬럼에 대하여 데이터타입 지정 - loading_dtype = { - 'inter_no':'int', 'start_hour':'int', 'start_minute':'int', 'cycle':'int','offset':'int', - 'node_id':'str', 'inter_type':'str', 'parent_id':'str','child_id':'str', - 'direction':'str', 'condition':'str', 'inc_edge':'str', 'out_edge':'str', - 'end_unix':'int', 'inter_name':'str', 'inter_lat':'float', 'inter_lon':'float', - 'group_no':'int', 'main_phase_no':'int', 'phase_no':'int','ring_type':'str' - } - for alph in ['A', 'B']: - for j in range(1,9): - loading_dtype[f'angle_{alph}{j}'] = 'str' - loading_dtype[f'dura_{alph}{j}'] = 'int' - - # 테이블 불러오기 - self.inter_info = pd.read_csv(os.path.join(self.path_tables, 'inter_info.csv'), dtype=loading_dtype) - self.plan = pd.read_csv(os.path.join(self.path_tables, 'plan.csv'), dtype=loading_dtype) - self.history = pd.read_csv(os.path.join(self.path_tables, 'history.csv'), dtype=loading_dtype) - self.inter_node = pd.read_csv(os.path.join(self.path_tables, 'inter_node.csv'), dtype=loading_dtype) - self.matching = pd.read_csv(os.path.join(self.path_intermediates, 'matching.csv'), dtype=loading_dtype) - self.match1 = pd.read_csv(os.path.join(self.path_intermediates, 'match1.csv'), dtype=loading_dtype) - self.match6 = pd.read_csv(os.path.join(self.path_intermediates, 'match6.csv'), dtype=loading_dtype) - self.match6 = self.match6[['node_id', 'phase_no', 'ring_type', 'inc_edge', 'out_edge']].reset_index(drop=True) - - # 교차로목록 정의 - self.inter_nos = sorted(self.inter_info.inter_no.unique()) - print("1-2. 테이블들이 로드되었습니다.") - - # 1-3. 네트워크 무결성 검사 - def check_networks(self): - # https://sumo.dlr.de/docs/Netedit/neteditUsageExamples.html#simplify_tls_program_state_after_changing_connections - if 'SUMO_HOME' in os.environ: - tools = os.path.join(os.environ['SUMO_HOME'], 'tools') - if tools not in sys.path: - sys.path.append(tools) - else: - raise EnvironmentError("please declare environment variable 'SUMO_HOME'") - traci.start([sumolib.checkBinary('sumo'), "-n", os.path.join(self.path_networks, 'sn.net.xml')]) - nodes = [node for node in self.net.getNodes() if node.getType()=='traffic_light'] - for node in nodes: - node_id = node.getID() - from_xml = len([c for c in node.getConnections() if c.getTLLinkIndex() >= 0]) - from_traci = len(traci.trafficlight.getRedYellowGreenState(node_id)) - if from_xml != from_traci: - sub = {'id': node_id, 'type': 'node', 'note': '유효하지 않은 연결이있음. netedit에서 clean states 필요.'} - self.issues.append(sub) - traci.close() - print("1-3. 네트워크의 모든 clean state requirement들을 체크했습니다.") - - # 1-4. 테이블 무결성 검사 - def check_tables(self): - self.check_history() - # 교차로정보, 방위각정보, 신호계획에 대해서는 preprocess_daily.py에서 - # 무결성검사를 완료했으므로 여기에서는 따로 검사하지 않음. - # self.check_moves() # 이동류번호에 대한 무결성검사 필요하나 아직 작성하지 않음. (24. 2. 5 화) - print("1-4. 테이블들의 무결성 검사를 완료했습니다.") - - # 1-4-1. 신호이력(history) 검사 - def check_history(self): - # 1-4-1-1. inter_no 검사 - # self.history.loc[0, 'inter_no'] = '4' # 에러 발생을 위한 코드 - missing_inter_nos = set(self.history.inter_no) - set(self.inter_nos) - if missing_inter_nos: - msg = f"1-4-1-1. history의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" - self.issues.append(msg) - - # 1-4-1-2. 종료유닉스 검사 - # self.history.loc[0, 'end_unix'] = 38.0 # 에러 발생을 위한 코드 - self.min_unix, self.max_unix = int(datetime(2020, 1, 1).timestamp()), int(datetime(2038, 1, 1).timestamp()) - for _, row in self.history.iterrows(): - unixbool = self.min_unix <= row['end_unix'] <= self.max_unix - if not unixbool: - msg = f"1-4-1-2. 적정 범위를 벗어난 유닉스시각(end_unix)이 존재합니다 : inter_no : {row['inter_no']}" - self.issues.append(msg) - - # 1-4-1-3. 현시시간 검사 - # self.history.loc[0, 'dura_A1'] = -2 # 에러 발생을 위한 코드 - durations = self.history[[f'dura_{alph}{j}' for alph in ['A','B'] for j in range(1, 9)]] - valid_indices = ((durations >= 0) & (durations <= 200)).all(axis=1) - invalid_inter_nos = sorted(self.history[~ valid_indices].inter_no.unique()) - if invalid_inter_nos: - msg = f"1-4-1-3. 음수이거나 200보다 큰 현시시간이 존재합니다. : {invalid_inter_nos}" - - # 1-5. 보조 딕셔너리, 데이터프레임, 리스트 등 만들기 - def prepare_auxiliaries(self): - # inter2node : a dictionary that maps inter_no to the node_id - inter_node_p = self.inter_node[self.inter_node.inter_type=='parent'] - self.inter2node = dict(zip(inter_node_p['inter_no'], inter_node_p['node_id'])) - self.node2inter = dict(zip(self.inter_node['node_id'], self.inter_node['inter_no'])) - - # hours : 정각에 해당하는 시각들 목록 - self.hours = np.array(range(self.midnight - 7200, self.next_day + 1, 3600)) - - # split, isplit : A,B 분리 혹은 통합시 사용될 수 있는 딕셔너리 - self.splits = {} # splits maps (inter_no, start_hour, start_minute) to split - for i, row in self.plan.iterrows(): - inter_no = row.inter_no - start_hour = row.start_hour - start_minute = row.start_minute - cycle = row.cycle - cums_A = row[[f'dura_A{j}' for j in range(1,9)]].cumsum() - cums_B = row[[f'dura_B{j}' for j in range(1,9)]].cumsum() - self.splits[(inter_no, start_hour, start_minute)] = {} # split maps (phas_A, phas_B) to k - k = 0 - for t in range(cycle): - new_phas_A = len(cums_A[cums_A < t]) + 1 - new_phas_B = len(cums_B[cums_B < t]) + 1 - if k == 0 or ((new_phas_A, new_phas_B) != (phas_A, phas_B)): - k += 1 - phas_A = new_phas_A - phas_B = new_phas_B - self.splits[(inter_no, start_hour, start_minute)][(phas_A, phas_B)] = k - self.isplits = {} # the inverse of splits - for i in self.splits: - self.isplits[i] = {self.splits[i][k]:k for k in self.splits[i]} # isplit maps k to (phas_A, phas_B) - - # timetable : 교차로별 프로그램 시작시각 - self.timetable = self.plan[['start_hour', 'start_minute']].drop_duplicates() - self.timetable['start_seconds'] = self.midnight + self.timetable['start_hour'] * 3600 + self.timetable['start_minute'] * 60 - - # A dictionary that maps parent_id to a list of child_ids - self.pa2ch = {'i0':['u00'], 'i1':[], 'i2':['u20'], 'i3':['c30', 'u30', 'u31', 'u32'], 'i6':['u60'], 'i7':[], 'i8':[], 'i9':[]} - self.node_ids = sorted(self.inter_node.node_id.unique()) - self.parent_ids = sorted(self.inter_node[self.inter_node.inter_type=='parent'].node_id.unique()) - self.nodes = [self.net.getNode(node_id) for node_id in self.node_ids] - - # node2num_cycles : A dictionary that maps a node_id to the number of cycles - with open(os.path.join(self.path_intermediates, 'node2num_cycles.json'), 'r') as file: - # json.load() 함수를 사용해 파일 내용을 Python 딕셔너리로 불러옵니다. - self.node2num_cycles = json.load(file) - - # 2. 신호이력 전처리 - def process_history(self): - print("2. 신호이력 테이블을 변환합니다.") - self.make_rhistory() - self.time21 = datetime.now() - self.make_rhists() - self.time22 = datetime.now() - self.make_hrhists() - self.time23 = datetime.now() - - # 2-1. rhistory - def make_rhistory(self): - # 1. 조회시점의 유닉스 타임 이전의 신호이력 수집 - self.rhistory = self.history.copy() # recent history - self.rhistory = self.rhistory[(self.rhistory.end_unix <= self.present_time) & (self.rhistory.end_unix > self.present_time - 9000)] # 두 시간 반 전부터 현재까지의 신호이력을 가져옴. 9000 = 3600 * 2.5 - - # rhistory에 모든 교차로번호가 존재하지 않으면 해당 교차로번호에 대한 신호이력을 추가함 (at 최근 프로그램 시작시각) - whole_inter_nos = sorted(self.history.inter_no.unique()) - recent_inter_nos = sorted(self.rhistory.inter_no.unique()) - if not whole_inter_nos==recent_inter_nos: - for inter_no in set(whole_inter_nos) - set(recent_inter_nos): - program_start, prow = self.load_prow(inter_no, self.present_time - 9000) - cycle = prow.cycle.iloc[0] - row1 = prow.drop(['start_hour', 'start_minute'], axis=1).copy() - row2 = prow.drop(['start_hour', 'start_minute'], axis=1).copy() - # prow에서 필요한 부분을 rhistory에 추가 - row1['end_unix'] = program_start - row2['end_unix'] = program_start + cycle - self.rhistory = pd.concat([self.rhistory, row1, row2]).reset_index(drop=True) - # present_time + adder 의 시각에 한 주기의 신호 추가 - for inter_no in set(whole_inter_nos): - program_start, prow = self.load_prow(inter_no, self.present_time) - cycle = prow.cycle.iloc[0] - row3 = prow.drop(['start_hour', 'start_minute'], axis=1).copy() - # prow에서 필요한 부분을 rhistory에 추가 - row3['end_unix'] = self.present_time + self.adder - self.rhistory = pd.concat([self.rhistory, row3]).reset_index(drop=True) - - # 2. 시작 유닉스 타임컬럼 생성 후 종류 유닉스 타임에서 현시별 현시기간 컬럼의 합을 뺀 값으로 입력 - # - 현시시간의 합을 뺀 시간의 +- 10초 이내에 이전 주기정보가 존재하면 그 유닉스 시간을 시작 유닉스시간 값으로 하고, 존재하지 않으면 현시시간의 합을 뺀 유닉스 시간을 시작 유닉스 시간으로 지정 - for i, row in self.rhistory.iterrows(): - inter_no = row.inter_no - end_unix = row.end_unix - elapsed_time = row[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]].sum() // 2 # 현시시간 합 - # 이전 유닉스 존재하지 않음 : 현시시간 합의 차 - start_unix = end_unix - elapsed_time - pre_rows = self.history[:i] # previous rows - if inter_no in pre_rows.inter_no.unique(): # 이전 유닉스 존재 - pre_unix = pre_rows[pre_rows.inter_no == inter_no]['end_unix'].iloc[-1] # previous unix time - # 이전 유닉스 존재, abs < 10 : 이전 유닉스 - if abs(pre_unix - start_unix) < 10: - start_unix = pre_unix - # 이전 유닉스 존재, abs >=10 : 현시시간 합의 차 - else: - pass - self.rhistory.loc[i, 'start_unix'] = start_unix - self.rhistory[self.rhistory.isna()] = 0 - self.rhistory['start_unix'] = self.rhistory['start_unix'].astype(int) - self.rhistory = self.rhistory[['inter_no', 'start_unix'] + [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)] + ['cycle']] - - def load_prow(self, inter_no, time): - ''' - load planned row - ''' - # 프로그램 시작시각 - program_starts = np.array(self.timetable.start_seconds) - idx = (program_starts <= time).sum() - 1 - program_start = program_starts[idx] - - # 최근 프로그램 시작시각에 대한 신호계획 - start_hour = self.timetable.iloc[idx].start_hour - start_minute = self.timetable.iloc[idx].start_minute - prow = self.plan[(self.plan.inter_no==inter_no) & (self.plan.start_hour==start_hour) & (self.plan.start_minute==start_minute)] # planned row - return program_start, prow - - # 2-2. rhists - def make_rhists(self): - self.rhists = [] - for inter_no in sorted(self.rhistory.inter_no.unique()): - filtered_rhist = self.rhistory[self.rhistory.inter_no == inter_no].drop_duplicates(subset=['start_unix']).reset_index(drop=True) - self.rhist = filtered_rhist - - # D_n 및 S_n 값 정의 - self.rhist['D_n'] = 0 # D_n : 시간차이 - self.rhist['S_n'] = 0 # S_n : 현시시간합 - for n in range(len(self.rhist)): - curr_unix = self.rhist.iloc[n].start_unix # current start_unix - self.rhist.loc[n, ['D_n', 'S_n']] = self.calculate_DS(self.rhist, curr_unix) - - # 이전시각, 현재시각 - prev_unix = self.rhist.loc[0, 'start_unix'] # previous start_unix - curr_unix = self.rhist.loc[1, 'start_unix'] # current start_unix - - # rhist의 마지막 행에 도달할 때까지 반복 - while True: - n = self.rhist[self.rhist.start_unix==curr_unix].index[0] - cycle = self.rhist.loc[n, 'cycle'] - D_n = self.rhist.loc[n, 'D_n'] - S_n = self.rhist.loc[n, 'S_n'] - # 참값인 경우 - if (abs(D_n - S_n) <= 5): - pass - # 참값이 아닌 경우 - else: - # 2-1-1. 결측치 처리 : 인접한 두 start_unix의 차이가 계획된 주기의 두 배보다 크면 결측이 일어났다고 판단, 신호계획의 현시시간으로 "대체" - if curr_unix - prev_unix >= 2 * cycle: - # prev_unix를 계획된 주기만큼 늘려가면서 한 행씩 채워나간다. - # (curr_unix와의 차이가 계획된 주기보다 작거나 같아질 때까지) - while curr_unix - prev_unix > cycle: - prev_unix += cycle - # 신호 계획(prow) 불러오기 - start_seconds = np.array(self.timetable.start_seconds) - idx = (start_seconds <= prev_unix).sum() - 1 - start_hour = self.timetable.iloc[idx].start_hour - start_minute = self.timetable.iloc[idx].start_minute - prow = self.plan.copy()[(self.plan.inter_no==inter_no) & (self.plan.start_hour==start_hour) & (self.plan.start_minute==start_minute)] # planned row - # prow에서 필요한 부분을 rhist에 추가 - prow['start_unix'] = prev_unix - prow = prow.drop(['start_hour', 'start_minute', 'offset'], axis=1) - cycle = prow.iloc[0].cycle - self.rhist = pd.concat([self.rhist, prow]) - self.rhist = self.rhist.sort_values(by='start_unix').reset_index(drop=True) - n += 1 - - # 2-1-2. 이상치 처리 : 비율에 따라 해당 행을 "삭제"(R_n <= 0.5) 또는 "조정"(R_n > 0.5)한다 - R_n = (curr_unix - prev_unix) / cycle # R_n : 비율 - # R_n이 0.5보다 작거나 같으면 해당 행을 삭제 - if R_n <= 0.5: - self.rhist = self.rhist.drop(index=n).reset_index(drop=True) - if n >= self.rhist.index[-1]: - break - # 행삭제에 따른 curr_unix, R_n 재정의 - curr_unix = self.rhist.loc[n, 'start_unix'] - R_n = (curr_unix - prev_unix) / cycle # R_n : 비율 - - # R_n이 0.5보다 크면 해당 행 조정 (비율을 유지한 채로 현시시간 대체) - if R_n > 0.5: - # 신호 계획(prow) 불러오기 - start_seconds = np.array(self.timetable.start_seconds) - idx = (start_seconds <= curr_unix).sum() - 1 - start_hour = self.timetable.iloc[idx].start_hour - start_minute = self.timetable.iloc[idx].start_minute - prow = self.plan[(self.plan.inter_no==inter_no) & (self.plan.start_hour==start_hour) & (self.plan.start_minute==start_minute)] # planned row - # 조정된 현시시간 (prow에 R_n을 곱하고 정수로 바꿈) - adjusted_dur = prow.copy()[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] * R_n - int_parts = adjusted_dur.iloc[0].apply(lambda x: int(x)) - frac_parts = adjusted_dur.iloc[0] - int_parts - difference = round(adjusted_dur.iloc[0].sum()) - int_parts.sum() - for _ in range(difference): # 소수 부분이 가장 큰 상위 'difference'개의 값에 대해 올림 처리 - max_frac_index = frac_parts.idxmax() - int_parts[max_frac_index] += 1 - frac_parts[max_frac_index] = 0 # 이미 처리된 항목은 0으로 설정 - # rhist에 조정된 현시시간을 반영 - self.rhist.loc[n, [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] = int_parts.values - self.rhist.loc[n, 'cycle'] = int_parts.sum().sum() // 2 - - if n >= self.rhist.index[-1]: - break - prev_unix = curr_unix - curr_unix = self.rhist.loc[n+1, 'start_unix'] - - self.rhists.append(self.rhist) - self.rhists = pd.concat(self.rhists).sort_values(by=['start_unix','inter_no']) - self.rhists = self.rhists[self.rhists.start_unix >= self.present_time - 3600] - self.rhists = self.rhists.drop(columns=['D_n', 'S_n']) - - def calculate_DS(self, rhist, curr_unix): - program_starts = np.array(self.timetable.start_seconds) - idx = (program_starts <= self.present_time).sum() - 1 - program_start = program_starts[idx] - if list(self.hours[self.hours <= curr_unix]): - ghour_lt_curr_unix = self.hours[self.hours <= curr_unix].max() # the greatest hour less than or equal to curr_unix - else: - ghour_lt_curr_unix = program_start - start_unixes = rhist.start_unix.unique() - start_unixes_lt_ghour = np.sort(start_unixes[start_unixes < ghour_lt_curr_unix]) # start unixes less than ghour_lt_curr_unix - # 기준유닉스(base_unix) : curr_unix보다 작은 hour 중에서 가장 큰 값으로부터 다섯 번째로 작은 start_unix - if len(start_unixes_lt_ghour) > 5: - base_unix = start_unixes_lt_ghour[-5] - # start_unixes_lt_ghour의 길이가 5 미만일 경우에는 맨 앞 start_unix로 base_unix를 지정 - else: - base_unix = rhist.start_unix.min() - D_n = curr_unix - base_unix - S_n_durs = rhist[(rhist.start_unix > base_unix) & (rhist.start_unix <= curr_unix)] \ - [[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] - S_n = S_n_durs.values.sum() // 2 - return D_n, S_n - - # 2-2. hrhists - def make_hrhists(self): - # 계층화된 형태로 변환 - self.hrhists = [] # hierarchied recent history - for i, row in self.rhists.iterrows(): - inter_no = row.inter_no - start_unix = row.start_unix - - ind = (self.timetable['start_seconds'] <= row.start_unix).sum() - 1 - start_hour = self.timetable.iloc[ind].start_hour - start_minute = self.timetable.iloc[ind].start_minute - self.isplit = self.isplits[(inter_no, start_hour, start_minute)] - phas_As = [self.isplit[j][0] for j in self.isplit.keys()] - phas_Bs = [self.isplit[j][1] for j in self.isplit.keys()] - durs_A = row[[f'dura_A{j}' for j in range(1,9)]] - durs_B = row[[f'dura_B{j}' for j in range(1,9)]] - durations = [] - for j in range(1, len(self.isplit)+1): - ja = self.isplit[j][0] - jb = self.isplit[j][1] - if ja == jb: - durations.append(min(durs_A[ja-1], durs_B[jb-1])) - else: - durations.append(abs(durs_A[ja-1] - durs_B[ja-1])) - new_rows = pd.DataFrame({'inter_no':[inter_no] * len(durations), 'start_unix':[start_unix] * len(durations), - 'phas_A':phas_As, 'phas_B':phas_Bs, 'duration':durations}) - self.hrhists.append(new_rows) - self.hrhists = pd.concat(self.hrhists) - self.hrhists = self.hrhists.sort_values(by = ['start_unix', 'inter_no', 'phas_A', 'phas_B']).reset_index(drop=True) - - # 3. 이동류정보 전처리 - def process_movement(self): - print("3. 이동류정보 테이블을 변환합니다.") - self.make_movement() - self.update_movement() - - # 3-1. movement - def make_movement(self): - # - 아래 절차를 5초마다 반복 - for fsec in range(self.present_time - 300, self.present_time + 1, 5): # fsec : unix time by Five SECond - # 1. 상태 테이블 조회해서 전체 데이터중 필요데이터(교차로번호, A링 현시번호, A링 이동류번호, B링 현시번호, B링 이동류번호)만 수집 : A - # move = time2move[fsec] - move = pd.read_csv(os.path.join(self.path_tables, 'move', f'move_{fsec}.csv'), index_col=0) - # 2. 이력 테이블 조회해서 교차로별로 유닉스시간 최대인 데이터(교차로변호, 종료유닉스타임)만 수집 : B - recent_histories = [group.iloc[-1:] for _, group in self.history[self.history['end_unix'] < fsec].groupby('inter_no')] # 교차로별로 유닉스시간이 최대인 행들 - if not recent_histories: - rhistory = pd.DataFrame({'inter_no':[], 'end_unix':[]}) # recent history - else: - rhistory = pd.concat(recent_histories) - recent_unix = rhistory[['inter_no', 'end_unix']] - # 3. 상태 테이블 조회정보(A)와 이력 테이블 조회정보(B) 조인(키값 : 교차로번호) : C - move = pd.merge(move, recent_unix, how='left', on='inter_no') - move['end_unix'] = move['end_unix'].fillna(0).astype(int) - move = move.drop_duplicates() - # 4. C데이터 프레임에 신규 컬럼(시작 유닉스타임) 생성 후 종료유닉스 타임 값 입력, 종료 유닉스 타임 컬럼 제거 - move = move.rename(columns = {'end_unix':'start_unix'}) - # 5. 이동류 이력정보 READ - # - CSV 파일로 서버에 저장된 이동류정보를 읽어옴(파일이 없는 경우에는 데이터가 없는 프레임 D 생성) - try: - if isinstance(movement, pd.DataFrame): # movement가 존재할 경우 그걸 그대로 씀. - pass - else: - movement = pd.DataFrame() - except NameError: # movement가 존재하지 않는 경우 생성 - movement = pd.DataFrame() - # 6. 이동류 이력정보 데이터테이블(D)에 C데이터 add - movement = pd.concat([movement, move]) - # 7. D데이터 프레임에서 중복데이터 제거(교차로번호, 시작 유닉스타임, A링 현시번호, B링 현시번호 같은 행은 제거) - movement = movement.drop_duplicates(['inter_no','phas_A','phas_B','start_unix']) - # 8. D데이터 보관 시간 기준시간을 시작 유닉스 타임의 최대값 - 3600을 값으로 산출하고, 보관 시간 기준시간보다 작은 시작 유닉스 타임을 가진 행은 모두 제거(1시간 데이터만 보관) - movement = movement[movement.start_unix > fsec - 3600] - movement = movement.sort_values(by=['start_unix','inter_no','phas_A','phas_B']).reset_index(drop=True) - self.movement = pd.read_csv(os.path.join(self.path_intermediates, 'movement', f'movement_{self.present_time}.csv'), index_col=0) - - # 3-2. movement_updated - def update_movement(self): - # 중복을 제거하고 (inter_no, start_unix) 쌍을 만듭니다. - hrhists_inter_unix = set(self.hrhists[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None)) - movement_inter_unix = set(self.movement[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None)) - - # hrhists에는 있지만 movement에는 없는 (inter_no, start_unix) 쌍을 찾습니다. - missing_in_movement = hrhists_inter_unix - movement_inter_unix - - # 새로운 행들을 생성합니다. - new_rows = [] - if missing_in_movement: - for inter_no, start_unix in missing_in_movement: - # match1에서 해당 inter_no의 데이터를 찾습니다. - new_row = self.match1[self.match1['inter_no'] == inter_no].copy() - # start_unix 값을 설정합니다. - new_row['start_unix'] = start_unix - new_rows.append(new_row) - - # 새로운 데이터프레임을 생성하고 기존 movement 데이터프레임과 합칩니다. - new_movement = pd.concat(new_rows, ignore_index=True) - self.movement_updated = pd.concat([self.movement, new_movement], ignore_index=True) - else: - self.movement_updated = self.movement - - # 4. 통합테이블 생성 - def make_histids(self): - print("4. 통합 테이블을 생성합니다.") - self.merge_dfs() - self.time41 = datetime.now() - self.attach_children() - self.time42 = datetime.now() - - # 4-1. histid - def merge_dfs(self): - # movements and durations - movedur = pd.merge(self.hrhists, self.movement_updated, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B']) - movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B']) - movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']] - - # matching DataFrame에 대해 multi-index 설정 - self.matching.set_index(['inter_no', 'move_no'], inplace=True) - self.matching.sort_index(inplace=True) - - for row in movedur.itertuples(index=True): - inter_no = row.inter_no - start_unix = row.start_unix - move_A = row.move_A - move_B = row.move_B - - # incoming and outgoing edges A - if move_A in [17, 18]: - inc_edge_A = np.nan - out_edge_A = np.nan - else: - match_A = self.matching.loc[(inter_no, move_A)] - inc_edge_A = match_A.inc_edge.values[0] - out_edge_A = match_A.out_edge.values[0] - movedur.at[row.Index, 'inc_edge_A'] = inc_edge_A - movedur.at[row.Index, 'out_edge_A'] = out_edge_A - - # incoming and outgoing edges B - if move_B in [17, 18]: - inc_edge_B = np.nan - out_edge_B = np.nan - else: - match_B = self.matching.loc[(inter_no, move_B)] - inc_edge_B = match_B.inc_edge.values[0] - out_edge_B = match_B.out_edge.values[0] - movedur.at[row.Index, 'inc_edge_B'] = inc_edge_B - movedur.at[row.Index, 'out_edge_B'] = out_edge_B - - # 이동류 컬럼 제거 - movedur = movedur.drop(['move_A', 'move_B'], axis=1) - - self.histid = movedur.copy() # history with edge ids (incoming and outgoing edge ids) - self.histid['node_id'] = self.histid['inter_no'].map(self.inter2node) - self.histid = self.histid[['inter_no', 'node_id', 'start_unix', 'phas_A', 'phas_B', 'duration', 'inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']] - histid_start = self.present_time - 600 - self.histid = self.histid[self.histid.start_unix > histid_start] - - # 4-2. histids - def attach_children(self): - ''' - 자식교차로에 대한 진입·진출 엣지 정보를 붙여주는 함수 - - input : - (1) histid - - 각 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지 - - 부모교차로(주교차로)에 대해서만 값이 지정되어 있음 - (2) match6 - - (현시, 링)별 진입·진출엣지 - - 자식교차로(유턴 및 연동교차로)에 대해서도 값이 지정되어 있음 - (3) parent_ids : 부모교차로 목록 - (4) pa2ch : 각 부모교차로id를 부모교차로가 포함하고 있는 자식교차로들의 id들의 리스트로 대응시키는 딕셔너리 - - output : histids - - 모든(부모 및 자식) 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지 - ''' - new_histids = [] - for parent_id in self.parent_ids: - for child_id in self.pa2ch[parent_id]: - new_histid = self.histid.copy()[self.histid.node_id==parent_id] - new_histid[['inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']] = np.nan - for row in new_histid.itertuples(index=True): - phas_A = row.phas_A - phas_B = row.phas_B - new_match = self.match6[self.match6.node_id==child_id] - Arow = new_match[(new_match.phase_no==phas_A) & (new_match.ring_type=='A')] - if not Arow[['inc_edge', 'out_edge']].isna().all().all(): - inc_edge = Arow.iloc[0].inc_edge - out_edge = Arow.iloc[0].out_edge - new_histid.loc[row.Index, ['inc_edge_A', 'out_edge_A']] = [inc_edge, out_edge] - Brow = new_match[(new_match.phase_no==phas_B) & (new_match.ring_type=='B')] - if not Brow[['inc_edge', 'out_edge']].isna().all().all(): - inc_edge = Brow.iloc[0].inc_edge - out_edge = Brow.iloc[0].out_edge - new_histid.loc[row.Index, ['inc_edge_B', 'out_edge_B']] = [inc_edge, out_edge] - new_histid.loc[row.Index, 'node_id'] = child_id - new_histids.append(new_histid) - new_histids = pd.concat(new_histids) - self.histids = pd.concat([self.histid.copy(), new_histids]) - self.histids = self.histids.sort_values(by=['start_unix', 'node_id', 'phas_A', 'phas_B']).reset_index(drop=True) - - # 5. 신호 생성 - def get_signals(self): - print("5. 신호를 생성합니다.") - self.initialize_states() - self.assign_signals() - self.set_timepoints() - self.assign_red_yellow() - self.make_tl_file() - - # 5-1. 신호초기화 - def initialize_states(self): - ''' - 신호 초기화 - - input : - (1) net : 네트워크 - (2) nodes : 노드 목록 - (3) histids : 모든 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지 - - output : node2init - - 각 노드를 초기화된 신호로 맵핑하는 딕셔너리 - - 초기화된 신호란, 우회전을 g로 나머지는 r로 지정한 신호를 말함. - ''' - self.node2init = {} - for node in self.nodes: - node_id = node.getID() - conns = [(c.getJunctionIndex(), c) for c in node.getConnections()] - conns = [c for c in conns if c[0] >= 0] - conns = sorted(conns, key=lambda x: x[0]) - state = [] - for i, ci in conns: - if ci.getTLLinkIndex() < 0: - continue - are_foes = False - for j, cj in conns: - if ci.getTo() == cj.getTo(): - continue - if node.areFoes(i, j): - are_foes = True - break - state.append('r' if are_foes else 'g') - self.node2init[node_id] = state - - # 어떤 연결과도 상충이 일어나지는 않지만, 신호가 부여되어 있는 경우에는 r을 부여 - for _, row in self.histids.iterrows(): - node_id = row['node_id'] - inc_edge_A = row.inc_edge_A - inc_edge_B = row.inc_edge_B - out_edge_A = row.out_edge_A - out_edge_B = row.out_edge_B - - if pd.isna(inc_edge_A) or pd.isna(out_edge_A): - pass - else: - inc_edge_A = self.net.getEdge(inc_edge_A) - out_edge_A = self.net.getEdge(out_edge_A) - for conn in inc_edge_A.getConnections(out_edge_A): - index = conn.getTLLinkIndex() - if index >= 0: - self.node2init[node_id][index] = 'r' - - if pd.isna(inc_edge_B) or pd.isna(out_edge_B): - pass - else: - inc_edge_B = self.net.getEdge(inc_edge_B) - out_edge_B = self.net.getEdge(out_edge_B) - for conn in inc_edge_B.getConnections(out_edge_B): - index = conn.getTLLinkIndex() - if index >= 0: - self.node2init[node_id][index] = 'r' - - # 5-2. 녹색신호 부여 - def assign_signals(self): - ''' - 진입·진출엣지를 신호문자열로 배정 - - input : - (1) histids : 모든 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지 - (2) node2init : 각 노드를 초기화된 신호로 맵핑하는 딕셔너리 - (3) net : 네트워크 - - output : sigtable - - 모든 교차로에 대한 (시작유닉스, A현시, B현시)별 현시시간, 신호문자열 - - 황색 및 적색신호는 아직 반영되지 않았음. - ''' - self.sigtable = self.histids.copy() - self.sigtable['init_state'] = self.sigtable['node_id'].map(self.node2init) - self.sigtable['state'] = self.sigtable['init_state'].map(lambda x:''.join(x)) - for i, row in self.sigtable.iterrows(): - node_id = row.node_id - inc_edge_A = row.inc_edge_A - inc_edge_B = row.inc_edge_B - out_edge_A = row.out_edge_A - out_edge_B = row.out_edge_B - state = copy.deepcopy(self.node2init)[node_id] - if pd.isna(inc_edge_A) or pd.isna(out_edge_A): - pass - else: - inc_edge_A = self.net.getEdge(inc_edge_A) - out_edge_A = self.net.getEdge(out_edge_A) - for conn in inc_edge_A.getConnections(out_edge_A): - index = conn.getTLLinkIndex() - if index >= 0: - state[index] = 'G' - self.sigtable.at[i, 'state'] = ''.join(state) - - if pd.isna(inc_edge_B) or pd.isna(out_edge_B): - pass - else: - inc_edge_B = self.net.getEdge(inc_edge_B) - out_edge_B = self.net.getEdge(out_edge_B) - for conn in inc_edge_B.getConnections(out_edge_B): - index = conn.getTLLinkIndex() - if index >= 0: - state[index] = 'G' - self.sigtable.at[i, 'state'] = ''.join(state) - self.sigtable = self.sigtable.dropna(subset='state') - self.sigtable = self.sigtable.reset_index(drop=True) - self.sigtable['phase_sumo'] = self.sigtable.groupby(['node_id', 'start_unix']).cumcount() - self.sigtable = self.sigtable[['node_id', 'start_unix', 'phase_sumo', 'duration', 'state']] - self.sigtable = self.sigtable.sort_values(by=['start_unix', 'node_id']) - self.sigtable['start_dt'] = self.sigtable['start_unix'].apply(lambda x:datetime.fromtimestamp(x)) - - # 5-3. 신호 파일의 시작 및 종료시각 설정 - def set_timepoints(self): - self.offsets = {} - self.Sigtable = [] - sim_start = self.present_time - 300 - for node_id, group in self.sigtable.groupby('node_id'): - lsbs = group[group['start_unix'] < sim_start]['start_unix'].max() # the last start_unix before sim_start - self.offsets[node_id] = lsbs - sim_start - group = group[group.start_unix >= lsbs] - start_unixes = np.array(group.start_unix) - start_unixes = np.sort(np.unique(start_unixes))[:self.node2num_cycles[node_id]] - group = group[group.start_unix.isin(start_unixes)] - self.Sigtable.append(group) - self.Sigtable = pd.concat(self.Sigtable) - - # 5-4. 적색 및 황색신호 부여 - def assign_red_yellow(self): - ''' - 적색, 황색신호를 반영한 신호문자열 배정 - - input : Sigtable - - 모든 교차로에 대한 (시작유닉스, 세부현시번호)별 현시시간, 신호문자열, 진입·진출엣지 - * 세부현시란 오버랩을 반영한 현시번호를 뜻함. - - output : SIGTABLE - - 모든 교차로에 대한 (시작유닉스, 녹황적세부현시번호)별 현시시간, (황·적색신호가 포함된) 신호문자열 - * 녹황적세부현시번호란 세부현시번호에 r, g, y 옵션까지 포함된 현시번호를 뜻함. - ''' - self.SIGTABLE = [] - for node_id, group in self.Sigtable.groupby('node_id'): - new_rows_list = [] - for i in range(1, len(group)): - prev_row = group.iloc[i-1:i].copy() - next_row = group.iloc[i:i+1].copy() - new_rows = pd.concat([prev_row, prev_row, next_row]).reset_index(drop=True) - new_rows.loc[0, 'phase_sumo'] = str(prev_row.phase_sumo.iloc[0]) + '_g' - new_rows.loc[0, 'duration'] = new_rows.loc[0, 'duration'] - 5 - new_rows.loc[1, 'phase_sumo'] = str(prev_row.phase_sumo.iloc[0]) + '_y' - new_rows.loc[1, 'duration'] = 4 - yellow_state = '' - red_state = '' - for a, b in zip(prev_row.state.iloc[0], next_row.state.iloc[0]): - if a == 'G' and b == 'r': - yellow_state += 'y' - red_state += 'r' - else: - yellow_state += a - red_state += a - new_rows.loc[2, 'phase_sumo'] = str(next_row.phase_sumo.iloc[0]) + '__r' - new_rows.loc[2, 'duration'] = 1 - new_rows.loc[1, 'state'] = yellow_state - new_rows.loc[2, 'state'] = red_state - new_rows_list.append(new_rows) - next_row['phase_sumo'] = str(next_row.phase_sumo.iloc[0]) + '_g' - next_row['duration'] -= 5 - # next_row.loc['duration'] -= 5 - new_rows_list.append(next_row) - new_rows = pd.concat(new_rows_list) - self.SIGTABLE.append(new_rows) - self.SIGTABLE = pd.concat(self.SIGTABLE).sort_values(by=['node_id', 'start_unix', 'phase_sumo']).reset_index(drop=True) - - # 5-5. 신호파일 생성 - def make_tl_file(self): - strings = ['\n'] - for node_id, group in self.SIGTABLE.groupby('node_id'): - strings.append(f' \n') - for i, row in group.iterrows(): - duration = row.duration - state = row.state - strings.append(f' \n') - strings.append(' \n') - strings.append('') - strings = ''.join(strings) - # 저장 - self.path_output = os.path.join(self.path_results, f'sn_{self.present_time}.add.xml') - with open(self.path_output, 'w') as f: - f.write(strings) - - # 6. 이슈사항 저장 - def write_issues(self): - print('6. 이슈사항을 저장합니다.') - path_issues = os.path.join(self.path_results, "issues_generate_signals.txt") - with open(path_issues, "w", encoding="utf-8") as file: - for item in self.issues: - file.write(item + "\n") - if self.issues: - print("데이터 처리 중 발생한 특이사항은 다음과 같습니다. :") - for review in self.issues: - print(review) - - def main(self): - self.time0 = datetime.now() - # 1. 데이터 준비 - self.prepare_data() - self.time1 = datetime.now() - # 2. 신호이력 전처리 - self.process_history() - self.time2 = datetime.now() - # 3. 이동류정보 전처리 - self.process_movement() - self.time3 = datetime.now() - # 4. 통합테이블 생성 - self.make_histids() - self.time4 = datetime.now() - # 5. 신호 생성 - self.get_signals() - self.time5 = datetime.now() - # 6. 이슈사항 저장 - self.write_issues() - self.time6 = datetime.now() - - print('(1)', self.time1 - self.time0) - print('(2-1)', self.time21 - self.time1) - print('(2-2)', self.time22 - self.time21) - print('(2-3)', self.time23 - self.time22) - print('(2)', self.time2 - self.time1) - print('(3)', self.time3 - self.time2) - print('(4)', self.time4 - self.time3) - print('(4-1)', self.time41 - self.time3) - print('(4-2)', self.time42 - self.time41) - print('(5)', self.time5 - self.time4) - print('(6)', self.time6 - self.time5) - print('total time :', self.time6 - self.time0) - -if __name__ == '__main__': - self = SignalGenerator() - self.main() - self.path_unit = os.path.join(self.path_root, 'Analysis', '0207_unit_test') - self.hrhists.to_csv(os.path.join(self.path_unit, 'hrhists.csv')) - self.histids.to_csv(os.path.join(self.path_unit, 'histids.csv')) - self.sigtable.to_csv(os.path.join(self.path_unit, 'sigtable.csv')) - self.Sigtable.to_csv(os.path.join(self.path_unit, 'ssigtable.csv')) - # print("elapsed time :", datetime.now() - starting_time) \ No newline at end of file diff --git a/Archives/Scripts/preprocess.ipynb b/Archives/Scripts/preprocess.ipynb deleted file mode 100644 index b22f4040b..000000000 --- a/Archives/Scripts/preprocess.ipynb +++ /dev/null @@ -1,1814 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import os\n", - "import sumolib\n", - "import random\n", - "from tqdm import tqdm\n", - "from datetime import datetime" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# A. 이동류 매칭" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match1():\n", - " '''\n", - " 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다.\n", - " '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다.\n", - "\n", - " return : 통합된 이동류정보\n", - " - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보\n", - "\n", - " match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다.\n", - " '''\n", - " # [이동류번호] 불러오기 (약 1분의 소요시간)\n", - " path_move = '../Data/tables/move/'\n", - " csv_moves = os.listdir(path_move)\n", - " moves = [pd.read_csv(path_move + csv_move, index_col=0) for csv_move in tqdm(csv_moves)]\n", - " match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True)\n", - " match1.to_csv('../Intermediates/match1.csv')\n", - " return match1" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match2(match1):\n", - " '''\n", - " match1을 계층화함.\n", - " - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B\n", - " - match2의 컬럼 : inter_no, phase_no, ring_type, move_no\n", - " '''\n", - " # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no')\n", - " matchA = match1[['inter_no', 'phas_A', 'move_A']].copy()\n", - " matchA.columns = ['inter_no', 'phase_no', 'move_no']\n", - " matchA['ring_type'] = 'A'\n", - " matchB = match1[['inter_no', 'phas_B', 'move_B']].copy()\n", - " matchB.columns = ['inter_no', 'phase_no', 'move_no']\n", - " matchB['ring_type'] = 'B'\n", - " match2 = pd.concat([matchA, matchB]).drop_duplicates()\n", - " match2 = match2[['inter_no', 'phase_no', 'ring_type', 'move_no']]\n", - " match2 = match2.sort_values(by=list(match2.columns))\n", - " return match2" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match3(match2):\n", - " '''\n", - " 각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함.\n", - " - match2의 컬럼 : inter_no, phase_no, ring_type, move_no\n", - " - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir\n", - "\n", - " nema : \n", - " - 컬럼 : move_no, inc_dir, out_dir\n", - " - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블\n", - " - 이동류번호 : 1 ~ 16, 17, 18, 21\n", - " - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서\n", - " '''\n", - " # nema 정보 불러오기 및 병합\n", - " nema = pd.read_csv('../Data/tables/nema.csv', encoding='cp949')\n", - " match3 = pd.merge(match2, nema, how='left', on='move_no').drop_duplicates()\n", - " return match3" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match4(match3):\n", - " '''\n", - " 방위각 정보를 매칭시켜 추가함.\n", - " - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir\n", - " - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle\n", - "\n", - " angle_original : \n", - " - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8)\n", - " - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블\n", - " - 이동류번호 : 1 ~ 16, 17, 18, 21\n", - " - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서\n", - " '''\n", - "\n", - " # 방위각 정보 불러오기\n", - " dtype_dict = {f'angle_{alph}{j}':'str' for alph in ['A', 'B'] for j in range(1,9)}\n", - " angle_original = pd.read_csv('../Data/tables/angle.csv', index_col=0, dtype = dtype_dict)\n", - "\n", - " # 계층화\n", - " angle = []\n", - " for i, row in angle_original.iterrows():\n", - " angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]\n", - " new = pd.DataFrame({'inter_no':[row.inter_no] * 16, 'phase_no':list(range(1, 9))*2, 'ring_type':['A'] * 8 + ['B'] * 8, 'angle_code':angle_codes.to_list()})\n", - " angle.append(new)\n", - " angle = pd.concat(angle)\n", - " angle = angle.dropna().reset_index(drop=True)\n", - "\n", - " # 병합\n", - " six_chars = angle.angle_code.apply(lambda x:len(x)==6)\n", - " angle.loc[six_chars,'inc_angle'] = angle.angle_code.apply(lambda x:x[:3])\n", - " angle.loc[six_chars,'out_angle'] = angle.angle_code.apply(lambda x:x[3:])\n", - " angle = angle.drop('angle_code', axis=1)\n", - " match4 = pd.merge(match3, angle, how='left', left_on=['inter_no', 'phase_no', 'ring_type'],\n", - " right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates()\n", - " return match4" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match5(match4):\n", - " '''\n", - " 진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로).\n", - " - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle\n", - " - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id\n", - " \n", - " 사용된 데이터 : \n", - " (1) net\n", - " - 성남시 정자동 부근의 샘플 네트워크\n", - " (2) inter_node\n", - " - 교차로번호와 노드id를 매칭시키는 테이블.\n", - " - parent/child 정보도 포함되어 있음\n", - " - 컬럼 : inter_no, node_id, inter_type\n", - " (3) inter_info\n", - " - 교차로 정보. 여기에서는 위도와 경도가 쓰임.\n", - " - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no\n", - "\n", - " 진입엣지id, 진출엣지id를 얻는 과정 :\n", - " - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함.\n", - " * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음\n", - " - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장.\n", - " * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge\n", - " - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄.\n", - " - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄.\n", - " - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함.\n", - " '''\n", - "\n", - " # 네트워크 불러오기 \n", - " net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n", - " # 교차로-노드 매칭 정보 불러오기\n", - " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - " # 교차로정보(위, 경도) 불러오기\n", - " inter_info = pd.read_csv('../Data/tables/inter_info.csv', index_col=0)\n", - "\n", - " # parent node만 가져옴.\n", - " inter_node1 = inter_node[inter_node.inter_type == 'parent'].drop('inter_type', axis=1)\n", - " inter_info1 = inter_info[['inter_no', 'inter_lat', 'inter_lon']]\n", - " inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'],\n", - " right_on=['inter_no']).drop_duplicates()\n", - "\n", - " inter2node = dict(zip(inter['inter_no'], inter['node_id']))\n", - "\n", - " match5 = match4.copy()\n", - " # 진입진출ID 매칭\n", - " for index, row in match5.iterrows():\n", - " node_id = inter2node[row.inter_no]\n", - " node = net.getNode(node_id)\n", - " # 교차로의 모든 (from / to) edges\n", - " inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges\n", - " out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges\n", - " # 교차로의 모든 (from / to) directions\n", - " inc_dirs = []\n", - " for inc_edge in inc_edges:\n", - " start = inc_edge.getShape()[-2]\n", - " end = inc_edge.getShape()[-1]\n", - " inc_dir = np.array(end) - np.array(start)\n", - " inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5\n", - " inc_dirs.append(inc_dir)\n", - " out_dirs = []\n", - " for out_edge in out_edges:\n", - " start = out_edge.getShape()[0]\n", - " end = out_edge.getShape()[1]\n", - " out_dir = np.array(end) - np.array(start)\n", - " out_dir = out_dir / (out_dir ** 2).sum() ** 0.5\n", - " out_dirs.append(out_dir)\n", - " # 진입각, 진출각 불러오기\n", - " if not pd.isna(row.inc_angle):\n", - " inc_angle = int(row.inc_angle)\n", - " out_angle = int(row.out_angle)\n", - " # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환\n", - " inc_angle = (-90 - inc_angle) % 360\n", - " inc_angle = inc_angle * np.pi / 180.\n", - " inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)])\n", - " out_angle = (90 - out_angle) % 360\n", - " out_angle = out_angle * np.pi / 180.\n", - " out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)])\n", - " # 매칭 엣지 반환\n", - " inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax()\n", - " out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax()\n", - " inc_edge_id = inc_edges[inc_index].getID()\n", - " out_edge_id = out_edges[out_index].getID()\n", - " match5.at[index, 'inc_edge'] = inc_edge_id\n", - " match5.at[index, 'out_edge'] = out_edge_id\n", - " match5['node_id'] = match5['inter_no'].map(inter2node)\n", - " match5 = match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True)\n", - " return match5" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match6(match5):\n", - " '''\n", - " 진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로).\n", - " - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id\n", - " \n", - " 사용된 데이터 : \n", - " (1) inter_node\n", - " - 교차로번호와 노드id를 매칭시키는 테이블.\n", - " - parent/child 정보도 포함되어 있음\n", - " - 컬럼 : inter_no, node_id, inter_type\n", - " (2) uturn (유턴정보)\n", - " - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge\n", - " - parent_id, child_id : 주교차로id, 유턴교차로id\n", - " - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향)\n", - " - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나\n", - " - inc_edge, out_edge : 유턴에 대한 진입진출엣지\n", - " (3) coord (연동교차로정보)\n", - " - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge\n", - " - parent_id, child_id : 주교차로id, 연동교차로id\n", - " - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지\n", - "\n", - " 설명 :\n", - " - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음.\n", - " 여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함.\n", - " 유턴교차로 :\n", - " - directions를 정북기준 시계방향의 8방위로 정함.\n", - " - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음.\n", - " - 예) 진입방향(direction)이 '북'일 때, \n", - " - 직진 : (북, 남)\n", - " * 남 : directions[(ind + 4) % len(directions)]\n", - " - 좌회전 : (북, 동)\n", - " * 동 : directions[(ind + 2) % len(directions)]\n", - " - 보행 : (서, 동)\n", - " * 서 : directions[(ind - 2) % len(directions)]\n", - " - uturn의 각 행을 순회하면서 아래 과정을 반복함\n", - " - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch).\n", - " - condition 별로 진입방향, 진출방향A, 진출방향B 정함.\n", - " - 상술한 directions를 활용하여 정함.\n", - " - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함.\n", - " - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함.\n", - "\n", - " 연동교차로 :\n", - " - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음.\n", - " - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음.\n", - " - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음.\n", - "\n", - " match6 :\n", - " - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄.\n", - " '''\n", - "\n", - " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - " node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", - "\n", - " uturn = pd.read_csv('../Data/tables/child_uturn.csv')\n", - " coord = pd.read_csv('../Data/tables/child_coord.csv')\n", - " child_ids = inter_node[inter_node.inter_type=='child'].node_id.unique()\n", - " ch2pa = {} # child to parent\n", - " for child_id in child_ids:\n", - " parent_no = inter_node[inter_node.node_id==child_id].inter_no.iloc[0]\n", - " sub_inter_node = inter_node[inter_node.inter_no==parent_no]\n", - " ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id\n", - " directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향\n", - "\n", - " # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여\n", - " cmatches = []\n", - " for _, row in uturn.iterrows():\n", - " child_id = row.child_id\n", - " parent_id = row.parent_id\n", - " direction = row.direction\n", - " condition = row.condition\n", - " inc_edge_id = row.inc_edge\n", - " out_edge_id = row.out_edge\n", - " # match5에서 parent_id에 해당하는 행들을 가져옴\n", - " cmatch = match5.copy()[match5.node_id==parent_id] # match dataframe for a child node\n", - " cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True)\n", - " cmatch['node_id'] = child_id\n", - " cmatch[['inc_edge', 'out_edge']] = np.nan\n", - "\n", - " # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함\n", - " ind = directions.index(direction)\n", - " if condition == \"좌회전시\":\n", - " inc_dire = direction\n", - " out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)]\n", - " elif condition == \"직진시\":\n", - " inc_dire = direction\n", - " out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)]\n", - " elif condition == \"보행신호시\":\n", - " inc_dire = directions[(ind + 2) % len(directions)]\n", - " out_dire_A = directions[(ind - 2) % len(directions)]\n", - " out_dire_B = directions[(ind - 2) % len(directions)]\n", - "\n", - " # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함\n", - " if condition == '보행신호시':\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다.\n", - " cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " else: # '직진시', '좌회전시'\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " # 유턴신호의 이동류번호를 19로 부여한다.\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19\n", - " cmatches.append(cmatch)\n", - "\n", - " # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여\n", - " coord['inter_no'] = coord['parent_id'].map(node2inter)\n", - " coord = coord.rename(columns={'child_id':'node_id'})\n", - " coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan\n", - " coord['move_no'] = 20\n", - " coord = coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']]\n", - " \n", - " # display(coord)\n", - " cmatches = pd.concat(cmatches)\n", - " match6 = pd.concat([match5, cmatches, coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type'])\n", - " match6.to_csv('../Intermediates/match6.csv')\n", - " return match6" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "def make_matching(match6):\n", - " '''\n", - " 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다.\n", - " 모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다. \n", - " - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id\n", - " \n", - " 설명 : \n", - " - 필요한 리스트, 딕셔너리 등을 정의\n", - " (1) 가능한 (진입방향, 진출방향) 목록 [리스트]\n", - " (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리]\n", - " (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리]\n", - " (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리]\n", - " (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리]\n", - " - matching은 빈 리스트로 지정.\n", - " - 모든 노드id에 대하여 다음 과정을 반복\n", - " - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복\n", - " - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄\n", - " - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append\n", - " '''\n", - "\n", - " match7 = match6.copy()\n", - " match7 = match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']]\n", - " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - " nema = pd.read_csv('../Data/tables/nema.csv', encoding='cp949')\n", - "\n", - " parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n", - " child_ids = sorted(inter_node[inter_node.inter_type=='child'].node_id.unique())\n", - "\n", - " # (1) 가능한 (진입방향, 진출방향) 목록 \n", - " flows = nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist()\n", - " # (2) 각 교차로별 방향 목록 : pdires (possible directions)\n", - " pdires = {}\n", - " for node_id in parent_ids:\n", - " dires = match7[match7.node_id == node_id][['inc_dir','out_dir']].values.flatten()\n", - " dires = {dire for dire in dires if type(dire)==str}\n", - " pdires[node_id] = dires\n", - " # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id)\n", - " inc2id = {}\n", - " for node_id in parent_ids:\n", - " for inc_dir in pdires[node_id]:\n", - " df = match7[(match7.node_id==node_id) & (match7.inc_dir==inc_dir)]\n", - " inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0]\n", - " # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id)\n", - " out2id = {}\n", - " for node_id in parent_ids:\n", - " for out_dir in pdires[node_id]:\n", - " df = match7[(match7.node_id==node_id) & (match7.out_dir==out_dir)]\n", - " out2id[(node_id, out_dir)] = df.out_edge.iloc[0]\n", - " # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows)\n", - " pflow = {}\n", - " for node_id in parent_ids:\n", - " pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])]\n", - " # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching\n", - " node2inter = dict(zip(match7['node_id'], match7['inter_no']))\n", - " dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow\n", - " matching = []\n", - " for node_id in parent_ids:\n", - " inter_no = node2inter[node_id]\n", - " # 좌회전과 직진(1 ~ 16)\n", - " for (inc_dir, out_dir) in pflow[node_id]:\n", - " move_no = nema[(nema.inc_dir==inc_dir) & (nema.out_dir==out_dir)].move_no.iloc[0]\n", - " inc_edge = inc2id[(node_id, inc_dir)]\n", - " out_edge = out2id[(node_id, out_dir)]\n", - " new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no],\n", - " 'inc_dir':[inc_dir], 'out_dir':[out_dir],\n", - " 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})\n", - " matching.append(new_row)\n", - " # 보행신호(17), 전적색(18)\n", - " new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18],\n", - " 'inc_dir':[None]*2, 'out_dir':[None]*2,\n", - " 'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2})\n", - " matching.append(new_row)\n", - " # 신호우회전(21)\n", - " for d in range(len(dires_right)-1):\n", - " inc_dir = dires_right[d]\n", - " out_dir = dires_right[d+1]\n", - " if {inc_dir, out_dir}.issubset(pdires[node_id]):\n", - " inc_edge = inc2id[(node_id, inc_dir)]\n", - " out_edge = out2id[(node_id, out_dir)]\n", - " new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21],\n", - " 'inc_dir':[inc_dir], 'out_dir':[out_dir],\n", - " 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})\n", - " matching.append(new_row)\n", - " matching.append(match7[match7.node_id.isin(child_ids)])\n", - " matching = pd.concat(matching)\n", - " matching = matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True)\n", - " matching['move_no'] = matching['move_no'].astype(int)\n", - " matching.to_csv('../Intermediates/matching.csv')\n", - " return matching" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "match1 = pd.read_csv('../Intermediates/match1.csv', index_col=0)\n", - "match2 = make_match2(match1)\n", - "match3 = make_match3(match2)\n", - "match4 = make_match4(match3)\n", - "match5 = make_match5(match4)\n", - "match6 = make_match6(match5)\n", - "matching = make_matching(match6)" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nophase_noring_typemove_noinc_dirout_dirinc_angleout_angleinc_edgeout_edgenode_id
01751A8179004-571542797_02571500487_01i0
11751B4003176-571500487_01571542797_02i0
21752A7001095-571500487_01571545870_01i0
31752B3179270-571542797_02571510153_01i0
41753A6090270571545870_02571510153_01i0
....................................
32102B2270090NaNNaNu60
42103A7359090NaNNaNu60
52103B4000180NaNNaNu60
62104A8180000NaNNaNu60
72104B3180270NaNNaNu60
\n", - "

116 rows × 11 columns

\n", - "
" - ], - "text/plain": [ - " inter_no phase_no ring_type move_no inc_dir out_dir inc_angle out_angle \\\n", - "0 175 1 A 8 남 북 179 004 \n", - "1 175 1 B 4 북 남 003 176 \n", - "2 175 2 A 7 북 동 001 095 \n", - "3 175 2 B 3 남 서 179 270 \n", - "4 175 3 A 6 동 서 090 270 \n", - ".. ... ... ... ... ... ... ... ... \n", - "3 210 2 B 2 서 동 270 090 \n", - "4 210 3 A 7 북 동 359 090 \n", - "5 210 3 B 4 북 남 000 180 \n", - "6 210 4 A 8 남 북 180 000 \n", - "7 210 4 B 3 남 서 180 270 \n", - "\n", - " inc_edge out_edge node_id \n", - "0 -571542797_02 571500487_01 i0 \n", - "1 -571500487_01 571542797_02 i0 \n", - "2 -571500487_01 571545870_01 i0 \n", - "3 -571542797_02 571510153_01 i0 \n", - "4 571545870_02 571510153_01 i0 \n", - ".. ... ... ... \n", - "3 NaN NaN u60 \n", - "4 NaN NaN u60 \n", - "5 NaN NaN u60 \n", - "6 NaN NaN u60 \n", - "7 NaN NaN u60 \n", - "\n", - "[116 rows x 11 columns]" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "match6" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# B. 5초 간격으로 이동류번호 수집" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# 5초 단위로 이동류번호 저장 및 신호이력에서 유닉스시각 가져와서 표시, 한시간동안의 데이터만 보관\n", - "midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - "next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - "fsecs = range(midnight, next_day, 5) # fsecs : unix time by Five SECondS\n", - "fmins = range(midnight, next_day, 300) # fmins : unix time by Five MINuteS" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def save_movement():\n", - " # time2move = dict(zip(fsecs,moves)) # move : 어느 순간의 이동류정보\n", - " history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", - "\n", - " time2movement = {} # movement : 어느 순간의, 그 순간으로부터 한시간 동안의 (교차로번호 + 현시별이동류번호 + 시작시간)\n", - " # - 아래 절차를 5초마다 반복\n", - " for fsec in tqdm(fsecs): # fsec : unix time by Five SECond\n", - " # 1. 상태 테이블 조회해서 전체 데이터중 필요데이터(교차로번호, A링 현시번호, A링 이동류번호, B링 현시번호, B링 이동류번호)만 수집 : A\n", - " # move = time2move[fsec]\n", - " move = pd.read_csv(f'../Data/tables/move/move_{fsec}.csv', index_col=0)\n", - " # 2. 이력 테이블 조회해서 교차로별로 유닉스시간 최대인 데이터(교차로변호, 종료유닉스타임)만 수집 : B\n", - " recent_histories = [group.iloc[-1:] for _, group in history[history['end_unix'] < fsec].groupby('inter_no')] # 교차로별로 유닉스시간이 최대인 행들\n", - " if not recent_histories:\n", - " rhistory = pd.DataFrame({'inter_no':[], 'end_unix':[]}) # recent history\n", - " else:\n", - " rhistory = pd.concat(recent_histories)\n", - " recent_unix = rhistory[['inter_no', 'end_unix']]\n", - " # 3. 상태 테이블 조회정보(A)와 이력 테이블 조회정보(B) 조인(키값 : 교차로번호) : C\n", - " move = pd.merge(move, recent_unix, how='left', on='inter_no')\n", - " move['end_unix'] = move['end_unix'].fillna(0).astype(int)\n", - " move = move.drop_duplicates()\n", - " # 4. C데이터 프레임에 신규 컬럼(시작 유닉스타임) 생성 후 종료유닉스 타임 값 입력, 종료 유닉스 타임 컬럼 제거\n", - " move = move.rename(columns = {'end_unix':'start_unix'})\n", - " # 5. 이동류 이력정보 READ\n", - " # - CSV 파일로 서버에 저장된 이동류정보를 읽어옴(파일이 없는 경우에는 데이터가 없는 프레임 D 생성)\n", - " try:\n", - " if isinstance(movement, pd.DataFrame): # movement가 존재할 경우 그걸 그대로 씀.\n", - " pass\n", - " else: \n", - " movement = pd.DataFrame()\n", - " except NameError: # movement가 존재하지 않는 경우 생성\n", - " movement = pd.DataFrame()\n", - " # 6. 이동류 이력정보 데이터테이블(D)에 C데이터 add\n", - " movement = pd.concat([movement, move])\n", - " # 7. D데이터 프레임에서 중복데이터 제거(교차로번호, 시작 유닉스타임, A링 현시번호, B링 현시번호 같은 행은 제거)\n", - " movement = movement.drop_duplicates(['inter_no','phas_A','phas_B','start_unix'])\n", - " # 8. D데이터 보관 시간 기준시간을 시작 유닉스 타임의 최대값 - 3600을 값으로 산출하고, 보관 시간 기준시간보다 작은 시작 유닉스 타임을 가진 행은 모두 제거(1시간 데이터만 보관)\n", - " movement = movement[movement.start_unix > fsec - 3600]\n", - " movement = movement.sort_values(by=['start_unix','inter_no','phas_A','phas_B']).reset_index(drop=True)\n", - "\n", - " time2movement[fsec] = movement\n", - " movement.to_csv(f'../Intermediates/movement/movement_{fsec}.csv')\n", - "# save_movement()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# C. 5분 간격으로 신호이력 수집 및 통합테이블 생성" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "m = 30\n", - "midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - "next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - "fmins = range(midnight, next_day, 300)\n", - "\n", - "# 현재시각\n", - "present_time = fmins[m]\n", - "\n", - "plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", - "history = pd.read_csv('../Data/tables/history.csv', index_col=0)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def make_splits(plan):\n", - " # split, isplit : A,B 분리 혹은 통합시 사용될 수 있는 딕셔너리 \n", - " splits = {} # splits maps (inter_no, start_hour, start_minute) to split \n", - " for i, row in plan.iterrows():\n", - " inter_no = row.inter_no\n", - " start_hour = row.start_hour\n", - " start_minute = row.start_minute\n", - " cycle = row.cycle\n", - " cums_A = row[[f'dura_A{j}' for j in range(1,9)]].cumsum()\n", - " cums_B = row[[f'dura_B{j}' for j in range(1,9)]].cumsum()\n", - " splits[(inter_no, start_hour, start_minute)] = {} # split maps (phas_A, phas_B) to k\n", - " k = 0\n", - " for t in range(cycle):\n", - " new_phas_A = len(cums_A[cums_A < t]) + 1\n", - " new_phas_B = len(cums_B[cums_B < t]) + 1\n", - " if k == 0 or ((new_phas_A, new_phas_B) != (phas_A, phas_B)):\n", - " k += 1\n", - " phas_A = new_phas_A\n", - " phas_B = new_phas_B\n", - " splits[(inter_no, start_hour, start_minute)][(phas_A, phas_B)] = k\n", - "\n", - " isplits = {} # the inverse of splits\n", - " for i in splits:\n", - " isplits[i] = {splits[i][k]:k for k in splits[i]} # isplit maps k to (phas_A, phas_B)\n", - " return splits, isplits\n", - "splits, isplits = make_splits(plan)\n", - "\n", - "def make_timetable(plan):\n", - " # timetable\n", - " timetable = plan[['start_hour', 'start_minute']].drop_duplicates()\n", - " timetable['start_seconds'] = midnight + timetable['start_hour'] * 3600 + timetable['start_minute'] * 60\n", - " return timetable\n", - "timetable = make_timetable(plan)\n", - "\n", - "# inter2node\n", - "inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - "inter_node = inter_node[inter_node.inter_type=='parent']\n", - "inter2node = dict(zip(inter_node['inter_no'], inter_node['node_id']))\n", - "\n", - "hours = np.array(range(midnight - 7200, next_day + 1, 3600)) # 정각에 해당하는 시각들 목록" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def calculate_DS(rhist, curr_unix, hours, timetable):\n", - " program_starts = np.array(timetable.start_seconds)\n", - " idx = (program_starts <= present_time).sum() - 1\n", - " program_start = program_starts[idx]\n", - " if list(hours[hours <= curr_unix]):\n", - " ghour_lt_curr_unix = hours[hours <= curr_unix].max() # the greatest hour less than or equal to curr_unix\n", - " else:\n", - " ghour_lt_curr_unix = program_start\n", - " start_unixes = rhist.start_unix.unique()\n", - " start_unixes_lt_ghour = np.sort(start_unixes[start_unixes < ghour_lt_curr_unix]) # start unixes less than ghour_lt_curr_unix\n", - " # 기준유닉스(base_unix) : curr_unix보다 작은 hour 중에서 가장 큰 값으로부터 다섯 번째로 작은 start_unix\n", - " if len(start_unixes_lt_ghour) > 5:\n", - " base_unix = start_unixes_lt_ghour[-5]\n", - " # start_unixes_lt_ghour의 길이가 5 미만일 경우에는 맨 앞 start_unix로 base_unix를 지정\n", - " else:\n", - " base_unix = rhist.start_unix.min()\n", - " D_n = curr_unix - base_unix\n", - " S_n_durs = rhist[(rhist.start_unix > base_unix) & (rhist.start_unix <= curr_unix)] \\\n", - " [[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]\n", - " S_n = S_n_durs.values.sum() // 2\n", - " return D_n, S_n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "def load_prow(timetable, inter_no, time):\n", - " '''\n", - " load planned row\n", - " '''\n", - " # 프로그램 시작시각\n", - " program_starts = np.array(timetable.start_seconds)\n", - " idx = (program_starts <= time).sum() - 1\n", - " program_start = program_starts[idx]\n", - "\n", - " # 최근 프로그램 시작시각에 대한 신호계획\n", - " start_hour = timetable.iloc[idx].start_hour\n", - " start_minute = timetable.iloc[idx].start_minute\n", - " prow = plan[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", - " return program_start, prow" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def make_rhistory(history, present_time, adder):\n", - " # 1. 조회시점의 유닉스 타임 이전의 신호이력 수집\n", - " rhistory = history.copy() # recent history\n", - " rhistory = rhistory[(rhistory.end_unix <= present_time) & (rhistory.end_unix > present_time - 9000)] # 두 시간 반 전부터 현재까지의 신호이력을 가져옴. 9000 = 3600 * 2.5\n", - "\n", - " # rhistory에 모든 교차로번호가 존재하지 않으면 해당 교차로번호에 대한 신호이력을 추가함 (at 최근 프로그램 시작시각)\n", - " whole_inter_nos = sorted(history.inter_no.unique())\n", - " recent_inter_nos = sorted(rhistory.inter_no.unique())\n", - " if not whole_inter_nos==recent_inter_nos:\n", - " for inter_no in set(whole_inter_nos) - set(recent_inter_nos):\n", - " program_start, prow = load_prow(timetable, inter_no, present_time - 9000)\n", - " cycle = prow.cycle.iloc[0]\n", - " row1 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", - " row2 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", - " # prow에서 필요한 부분을 rhistory에 추가\n", - " row1['end_unix'] = program_start\n", - " row2['end_unix'] = program_start + cycle\n", - " rhistory = pd.concat([rhistory, row1, row2]).reset_index(drop=True)\n", - " # present_time + adder 의 시각에 한 주기의 신호 추가\n", - " for inter_no in set(whole_inter_nos):\n", - " program_start, prow = load_prow(timetable, inter_no, present_time)\n", - " cycle = prow.cycle.iloc[0]\n", - " row3 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", - " # prow에서 필요한 부분을 rhistory에 추가\n", - " row3['end_unix'] = present_time + adder\n", - " rhistory = pd.concat([rhistory, row3]).reset_index(drop=True)\n", - "\n", - " # 2. 시작 유닉스 타임컬럼 생성 후 종류 유닉스 타임에서 현시별 현시기간 컬럼의 합을 뺀 값으로 입력\n", - " # - 현시시간의 합을 뺀 시간의 +- 10초 이내에 이전 주기정보가 존재하면 그 유닉스 시간을 시작 유닉스시간 값으로 하고, 존재하지 않으면 현시시간의 합을 뺀 유닉스 시간을 시작 유닉스 시간으로 지정\n", - " for i, row in rhistory.iterrows():\n", - " inter_no = row.inter_no\n", - " end_unix = row.end_unix\n", - " elapsed_time = row[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]].sum() // 2 # 현시시간 합\n", - " # 이전 유닉스 존재하지 않음 : 현시시간 합의 차\n", - " start_unix = end_unix - elapsed_time\n", - " pre_rows = history[:i] # previous rows\n", - " if inter_no in pre_rows.inter_no.unique(): # 이전 유닉스 존재\n", - " pre_unix = pre_rows[pre_rows.inter_no == inter_no]['end_unix'].iloc[-1] # previous unix time\n", - " # 이전 유닉스 존재, abs < 10 : 이전 유닉스\n", - " if abs(pre_unix - start_unix) < 10:\n", - " start_unix = pre_unix\n", - " # 이전 유닉스 존재, abs >=10 : 현시시간 합의 차\n", - " else:\n", - " pass\n", - " rhistory.loc[i, 'start_unix'] = start_unix \n", - " rhistory[rhistory.isna()] = 0\n", - " rhistory['start_unix'] = rhistory['start_unix'].astype(int)\n", - " rhistory = rhistory[['inter_no', 'start_unix'] + [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)] + ['cycle']]\n", - " return rhistory\n", - "adder = 600\n", - "rhistory = make_rhistory(history, present_time, adder)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def processing(rhistory, hours):\n", - " rhists = []\n", - " for inter_no in sorted(rhistory.inter_no.unique()):\n", - " rhist = rhistory.copy()[rhistory.inter_no==inter_no]\n", - " rhist = rhist.drop_duplicates(subset=['start_unix']).reset_index(drop=True)\n", - "\n", - " # D_n 및 S_n 값 정의\n", - " rhist['D_n'] = 0 # D_n : 시간차이\n", - " rhist['S_n'] = 0 # S_n : 현시시간합\n", - " for n in range(len(rhist)):\n", - " curr_unix = rhist.iloc[n].start_unix # current start_unix\n", - " rhist.loc[n, ['D_n', 'S_n']] = calculate_DS(rhist, curr_unix, hours, timetable)\n", - "\n", - " # 이전시각, 현재시각\n", - " prev_unix = rhist.loc[0, 'start_unix'] # previous start_unix\n", - " curr_unix = rhist.loc[1, 'start_unix'] # current start_unix\n", - "\n", - " # rhist의 마지막 행에 도달할 때까지 반복\n", - " while True:\n", - " n = rhist[rhist.start_unix==curr_unix].index[0]\n", - " cycle = rhist.loc[n, 'cycle']\n", - " D_n = rhist.loc[n, 'D_n']\n", - " S_n = rhist.loc[n, 'S_n']\n", - " # 참값인 경우\n", - " if (abs(D_n - S_n) <= 5):\n", - " pass\n", - " # 참값이 아닌 경우\n", - " else:\n", - " # 2-1-1. 결측치 처리 : 인접한 두 start_unix의 차이가 계획된 주기의 두 배보다 크면 결측이 일어났다고 판단, 신호계획의 현시시간으로 \"대체\"\n", - " if curr_unix - prev_unix >= 2 * cycle:\n", - " # prev_unix를 계획된 주기만큼 늘려가면서 한 행씩 채워나간다.\n", - " # (curr_unix와의 차이가 계획된 주기보다 작거나 같아질 때까지)\n", - " while curr_unix - prev_unix > cycle:\n", - " prev_unix += cycle\n", - " # 신호 계획(prow) 불러오기\n", - " start_seconds = np.array(timetable.start_seconds)\n", - " idx = (start_seconds <= prev_unix).sum() - 1\n", - " start_hour = timetable.iloc[idx].start_hour\n", - " start_minute = timetable.iloc[idx].start_minute\n", - " prow = plan.copy()[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", - " # prow에서 필요한 부분을 rhist에 추가\n", - " prow['start_unix'] = prev_unix\n", - " prow = prow.drop(['start_hour', 'start_minute', 'offset'], axis=1)\n", - " cycle = prow.iloc[0].cycle\n", - " rhist = pd.concat([rhist, prow])\n", - " rhist = rhist.sort_values(by='start_unix').reset_index(drop=True)\n", - " n += 1\n", - "\n", - " # 2-1-2. 이상치 처리 : 비율에 따라 해당 행을 \"삭제\"(R_n <= 0.5) 또는 \"조정\"(R_n > 0.5)한다\n", - " R_n = (curr_unix - prev_unix) / cycle # R_n : 비율\n", - " # R_n이 0.5보다 작거나 같으면 해당 행을 삭제\n", - " if R_n <= 0.5:\n", - " rhist = rhist.drop(index=n).reset_index(drop=True)\n", - " if n >= rhist.index[-1]:\n", - " break\n", - " # 행삭제에 따른 curr_unix, R_n 재정의\n", - " curr_unix = rhist.loc[n, 'start_unix']\n", - " R_n = (curr_unix - prev_unix) / cycle # R_n : 비율\n", - "\n", - " # R_n이 0.5보다 크면 해당 행 조정 (비율을 유지한 채로 현시시간 대체)\n", - " if R_n > 0.5:\n", - " # 신호 계획(prow) 불러오기\n", - " start_seconds = np.array(timetable.start_seconds)\n", - " idx = (start_seconds <= curr_unix).sum() - 1\n", - " start_hour = timetable.iloc[idx].start_hour\n", - " start_minute = timetable.iloc[idx].start_minute\n", - " prow = plan[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", - " # 조정된 현시시간 (prow에 R_n을 곱하고 정수로 바꿈)\n", - " adjusted_dur = prow.copy()[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] * R_n\n", - " int_parts = adjusted_dur.iloc[0].apply(lambda x: int(x))\n", - " frac_parts = adjusted_dur.iloc[0] - int_parts\n", - " difference = round(adjusted_dur.iloc[0].sum()) - int_parts.sum()\n", - " for _ in range(difference): # 소수 부분이 가장 큰 상위 'difference'개의 값에 대해 올림 처리\n", - " max_frac_index = frac_parts.idxmax()\n", - " int_parts[max_frac_index] += 1\n", - " frac_parts[max_frac_index] = 0 # 이미 처리된 항목은 0으로 설정\n", - " # rhist에 조정된 현시시간을 반영\n", - " rhist.loc[n, [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] = int_parts.values\n", - " rhist.loc[n, 'cycle'] = int_parts.sum().sum() // 2\n", - "\n", - " if n >= rhist.index[-1]:\n", - " break\n", - " prev_unix = curr_unix\n", - " curr_unix = rhist.loc[n+1, 'start_unix']\n", - "\n", - " # 생략해도 무방할 코드\n", - " rhist = rhist.reset_index(drop=True)\n", - " rhist = rhist.sort_values(by=['start_unix'])\n", - "\n", - " # D_n 및 S_n 값 재정의\n", - " for n in range(len(rhist)):\n", - " curr_unix = rhist.iloc[n].start_unix # current start_unix\n", - " rhist.loc[n, ['D_n', 'S_n']] = calculate_DS(rhist, curr_unix, hours, timetable)\n", - " rhists.append(rhist)\n", - " rhists = pd.concat(rhists).sort_values(by=['start_unix','inter_no'])\n", - " rhists = rhists[rhists.start_unix >= present_time - 3600]\n", - " rhists = rhists.drop(columns=['D_n', 'S_n'])\n", - " return rhists\n", - "rhists = processing(rhistory, hours)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def make_hrhists(rhists, isplits, timetable):\n", - " # 계층화된 형태로 변환\n", - " hrhists = [] # hierarchied recent history\n", - " for i, row in rhists.iterrows():\n", - " inter_no = row.inter_no\n", - " start_unix = row.start_unix\n", - "\n", - " ind = (timetable['start_seconds'] <= row.start_unix).sum() - 1\n", - " start_hour = timetable.iloc[ind].start_hour\n", - " start_minute = timetable.iloc[ind].start_minute\n", - " isplit = isplits[(inter_no, start_hour, start_minute)]\n", - " phas_As = [isplit[j][0] for j in isplit.keys()]\n", - " phas_Bs = [isplit[j][1] for j in isplit.keys()]\n", - " durs_A = row[[f'dura_A{j}' for j in range(1,9)]]\n", - " durs_B = row[[f'dura_B{j}' for j in range(1,9)]]\n", - " durations = []\n", - " for j in range(1, len(isplit)+1):\n", - " ja = isplit[j][0]\n", - " jb = isplit[j][1]\n", - " if ja == jb:\n", - " durations.append(min(durs_A[ja-1], durs_B[jb-1]))\n", - " else:\n", - " durations.append(abs(durs_A[ja-1] - durs_B[ja-1]))\n", - " new_rows = pd.DataFrame({'inter_no':[inter_no] * len(durations), 'start_unix':[start_unix] * len(durations),\n", - " 'phas_A':phas_As, 'phas_B':phas_Bs, 'duration':durations})\n", - " hrhists.append(new_rows)\n", - " hrhists = pd.concat(hrhists)\n", - " hrhists = hrhists.sort_values(by = ['start_unix', 'inter_no', 'phas_A', 'phas_B']).reset_index(drop=True)\n", - " return hrhists\n", - "hrhists = make_hrhists(rhists, isplits, timetable)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "def make_movements():\n", - " movements_path = '../Intermediates/movement/'\n", - " movements_list = [pd.read_csv(movements_path + file, index_col=0) for file in tqdm(os.listdir(movements_path))]\n", - " movements = pd.concat(movements_list)\n", - " movements = movements.drop(columns=['start_unix'])\n", - " movements = movements.drop_duplicates()\n", - " movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B'])\n", - " movements = movements.reset_index(drop=True)\n", - " movements.to_csv('../Intermediates/movements.csv')\n", - " return movements\n", - "movements = pd.read_csv('../Intermediates/movements.csv')" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def update_movement(hrhists, movement, movements):\n", - " # 중복을 제거하고 (inter_no, start_unix) 쌍을 만듭니다.\n", - " hrhists_inter_unix = set(hrhists[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None))\n", - " movement_inter_unix = set(movement[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None))\n", - "\n", - " # hrhists에는 있지만 movement에는 없는 (inter_no, start_unix) 쌍을 찾습니다.\n", - " missing_in_movement = hrhists_inter_unix - movement_inter_unix\n", - "\n", - " # 새로운 행들을 생성합니다.\n", - " new_rows = []\n", - " if missing_in_movement:\n", - " for inter_no, start_unix in missing_in_movement:\n", - " # movements에서 해당 inter_no의 데이터를 찾습니다.\n", - " new_row = movements[movements['inter_no'] == inter_no].copy()\n", - " # start_unix 값을 설정합니다.\n", - " new_row['start_unix'] = start_unix\n", - " new_rows.append(new_row)\n", - "\n", - " # 새로운 데이터프레임을 생성하고 기존 movement 데이터프레임과 합칩니다.\n", - " new_movement = pd.concat(new_rows, ignore_index=True)\n", - " movement_updated = pd.concat([movement, new_movement], ignore_index=True)\n", - " else:\n", - " movement_updated = movement\n", - " return movement_updated\n", - "movement = pd.read_csv(f'../Intermediates/movement/movement_{present_time}.csv', index_col=0)\n", - "movement_updated = update_movement(hrhists, movement, movements)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "def make_histid(hrhists, movement_updated, present_time, inter2node):\n", - " # movements and durations\n", - " movedur = pd.merge(hrhists, movement_updated, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B'])\n", - " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", - " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", - "\n", - " # 이동류 매칭 테이블에서 진입id, 진출id를 가져와서 붙임.\n", - " for i, row in movedur.iterrows():\n", - " inter_no = row.inter_no\n", - " start_unix = row.start_unix\n", - " # incoming and outgoing edges A\n", - " move_A = row.move_A\n", - " if move_A in [17, 18]:\n", - " inc_edge_A = np.nan\n", - " outhedge_A = np.nan\n", - " else:\n", - " match_A = matching[(matching.inter_no == inter_no) & (matching.move_no == move_A)].iloc[0]\n", - " inc_edge_A = match_A.inc_edge\n", - " out_edge_A = match_A.out_edge\n", - " movedur.loc[i, ['inc_edge_A', 'out_edge_A']] = [inc_edge_A, out_edge_A]\n", - " # incoming and outgoing edges B\n", - " move_B = row.move_B\n", - " if move_B in [17, 18]:\n", - " inc_edge_B = np.nan\n", - " out_edge_B = np.nan\n", - " else:\n", - " match_B = matching[(matching.inter_no == inter_no) & (matching.move_no == move_B)].iloc[0]\n", - " inc_edge_B = match_B.inc_edge\n", - " out_edge_B = match_B.out_edge\n", - " movedur.loc[i, ['inc_edge_B', 'out_edge_B']] = [inc_edge_B, out_edge_B]\n", - "\n", - " # 이동류 컬럼 제거\n", - " movedur = movedur.drop(['move_A', 'move_B'], axis=1)\n", - "\n", - " histid = movedur.copy() # history with edge ids (incoming and outgoing edge ids)\n", - " histid['node_id'] = histid['inter_no'].map(inter2node)\n", - " histid = histid[['inter_no', 'node_id', 'start_unix', 'phas_A', 'phas_B', 'duration', 'inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']]\n", - " histid_start = present_time - 600\n", - " histid = histid[histid.start_unix > histid_start]\n", - " return histid\n", - "histid = make_histid(hrhists, movement_updated, present_time, inter2node)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-01-05 08:45:00\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nonode_idstart_unixphas_Aphas_Bdurationinc_edge_Aout_edge_Ainc_edge_Bout_edge_B
1768206i717044113201144-571511538_02571542073_02571542073_01571511538_02
1769206i717044113202244NaN571542073_02NaNNaN
1770206i717044113203326-571511538_02571542073_02571542073_01571511538_02
1771206i717044113204426NaN571542073_02NaNNaN
1772178i317044113501138571540304_02571556450_01571556450_02571540304_01
.................................
1978201i817044123303318571500617_02571500618_01571500618_02571500617_01
1979201i817044123304458571500617_02571500618_01571500617_02571500569_01
1980201i817044123305518571500583_01571500617_01571500583_01571500569_01
1981202i917044123401126571510152_02-571510152_01571510152_01571510152_01.65
1982202i917044123402264NaN-571510152_01NaNNaN
\n", - "

215 rows × 10 columns

\n", - "
" - ], - "text/plain": [ - " inter_no node_id start_unix phas_A phas_B duration inc_edge_A \\\n", - "1768 206 i7 1704411320 1 1 44 -571511538_02 \n", - "1769 206 i7 1704411320 2 2 44 NaN \n", - "1770 206 i7 1704411320 3 3 26 -571511538_02 \n", - "1771 206 i7 1704411320 4 4 26 NaN \n", - "1772 178 i3 1704411350 1 1 38 571540304_02 \n", - "... ... ... ... ... ... ... ... \n", - "1978 201 i8 1704412330 3 3 18 571500617_02 \n", - "1979 201 i8 1704412330 4 4 58 571500617_02 \n", - "1980 201 i8 1704412330 5 5 18 571500583_01 \n", - "1981 202 i9 1704412340 1 1 26 571510152_02 \n", - "1982 202 i9 1704412340 2 2 64 NaN \n", - "\n", - " out_edge_A inc_edge_B out_edge_B \n", - "1768 571542073_02 571542073_01 571511538_02 \n", - "1769 571542073_02 NaN NaN \n", - "1770 571542073_02 571542073_01 571511538_02 \n", - "1771 571542073_02 NaN NaN \n", - "1772 571556450_01 571556450_02 571540304_01 \n", - "... ... ... ... \n", - "1978 571500618_01 571500618_02 571500617_01 \n", - "1979 571500618_01 571500617_02 571500569_01 \n", - "1980 571500617_01 571500583_01 571500569_01 \n", - "1981 -571510152_01 571510152_01 571510152_01.65 \n", - "1982 -571510152_01 NaN NaN \n", - "\n", - "[215 rows x 10 columns]" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def preprocess(m):\n", - " '''\n", - " 통합테이블(histid)를 만드는 함수\n", - "\n", - " input : m\n", - " - m ranges from 0 to 287, but 0 makes an error where 288 = 86400//300\n", - " - present_time = fmins[m] : 현재시점\n", - "\n", - " output : histid (통합테이블, HISTory with edge_IDs)\n", - " - 컬럼 : inter_no, node_id, start_unix, phas_A, phas_B, duration, inc_edge_A, out_edge_A, inc_edge_B, out_edge_B\n", - "\n", - " 주요 데이터, 중간산출물 및 결과물 :\n", - " # 데이터\n", - " - history : 신호이력 (inter_no, end_unix, dura_Aj, dura_Bj, cycle, offset)\n", - " - plan : 신호계획 (inter_no, start_hour, start_minute, dura_Aj, dura_Bj cycle, offset)\n", - " # 중간산출물\n", - " - rhists (recent history)\n", - " - history에서 현재 시각 이전의 데이터를 가져옴.\n", - " - end_unix를 start_unix로 변환\n", - " - 참값판단 프로세스(결측·이상치 처리)\n", - " - 컬럼 : inter_no, start_unix, dura_Aj, dura_Bj, cycle\n", - " - hrhists (hierarchized recent history)\n", - " - rhists를 계층화\n", - " - 컬럼 : inter_no, start_unix, phas_A, phas_B, duration\n", - " - movements\n", - " - 각 교차로에 대하여 현시별로 이동류를 정해놓음.\n", - " - join시 사용하기 위함.\n", - " - 한 번 만들어놓고 두고두고 사용함.\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B\n", - " - movement\n", - " - 현재 시점에서의 이동류정보\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix\n", - " - movement_updated\n", - " - movement와 hrhists를 join하기 전에, movement에는 없지만 hrhists에는 있는 start_unix에 대한 이동류 정보를 가져와 movement에 붙임\n", - " - 이동류정보는 앞서 정의한 movements에서 가져옴.\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix\n", - " - movedur\n", - " - hrhists와 movement_updated를 join\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix, duration\n", - " # 결과 : histid\n", - " - 신호생성에 직접적으로 사용되는 데이터프레임\n", - " - 컬럼 : inter_no, node_id, start_unix, phas_A, phas_B, duration, inc_edge_A, out_edge_A, inc_edge_B, out_edge_B\n", - " - 한글컬럼 : 교차로번호, 노드id, 시작유닉스, A현시번호, B현시번호, 현시시간, 진입엣지(A), 진출엣지(A), 진입엣지(B), 진출엣지(B)\n", - " '''\n", - " midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - " next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - " fmins = range(midnight, next_day, 300) # fmins : unix time by Five MINuteS\n", - " # 현재시각\n", - " present_time = fmins[m]\n", - " print(datetime.fromtimestamp(present_time))\n", - " # 사용할 표준 테이블 목록\n", - " plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", - " history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", - " # 참고할 딕셔너리, 데이터프레임, 리스트 등 목록\n", - " splits, isplits = make_splits(plan)\n", - " timetable = make_timetable(plan)\n", - " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - " inter_node = inter_node[inter_node.inter_type=='parent']\n", - " inter2node = dict(zip(inter_node['inter_no'], inter_node['node_id']))\n", - " hours = np.array(range(midnight - 7200, next_day + 1, 3600)) # 정각에 해당하는 시각들 목록\n", - " # rhistory, rhists, hrhists\n", - " adder = 600\n", - " rhistory = make_rhistory(history, present_time, adder)\n", - " rhists = processing(rhistory, hours)\n", - " hrhists = make_hrhists(rhists, isplits, timetable)\n", - " # movements, movement, movement_updated\n", - " movements = pd.read_csv('../Intermediates/movements.csv')\n", - " movement = pd.read_csv(f'../Intermediates/movement/movement_{present_time}.csv', index_col=0)\n", - " movement_updated = update_movement(hrhists, movement, movements)\n", - " # movedur\n", - " movedur = pd.merge(movement_updated, hrhists, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B']) # movements and durations\n", - " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", - " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", - " # histid\n", - " histid = make_histid(hrhists, movement_updated, present_time, inter2node)\n", - " histid.to_csv(f'../Intermediates/histid/histid_{fmins[m]}.csv')\n", - " return histid\n", - "preprocess(105)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# import matplotlib.pyplot as plt\n", - "# import matplotlib as mpl\n", - "# mpl.rcParams['font.family'] = 'Malgun Gothic'\n", - "\n", - "# m = 105\n", - "# present_time = fmins[m]\n", - "# histid = preprocess(m)\n", - "# import matplotlib.pyplot as plt\n", - "# k = 0\n", - "# for node_id, group in histid.groupby('node_id'):\n", - "# k += 1\n", - "# plt.plot(group.start_unix.unique(), [k] * len(group.start_unix.unique()), marker='o')\n", - "# plt.axvline(present_time - 300, c='r', linewidth=.5)\n", - "# plt.axvline(present_time, c='r', linewidth=.5)\n", - "# plt.title('adder = 600 (10분)')\n", - "# plt.savefig('../Analysis/0201_adder/adder=600.png')" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-01-05 08:45:00\n", - "2024-01-05 08:50:00\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nonode_idstart_unixphas_Aphas_Bdurationinc_edge_Aout_edge_Ainc_edge_Bout_edge_B
1756202i917044116101146571510152_02-571510152_01571510152_01571510152_01.65
1757202i9170441161022114NaN-571510152_01NaNNaN
1758175i017044116291140-571542797_02571500487_01-571500487_01571542797_02
1759175i017044116292242-571500487_01571545870_01-571542797_02571510153_01
1760175i017044116293329571545870_02571510153_01571545870_02571542797_02
.................................
1972201i817044126405517571500583_01571500617_01571500583_01571500569_01
1973206i717044126601125-571511538_02571542073_02571542073_01571511538_02
1974206i717044126602225NaN571542073_02NaNNaN
1975206i717044126603315-571511538_02571542073_02571542073_01571511538_02
1976206i717044126604415NaN571542073_02NaNNaN
\n", - "

221 rows × 10 columns

\n", - "
" - ], - "text/plain": [ - " inter_no node_id start_unix phas_A phas_B duration inc_edge_A \\\n", - "1756 202 i9 1704411610 1 1 46 571510152_02 \n", - "1757 202 i9 1704411610 2 2 114 NaN \n", - "1758 175 i0 1704411629 1 1 40 -571542797_02 \n", - "1759 175 i0 1704411629 2 2 42 -571500487_01 \n", - "1760 175 i0 1704411629 3 3 29 571545870_02 \n", - "... ... ... ... ... ... ... ... \n", - "1972 201 i8 1704412640 5 5 17 571500583_01 \n", - "1973 206 i7 1704412660 1 1 25 -571511538_02 \n", - "1974 206 i7 1704412660 2 2 25 NaN \n", - "1975 206 i7 1704412660 3 3 15 -571511538_02 \n", - "1976 206 i7 1704412660 4 4 15 NaN \n", - "\n", - " out_edge_A inc_edge_B out_edge_B \n", - "1756 -571510152_01 571510152_01 571510152_01.65 \n", - "1757 -571510152_01 NaN NaN \n", - "1758 571500487_01 -571500487_01 571542797_02 \n", - "1759 571545870_01 -571542797_02 571510153_01 \n", - "1760 571510153_01 571545870_02 571542797_02 \n", - "... ... ... ... \n", - "1972 571500617_01 571500583_01 571500569_01 \n", - "1973 571542073_02 571542073_01 571511538_02 \n", - "1974 571542073_02 NaN NaN \n", - "1975 571542073_02 571542073_01 571511538_02 \n", - "1976 571542073_02 NaN NaN \n", - "\n", - "[221 rows x 10 columns]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "preprocess(105)\n", - "preprocess(106)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# for m in range(30, 288):\n", - "# print(m)\n", - "# histid = preprocess(m)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "rts", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Archives/Scripts/preprocess_5min.ipynb b/Archives/Scripts/preprocess_5min.ipynb deleted file mode 100644 index a03091149..000000000 --- a/Archives/Scripts/preprocess_5min.ipynb +++ /dev/null @@ -1,985 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import os\n", - "from tqdm import tqdm\n", - "from datetime import datetime" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "datetime.datetime(2024, 1, 5, 11, 55, 13, 99135)" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "now = datetime.now()\n", - "now = now.replace(month=1, day=5)\n", - "now" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - "next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - "fsecs = range(midnight, next_day, 5) # fsecs : unix time by Five SECondS\n", - "fmins = range(midnight, next_day, 300) # fmins : unix time by Five MINuteS" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nophas_Aphas_Bmove_Amove_Bstart_unix
017711841704408330
117722731704408330
21773317181704408330
317744511704408330
420111831704408330
.....................
70017844611704411830
70120111831704411850
70220144611704411850
70320155741704411850
7042062217181704411880
\n", - "

705 rows × 6 columns

\n", - "
" - ], - "text/plain": [ - " inter_no phas_A phas_B move_A move_B start_unix\n", - "0 177 1 1 8 4 1704408330\n", - "1 177 2 2 7 3 1704408330\n", - "2 177 3 3 17 18 1704408330\n", - "3 177 4 4 5 1 1704408330\n", - "4 201 1 1 8 3 1704408330\n", - ".. ... ... ... ... ... ...\n", - "700 178 4 4 6 1 1704411830\n", - "701 201 1 1 8 3 1704411850\n", - "702 201 4 4 6 1 1704411850\n", - "703 201 5 5 7 4 1704411850\n", - "704 206 2 2 17 18 1704411880\n", - "\n", - "[705 rows x 6 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", - "m = 105\n", - "present_time = fmins[m]\n", - "\n", - "# - 아래 절차를 5초마다 반복\n", - "for fsec in range(midnight, present_time + 1, 5): # fsec : unix time by Five SECond\n", - " # 1. 상태 테이블 조회해서 전체 데이터중 필요데이터(교차로번호, A링 현시번호, A링 이동류번호, B링 현시번호, B링 이동류번호)만 수집 : A\n", - " # move = time2move[fsec]\n", - " move = pd.read_csv(f'../Data/tables/move/move_{fsec}.csv', index_col=0)\n", - " # 2. 이력 테이블 조회해서 교차로별로 유닉스시간 최대인 데이터(교차로변호, 종료유닉스타임)만 수집 : B\n", - " recent_histories = [group.iloc[-1:] for _, group in history[history['end_unix'] < fsec].groupby('inter_no')] # 교차로별로 유닉스시간이 최대인 행들\n", - " if not recent_histories:\n", - " rhistory = pd.DataFrame({'inter_no':[], 'end_unix':[]}) # recent history\n", - " else:\n", - " rhistory = pd.concat(recent_histories)\n", - " recent_unix = rhistory[['inter_no', 'end_unix']]\n", - " # 3. 상태 테이블 조회정보(A)와 이력 테이블 조회정보(B) 조인(키값 : 교차로번호) : C\n", - " move = pd.merge(move, recent_unix, how='left', on='inter_no')\n", - " move['end_unix'] = move['end_unix'].fillna(0).astype(int)\n", - " move = move.drop_duplicates()\n", - " # 4. C데이터 프레임에 신규 컬럼(시작 유닉스타임) 생성 후 종료유닉스 타임 값 입력, 종료 유닉스 타임 컬럼 제거\n", - " move = move.rename(columns = {'end_unix':'start_unix'})\n", - " # 5. 이동류 이력정보 READ\n", - " # - CSV 파일로 서버에 저장된 이동류정보를 읽어옴(파일이 없는 경우에는 데이터가 없는 프레임 D 생성)\n", - " try:\n", - " if isinstance(movement, pd.DataFrame): # movement가 존재할 경우 그걸 그대로 씀.\n", - " pass\n", - " else: \n", - " movement = pd.DataFrame()\n", - " except NameError: # movement가 존재하지 않는 경우 생성\n", - " movement = pd.DataFrame()\n", - " # 6. 이동류 이력정보 데이터테이블(D)에 C데이터 add\n", - " movement = pd.concat([movement, move])\n", - " # 7. D데이터 프레임에서 중복데이터 제거(교차로번호, 시작 유닉스타임, A링 현시번호, B링 현시번호 같은 행은 제거)\n", - " movement = movement.drop_duplicates(['inter_no','phas_A','phas_B','start_unix'])\n", - " # 8. D데이터 보관 시간 기준시간을 시작 유닉스 타임의 최대값 - 3600을 값으로 산출하고, 보관 시간 기준시간보다 작은 시작 유닉스 타임을 가진 행은 모두 제거(1시간 데이터만 보관)\n", - " movement = movement[movement.start_unix > fsec - 3600]\n", - " movement = movement.sort_values(by=['start_unix','inter_no','phas_A','phas_B']).reset_index(drop=True)\n", - "\n", - "display(movement)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def make_splits(plan):\n", - " # split, isplit : A,B 분리 혹은 통합시 사용될 수 있는 딕셔너리 \n", - " splits = {} # splits maps (inter_no, start_hour, start_minute) to split \n", - " for i, row in plan.iterrows():\n", - " inter_no = row.inter_no\n", - " start_hour = row.start_hour\n", - " start_minute = row.start_minute\n", - " cycle = row.cycle\n", - " cums_A = row[[f'dura_A{j}' for j in range(1,9)]].cumsum()\n", - " cums_B = row[[f'dura_B{j}' for j in range(1,9)]].cumsum()\n", - " splits[(inter_no, start_hour, start_minute)] = {} # split maps (phas_A, phas_B) to k\n", - " k = 0\n", - " for t in range(cycle):\n", - " new_phas_A = len(cums_A[cums_A < t]) + 1\n", - " new_phas_B = len(cums_B[cums_B < t]) + 1\n", - " if k == 0 or ((new_phas_A, new_phas_B) != (phas_A, phas_B)):\n", - " k += 1\n", - " phas_A = new_phas_A\n", - " phas_B = new_phas_B\n", - " splits[(inter_no, start_hour, start_minute)][(phas_A, phas_B)] = k\n", - "\n", - " isplits = {} # the inverse of splits\n", - " for i in splits:\n", - " isplits[i] = {splits[i][k]:k for k in splits[i]} # isplit maps k to (phas_A, phas_B)\n", - " return splits, isplits\n", - "\n", - "def make_timetable(plan):\n", - " # timetable\n", - " timetable = plan[['start_hour', 'start_minute']].drop_duplicates()\n", - " timetable['start_seconds'] = midnight + timetable['start_hour'] * 3600 + timetable['start_minute'] * 60\n", - " return timetable\n", - "\n", - "# inter2node\n", - "inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - "inter_node = inter_node[inter_node.inter_type=='parent']\n", - "inter2node = dict(zip(inter_node['inter_no'], inter_node['node_id']))\n", - "\n", - "hours = np.array(range(midnight - 7200, next_day + 1, 3600)) # 정각에 해당하는 시각들 목록" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def calculate_DS(rhist, curr_unix, hours, timetable):\n", - " program_starts = np.array(timetable.start_seconds)\n", - " idx = (program_starts <= present_time).sum() - 1\n", - " program_start = program_starts[idx]\n", - " if list(hours[hours <= curr_unix]):\n", - " ghour_lt_curr_unix = hours[hours <= curr_unix].max() # the greatest hour less than or equal to curr_unix\n", - " else:\n", - " ghour_lt_curr_unix = program_start\n", - " start_unixes = rhist.start_unix.unique()\n", - " start_unixes_lt_ghour = np.sort(start_unixes[start_unixes < ghour_lt_curr_unix]) # start unixes less than ghour_lt_curr_unix\n", - " # 기준유닉스(base_unix) : curr_unix보다 작은 hour 중에서 가장 큰 값으로부터 다섯 번째로 작은 start_unix\n", - " if len(start_unixes_lt_ghour) > 5:\n", - " base_unix = start_unixes_lt_ghour[-5]\n", - " # start_unixes_lt_ghour의 길이가 5 미만일 경우에는 맨 앞 start_unix로 base_unix를 지정\n", - " else:\n", - " base_unix = rhist.start_unix.min()\n", - " D_n = curr_unix - base_unix\n", - " S_n_durs = rhist[(rhist.start_unix > base_unix) & (rhist.start_unix <= curr_unix)] \\\n", - " [[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]\n", - " S_n = S_n_durs.values.sum() // 2\n", - " return D_n, S_n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def load_prow(plan, timetable, inter_no, time):\n", - " '''\n", - " load planned row\n", - " '''\n", - " # 프로그램 시작시각\n", - " program_starts = np.array(timetable.start_seconds)\n", - " idx = (program_starts <= time).sum() - 1\n", - " program_start = program_starts[idx]\n", - "\n", - " # 최근 프로그램 시작시각에 대한 신호계획\n", - " start_hour = timetable.iloc[idx].start_hour\n", - " start_minute = timetable.iloc[idx].start_minute\n", - " prow = plan[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", - " return program_start, prow" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def make_rhistory(plan, timetable, history, present_time, adder):\n", - " # 1. 조회시점의 유닉스 타임 이전의 신호이력 수집\n", - " rhistory = history.copy() # recent history\n", - " rhistory = rhistory[(rhistory.end_unix <= present_time) & (rhistory.end_unix > present_time - 9000)] # 두 시간 반 전부터 현재까지의 신호이력을 가져옴. 9000 = 3600 * 2.5\n", - "\n", - " # rhistory에 모든 교차로번호가 존재하지 않으면 해당 교차로번호에 대한 신호이력을 추가함 (at 최근 프로그램 시작시각)\n", - " whole_inter_nos = sorted(history.inter_no.unique())\n", - " recent_inter_nos = sorted(rhistory.inter_no.unique())\n", - " if not whole_inter_nos==recent_inter_nos:\n", - " for inter_no in set(whole_inter_nos) - set(recent_inter_nos):\n", - " program_start, prow = load_prow(plan, timetable, inter_no, present_time - 9000)\n", - " cycle = prow.cycle.iloc[0]\n", - " row1 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", - " row2 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", - " # prow에서 필요한 부분을 rhistory에 추가\n", - " row1['end_unix'] = program_start\n", - " row2['end_unix'] = program_start + cycle\n", - " rhistory = pd.concat([rhistory, row1, row2]).reset_index(drop=True)\n", - " # present_time + adder 의 시각에 한 주기의 신호 추가\n", - " for inter_no in set(whole_inter_nos):\n", - " program_start, prow = load_prow(plan, timetable, inter_no, present_time)\n", - " cycle = prow.cycle.iloc[0]\n", - " row3 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", - " # prow에서 필요한 부분을 rhistory에 추가\n", - " row3['end_unix'] = present_time + adder\n", - " rhistory = pd.concat([rhistory, row3]).reset_index(drop=True)\n", - "\n", - " # 2. 시작 유닉스 타임컬럼 생성 후 종류 유닉스 타임에서 현시별 현시기간 컬럼의 합을 뺀 값으로 입력\n", - " # - 현시시간의 합을 뺀 시간의 +- 10초 이내에 이전 주기정보가 존재하면 그 유닉스 시간을 시작 유닉스시간 값으로 하고, 존재하지 않으면 현시시간의 합을 뺀 유닉스 시간을 시작 유닉스 시간으로 지정\n", - " for i, row in rhistory.iterrows():\n", - " inter_no = row.inter_no\n", - " end_unix = row.end_unix\n", - " elapsed_time = row[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]].sum() // 2 # 현시시간 합\n", - " # 이전 유닉스 존재하지 않음 : 현시시간 합의 차\n", - " start_unix = end_unix - elapsed_time\n", - " pre_rows = history[:i] # previous rows\n", - " if inter_no in pre_rows.inter_no.unique(): # 이전 유닉스 존재\n", - " pre_unix = pre_rows[pre_rows.inter_no == inter_no]['end_unix'].iloc[-1] # previous unix time\n", - " # 이전 유닉스 존재, abs < 10 : 이전 유닉스\n", - " if abs(pre_unix - start_unix) < 10:\n", - " start_unix = pre_unix\n", - " # 이전 유닉스 존재, abs >=10 : 현시시간 합의 차\n", - " else:\n", - " pass\n", - " rhistory.loc[i, 'start_unix'] = start_unix \n", - " rhistory[rhistory.isna()] = 0\n", - " rhistory['start_unix'] = rhistory['start_unix'].astype(int)\n", - " rhistory = rhistory[['inter_no', 'start_unix'] + [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)] + ['cycle']]\n", - " return rhistory\n", - "adder = 600" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def processing(plan, rhistory, timetable, hours):\n", - " rhists = []\n", - " for inter_no in sorted(rhistory.inter_no.unique()):\n", - " rhist = rhistory.copy()[rhistory.inter_no==inter_no]\n", - " rhist = rhist.drop_duplicates(subset=['start_unix']).reset_index(drop=True)\n", - "\n", - " # D_n 및 S_n 값 정의\n", - " rhist['D_n'] = 0 # D_n : 시간차이\n", - " rhist['S_n'] = 0 # S_n : 현시시간합\n", - " for n in range(len(rhist)):\n", - " curr_unix = rhist.iloc[n].start_unix # current start_unix\n", - " rhist.loc[n, ['D_n', 'S_n']] = calculate_DS(rhist, curr_unix, hours, timetable)\n", - "\n", - " # 이전시각, 현재시각\n", - " prev_unix = rhist.loc[0, 'start_unix'] # previous start_unix\n", - " curr_unix = rhist.loc[1, 'start_unix'] # current start_unix\n", - "\n", - " # rhist의 마지막 행에 도달할 때까지 반복\n", - " while True:\n", - " n = rhist[rhist.start_unix==curr_unix].index[0]\n", - " cycle = rhist.loc[n, 'cycle']\n", - " D_n = rhist.loc[n, 'D_n']\n", - " S_n = rhist.loc[n, 'S_n']\n", - " # 참값인 경우\n", - " if (abs(D_n - S_n) <= 5):\n", - " pass\n", - " # 참값이 아닌 경우\n", - " else:\n", - " # 2-1-1. 결측치 처리 : 인접한 두 start_unix의 차이가 계획된 주기의 두 배보다 크면 결측이 일어났다고 판단, 신호계획의 현시시간으로 \"대체\"\n", - " if curr_unix - prev_unix >= 2 * cycle:\n", - " # prev_unix를 계획된 주기만큼 늘려가면서 한 행씩 채워나간다.\n", - " # (curr_unix와의 차이가 계획된 주기보다 작거나 같아질 때까지)\n", - " while curr_unix - prev_unix > cycle:\n", - " prev_unix += cycle\n", - " # 신호 계획(prow) 불러오기\n", - " start_seconds = np.array(timetable.start_seconds)\n", - " idx = (start_seconds <= prev_unix).sum() - 1\n", - " start_hour = timetable.iloc[idx].start_hour\n", - " start_minute = timetable.iloc[idx].start_minute\n", - " prow = plan.copy()[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", - " # prow에서 필요한 부분을 rhist에 추가\n", - " prow['start_unix'] = prev_unix\n", - " prow = prow.drop(['start_hour', 'start_minute', 'offset'], axis=1)\n", - " cycle = prow.iloc[0].cycle\n", - " rhist = pd.concat([rhist, prow])\n", - " rhist = rhist.sort_values(by='start_unix').reset_index(drop=True)\n", - " n += 1\n", - "\n", - " # 2-1-2. 이상치 처리 : 비율에 따라 해당 행을 \"삭제\"(R_n <= 0.5) 또는 \"조정\"(R_n > 0.5)한다\n", - " R_n = (curr_unix - prev_unix) / cycle # R_n : 비율\n", - " # R_n이 0.5보다 작거나 같으면 해당 행을 삭제\n", - " if R_n <= 0.5:\n", - " rhist = rhist.drop(index=n).reset_index(drop=True)\n", - " if n >= rhist.index[-1]:\n", - " break\n", - " # 행삭제에 따른 curr_unix, R_n 재정의\n", - " curr_unix = rhist.loc[n, 'start_unix']\n", - " R_n = (curr_unix - prev_unix) / cycle # R_n : 비율\n", - "\n", - " # R_n이 0.5보다 크면 해당 행 조정 (비율을 유지한 채로 현시시간 대체)\n", - " if R_n > 0.5:\n", - " # 신호 계획(prow) 불러오기\n", - " start_seconds = np.array(timetable.start_seconds)\n", - " idx = (start_seconds <= curr_unix).sum() - 1\n", - " start_hour = timetable.iloc[idx].start_hour\n", - " start_minute = timetable.iloc[idx].start_minute\n", - " prow = plan[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", - " # 조정된 현시시간 (prow에 R_n을 곱하고 정수로 바꿈)\n", - " adjusted_dur = prow.copy()[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] * R_n\n", - " int_parts = adjusted_dur.iloc[0].apply(lambda x: int(x))\n", - " frac_parts = adjusted_dur.iloc[0] - int_parts\n", - " difference = round(adjusted_dur.iloc[0].sum()) - int_parts.sum()\n", - " for _ in range(difference): # 소수 부분이 가장 큰 상위 'difference'개의 값에 대해 올림 처리\n", - " max_frac_index = frac_parts.idxmax()\n", - " int_parts[max_frac_index] += 1\n", - " frac_parts[max_frac_index] = 0 # 이미 처리된 항목은 0으로 설정\n", - " # rhist에 조정된 현시시간을 반영\n", - " rhist.loc[n, [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] = int_parts.values\n", - " rhist.loc[n, 'cycle'] = int_parts.sum().sum() // 2\n", - "\n", - " if n >= rhist.index[-1]:\n", - " break\n", - " prev_unix = curr_unix\n", - " curr_unix = rhist.loc[n+1, 'start_unix']\n", - "\n", - " # 생략해도 무방할 코드\n", - " rhist = rhist.reset_index(drop=True)\n", - " rhist = rhist.sort_values(by=['start_unix'])\n", - "\n", - " # D_n 및 S_n 값 재정의\n", - " for n in range(len(rhist)):\n", - " curr_unix = rhist.iloc[n].start_unix # current start_unix\n", - " rhist.loc[n, ['D_n', 'S_n']] = calculate_DS(rhist, curr_unix, hours, timetable)\n", - " rhists.append(rhist)\n", - " rhists = pd.concat(rhists).sort_values(by=['start_unix','inter_no'])\n", - " rhists = rhists[rhists.start_unix >= present_time - 3600]\n", - " rhists = rhists.drop(columns=['D_n', 'S_n'])\n", - " return rhists" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def make_hrhists(rhists, isplits, timetable):\n", - " # 계층화된 형태로 변환\n", - " hrhists = [] # hierarchied recent history\n", - " for i, row in rhists.iterrows():\n", - " inter_no = row.inter_no\n", - " start_unix = row.start_unix\n", - "\n", - " ind = (timetable['start_seconds'] <= row.start_unix).sum() - 1\n", - " start_hour = timetable.iloc[ind].start_hour\n", - " start_minute = timetable.iloc[ind].start_minute\n", - " isplit = isplits[(inter_no, start_hour, start_minute)]\n", - " phas_As = [isplit[j][0] for j in isplit.keys()]\n", - " phas_Bs = [isplit[j][1] for j in isplit.keys()]\n", - " durs_A = row[[f'dura_A{j}' for j in range(1,9)]]\n", - " durs_B = row[[f'dura_B{j}' for j in range(1,9)]]\n", - " durations = []\n", - " for j in range(1, len(isplit)+1):\n", - " ja = isplit[j][0]\n", - " jb = isplit[j][1]\n", - " if ja == jb:\n", - " durations.append(min(durs_A[ja-1], durs_B[jb-1]))\n", - " else:\n", - " durations.append(abs(durs_A[ja-1] - durs_B[ja-1]))\n", - " new_rows = pd.DataFrame({'inter_no':[inter_no] * len(durations), 'start_unix':[start_unix] * len(durations),\n", - " 'phas_A':phas_As, 'phas_B':phas_Bs, 'duration':durations})\n", - " hrhists.append(new_rows)\n", - " hrhists = pd.concat(hrhists)\n", - " hrhists = hrhists.sort_values(by = ['start_unix', 'inter_no', 'phas_A', 'phas_B']).reset_index(drop=True)\n", - " return hrhists" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def update_movement(hrhists, movement, movements):\n", - " # 중복을 제거하고 (inter_no, start_unix) 쌍을 만듭니다.\n", - " hrhists_inter_unix = set(hrhists[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None))\n", - " movement_inter_unix = set(movement[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None))\n", - "\n", - " # hrhists에는 있지만 movement에는 없는 (inter_no, start_unix) 쌍을 찾습니다.\n", - " missing_in_movement = hrhists_inter_unix - movement_inter_unix\n", - "\n", - " # 새로운 행들을 생성합니다.\n", - " new_rows = []\n", - " if missing_in_movement:\n", - " for inter_no, start_unix in missing_in_movement:\n", - " # movements에서 해당 inter_no의 데이터를 찾습니다.\n", - " new_row = movements[movements['inter_no'] == inter_no].copy()\n", - " # start_unix 값을 설정합니다.\n", - " new_row['start_unix'] = start_unix\n", - " new_rows.append(new_row)\n", - "\n", - " # 새로운 데이터프레임을 생성하고 기존 movement 데이터프레임과 합칩니다.\n", - " new_movement = pd.concat(new_rows, ignore_index=True)\n", - " movement_updated = pd.concat([movement, new_movement], ignore_index=True)\n", - " else:\n", - " movement_updated = movement\n", - " return movement_updated" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def make_histid(present_time, hrhists, movement_updated, inter2node, matching):\n", - " # movements and durations\n", - " movedur = pd.merge(hrhists, movement_updated, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B'])\n", - " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", - " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", - "\n", - " # 이동류 매칭 테이블에서 진입id, 진출id를 가져와서 붙임.\n", - " for i, row in movedur.iterrows():\n", - " inter_no = row.inter_no\n", - " start_unix = row.start_unix\n", - " # incoming and outgoing edges A\n", - " move_A = row.move_A\n", - " if move_A in [17, 18]:\n", - " inc_edge_A = np.nan\n", - " outhedge_A = np.nan\n", - " else:\n", - " match_A = matching[(matching.inter_no == inter_no) & (matching.move_no == move_A)].iloc[0]\n", - " inc_edge_A = match_A.inc_edge\n", - " out_edge_A = match_A.out_edge\n", - " movedur.loc[i, ['inc_edge_A', 'out_edge_A']] = [inc_edge_A, out_edge_A]\n", - " # incoming and outgoing edges B\n", - " move_B = row.move_B\n", - " if move_B in [17, 18]:\n", - " inc_edge_B = np.nan\n", - " out_edge_B = np.nan\n", - " else:\n", - " match_B = matching[(matching.inter_no == inter_no) & (matching.move_no == move_B)].iloc[0]\n", - " inc_edge_B = match_B.inc_edge\n", - " out_edge_B = match_B.out_edge\n", - " movedur.loc[i, ['inc_edge_B', 'out_edge_B']] = [inc_edge_B, out_edge_B]\n", - "\n", - " # 이동류 컬럼 제거\n", - " movedur = movedur.drop(['move_A', 'move_B'], axis=1)\n", - "\n", - " histid = movedur.copy() # history with edge ids (incoming and outgoing edge ids)\n", - " histid['node_id'] = histid['inter_no'].map(inter2node)\n", - " histid = histid[['inter_no', 'node_id', 'start_unix', 'phas_A', 'phas_B', 'duration', 'inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']]\n", - " histid_start = present_time - 600\n", - " histid = histid[histid.start_unix > histid_start]\n", - " return histid" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def preprocess(m):\n", - " '''\n", - " 통합테이블(histid)를 만드는 함수\n", - "\n", - " input : m\n", - " - m ranges from 0 to 287, but 0 makes an error where 288 = 86400//300\n", - " - present_time = fmins[m] : 현재시점\n", - "\n", - " output : histid (통합테이블, HISTory with edge_IDs)\n", - " - 컬럼 : inter_no, node_id, start_unix, phas_A, phas_B, duration, inc_edge_A, out_edge_A, inc_edge_B, out_edge_B\n", - "\n", - " 주요 데이터, 중간산출물 및 결과물 :\n", - " # 데이터\n", - " - history : 신호이력 (inter_no, end_unix, dura_Aj, dura_Bj, cycle, offset)\n", - " - plan : 신호계획 (inter_no, start_hour, start_minute, dura_Aj, dura_Bj cycle, offset)\n", - " # 중간산출물\n", - " - rhists (recent history)\n", - " - history에서 현재 시각 이전의 데이터를 가져옴.\n", - " - end_unix를 start_unix로 변환\n", - " - 참값판단 프로세스(결측·이상치 처리)\n", - " - 컬럼 : inter_no, start_unix, dura_Aj, dura_Bj, cycle\n", - " - hrhists (hierarchized recent history)\n", - " - rhists를 계층화\n", - " - 컬럼 : inter_no, start_unix, phas_A, phas_B, duration\n", - " - movements\n", - " - 각 교차로에 대하여 현시별로 이동류를 정해놓음.\n", - " - join시 사용하기 위함.\n", - " - 한 번 만들어놓고 두고두고 사용함.\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B\n", - " - movement\n", - " - 현재 시점에서의 이동류정보\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix\n", - " - movement_updated\n", - " - movement와 hrhists를 join하기 전에, movement에는 없지만 hrhists에는 있는 start_unix에 대한 이동류 정보를 가져와 movement에 붙임\n", - " - 이동류정보는 앞서 정의한 movements에서 가져옴.\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix\n", - " - movedur\n", - " - hrhists와 movement_updated를 join\n", - " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix, duration\n", - " # 결과 : histid\n", - " - 신호생성에 직접적으로 사용되는 데이터프레임\n", - " - 컬럼 : inter_no, node_id, start_unix, phas_A, phas_B, duration, inc_edge_A, out_edge_A, inc_edge_B, out_edge_B\n", - " - 한글컬럼 : 교차로번호, 노드id, 시작유닉스, A현시번호, B현시번호, 현시시간, 진입엣지(A), 진출엣지(A), 진입엣지(B), 진출엣지(B)\n", - " '''\n", - " midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", - " next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", - " fmins = range(midnight, next_day, 300) # fmins : unix time by Five MINuteS\n", - " # 현재시각\n", - " present_time = fmins[m]\n", - " print(datetime.fromtimestamp(present_time))\n", - " # 사용할 표준 테이블 목록\n", - " plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", - " history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", - " matching = pd.read_csv('../Intermediates/matching.csv', index_col=0)\n", - " # 참고할 딕셔너리, 데이터프레임, 리스트 등 목록\n", - " splits, isplits = make_splits(plan)\n", - " timetable = make_timetable(plan)\n", - " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - " inter_node = inter_node[inter_node.inter_type=='parent']\n", - " inter2node = dict(zip(inter_node['inter_no'], inter_node['node_id']))\n", - " hours = np.array(range(midnight - 7200, next_day + 1, 3600)) # 정각에 해당하는 시각들 목록\n", - " # rhistory, rhists, hrhists\n", - " adder = 600\n", - " rhistory = make_rhistory(plan, timetable, history, present_time, adder)\n", - " rhists = processing(plan, rhistory, timetable, hours)\n", - " hrhists = make_hrhists(rhists, isplits, timetable)\n", - " # movements, movement, movement_updated\n", - " movements = pd.read_csv('../Intermediates/movements.csv')\n", - " movement = pd.read_csv(f'../Intermediates/movement/movement_{present_time}.csv', index_col=0)\n", - " movement_updated = update_movement(hrhists, movement, movements)\n", - " # movedur\n", - " movedur = pd.merge(movement_updated, hrhists, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B']) # movements and durations\n", - " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", - " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", - " # histid\n", - " histid = make_histid(present_time, hrhists, movement_updated, inter2node, matching)\n", - " histid.to_csv(f'../Intermediates/histid/histid_{fmins[m]}.csv')\n", - " return histid" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-01-05 08:45:00\n", - "2024-01-05 08:50:00\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nonode_idstart_unixphas_Aphas_Bdurationinc_edge_Aout_edge_Ainc_edge_Bout_edge_B
655202i917044116101146571510152_02-571510152_01571510152_01571510152_01.65
656202i9170441161022114NaN-571510152_01NaNNaN
657175i017044116291140-571542797_02571500487_01-571500487_01571542797_02
658175i017044116292242-571500487_01571545870_01-571542797_02571510153_01
659175i017044116293329571545870_02571510153_01571545870_02571542797_02
.................................
871201i817044126405517571500583_01571500617_01571500583_01571500569_01
872206i717044126601125-571511538_02571542073_02571542073_01571511538_02
873206i717044126602225NaN571542073_02NaNNaN
874206i717044126603315-571511538_02571542073_02571542073_01571511538_02
875206i717044126604415NaN571542073_02NaNNaN
\n", - "

221 rows × 10 columns

\n", - "
" - ], - "text/plain": [ - " inter_no node_id start_unix phas_A phas_B duration inc_edge_A \\\n", - "655 202 i9 1704411610 1 1 46 571510152_02 \n", - "656 202 i9 1704411610 2 2 114 NaN \n", - "657 175 i0 1704411629 1 1 40 -571542797_02 \n", - "658 175 i0 1704411629 2 2 42 -571500487_01 \n", - "659 175 i0 1704411629 3 3 29 571545870_02 \n", - ".. ... ... ... ... ... ... ... \n", - "871 201 i8 1704412640 5 5 17 571500583_01 \n", - "872 206 i7 1704412660 1 1 25 -571511538_02 \n", - "873 206 i7 1704412660 2 2 25 NaN \n", - "874 206 i7 1704412660 3 3 15 -571511538_02 \n", - "875 206 i7 1704412660 4 4 15 NaN \n", - "\n", - " out_edge_A inc_edge_B out_edge_B \n", - "655 -571510152_01 571510152_01 571510152_01.65 \n", - "656 -571510152_01 NaN NaN \n", - "657 571500487_01 -571500487_01 571542797_02 \n", - "658 571545870_01 -571542797_02 571510153_01 \n", - "659 571510153_01 571545870_02 571542797_02 \n", - ".. ... ... ... \n", - "871 571500617_01 571500583_01 571500569_01 \n", - "872 571542073_02 571542073_01 571511538_02 \n", - "873 571542073_02 NaN NaN \n", - "874 571542073_02 571542073_01 571511538_02 \n", - "875 571542073_02 NaN NaN \n", - "\n", - "[221 rows x 10 columns]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "preprocess(105)\n", - "preprocess(106)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# for m in range(30, 288):\n", - "# print(m)\n", - "# histid = preprocess(m)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "rts", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Archives/Scripts/preprocess_daily.ipynb b/Archives/Scripts/preprocess_daily.ipynb deleted file mode 100644 index 6d55265e6..000000000 --- a/Archives/Scripts/preprocess_daily.ipynb +++ /dev/null @@ -1,1687 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import os\n", - "import json\n", - "import sumolib\n", - "from tqdm import tqdm" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "loading_dtype = {\n", - " 'inter_no':'int', 'start_hour':'int', 'start_minute':'int', 'cycle':'int','offset':'int',\n", - " 'node_id':'str', 'inter_type':'str', 'parent_id':'str','child_id':'str',\n", - " 'direction':'str', 'condition':'str', 'inc_edge':'str', 'out_edge':'str',\n", - " 'end_unix':'int', 'inter_name':'str', 'inter_lat':'float', 'inter_lon':'float',\n", - " 'group_no':'int', 'main_phase_no':'int', 'phase_no':'int','ring_type':'str'\n", - " }\n", - "for alph in ['A', 'B']:\n", - " for j in range(1,9):\n", - " loading_dtype[f'angle_{alph}{j}'] = 'str'\n", - " loading_dtype[f'dura_{alph}{j}'] = 'int'" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['match1.csv', 'match6.csv', 'matching.csv', 'movement', 'node2num_cycles.json']\n" - ] - } - ], - "source": [ - "path_intermediates = '../../Intermediates'\n", - "files = os.listdir(path_intermediates)\n", - "print(files)\n", - "\n", - "match1 = pd.read_csv(os.path.join(path_intermediates, 'match1.csv'), index_col=0)\n", - "match6 = pd.read_csv(os.path.join(path_intermediates, 'match6.csv'), index_col=0)\n", - "matching = pd.read_csv(os.path.join(path_intermediates, 'matching.csv'), index_col=0)\n", - "nema = pd.read_csv(os.path.join('../../Data/tables', 'nema.csv'), encoding='cp949')\n", - "plan = pd.read_csv(os.path.join('../../Data/tables', 'plan.csv'), index_col=0)\n", - "angle = pd.read_csv(os.path.join('../../Data/tables', 'angle.csv'), index_col=0, dtype = loading_dtype)\n", - "inter_node = pd.read_csv(os.path.join('../../Data/tables', 'inter_node.csv'), index_col=0)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nonode_idinter_type
0175i0parent
1175u00child
2176i1parent
3177i2parent
4177u20child
5178i3parent
6178u30child
7178u31child
8178u32child
9201i8parent
10202i9parent
11206i7parent
12210i6parent
13210u60child
14178c30child
\n", - "
" - ], - "text/plain": [ - " inter_no node_id inter_type\n", - "0 175 i0 parent\n", - "1 175 u00 child\n", - "2 176 i1 parent\n", - "3 177 i2 parent\n", - "4 177 u20 child\n", - "5 178 i3 parent\n", - "6 178 u30 child\n", - "7 178 u31 child\n", - "8 178 u32 child\n", - "9 201 i8 parent\n", - "10 202 i9 parent\n", - "11 206 i7 parent\n", - "12 210 i6 parent\n", - "13 210 u60 child\n", - "14 178 c30 child" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inter_node[]" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
move_noinc_dirout_dir
01
12
23
34
45
56
67
78
89북동남동
910남서북동
1011남동남서
1112북서남동
1213남서북서
1314북동남서
1415북서북동
1516남동북서
1617NaNNaN
1718NaNNaN
1821NaNNaN
\n", - "
" - ], - "text/plain": [ - " move_no inc_dir out_dir\n", - "0 1 동 남\n", - "1 2 서 동\n", - "2 3 남 서\n", - "3 4 북 남\n", - "4 5 서 북\n", - "5 6 동 서\n", - "6 7 북 동\n", - "7 8 남 북\n", - "8 9 북동 남동\n", - "9 10 남서 북동\n", - "10 11 남동 남서\n", - "11 12 북서 남동\n", - "12 13 남서 북서\n", - "13 14 북동 남서\n", - "14 15 북서 북동\n", - "15 16 남동 북서\n", - "16 17 NaN NaN\n", - "17 18 NaN NaN\n", - "18 21 NaN NaN" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nostart_hourstart_minutedura_A1dura_A2dura_A3dura_A4dura_A5dura_A6dura_A7dura_A8dura_B1dura_B2dura_B3dura_B4dura_B5dura_B6dura_B7dura_B8
4176003773400000037734000000
5176703793400000037934000000
617690371034000000371034000000
71761830371134000000371134000000
\n", - "
" - ], - "text/plain": [ - " inter_no start_hour start_minute dura_A1 dura_A2 dura_A3 dura_A4 \\\n", - "4 176 0 0 37 73 40 0 \n", - "5 176 7 0 37 93 40 0 \n", - "6 176 9 0 37 103 40 0 \n", - "7 176 18 30 37 113 40 0 \n", - "\n", - " dura_A5 dura_A6 dura_A7 dura_A8 dura_B1 dura_B2 dura_B3 dura_B4 \\\n", - "4 0 0 0 0 37 73 40 0 \n", - "5 0 0 0 0 37 93 40 0 \n", - "6 0 0 0 0 37 103 40 0 \n", - "7 0 0 0 0 37 113 40 0 \n", - "\n", - " dura_B5 dura_B6 dura_B7 dura_B8 \n", - "4 0 0 0 0 \n", - "5 0 0 0 0 \n", - "6 0 0 0 0 \n", - "7 0 0 0 0 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_noangle_A1angle_A2angle_A3angle_A4angle_A5angle_A6angle_A7angle_A8angle_B1angle_B2angle_B3angle_B4angle_B5angle_B6angle_B7angle_B8
1176180000180000270356NaNNaNNaNNaNNaN359180180270NaNNaNNaNNaNNaNNaN
\n", - "
" - ], - "text/plain": [ - " inter_no angle_A1 angle_A2 angle_A3 angle_A4 angle_A5 angle_A6 angle_A7 \\\n", - "1 176 180000 180000 270356 NaN NaN NaN NaN \n", - "\n", - " angle_A8 angle_B1 angle_B2 angle_B3 angle_B4 angle_B5 angle_B6 angle_B7 \\\n", - "1 NaN 359180 180270 NaN NaN NaN NaN NaN \n", - "\n", - " angle_B8 \n", - "1 NaN " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nonode_idinter_type
2176i1parent
\n", - "
" - ], - "text/plain": [ - " inter_no node_id inter_type\n", - "2 176 i1 parent" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nophas_Aphas_Bmove_Amove_B
51761184
61762283
717633518
\n", - "
" - ], - "text/plain": [ - " inter_no phas_A phas_B move_A move_B\n", - "5 176 1 1 8 4\n", - "6 176 2 2 8 3\n", - "7 176 3 3 5 18" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nophase_noring_typemove_noinc_dirout_dirinc_angleout_angleinc_edgeout_edgenode_id
81761A8180.00.0-571542810_01-571542797_02.99i1
91761B4359.0180.0571542797_02.99571542810_01i1
101762A8180.00.0-571542810_01-571542797_02.99i1
111762B3180.0270.0-571542810_01571543469_01i1
121763A5270.0356.0571543469_02-571542797_02.99i1
131763B18NaNNaNNaNNaNNaNNaNi1
\n", - "
" - ], - "text/plain": [ - " inter_no phase_no ring_type move_no inc_dir out_dir inc_angle \\\n", - "8 176 1 A 8 남 북 180.0 \n", - "9 176 1 B 4 북 남 359.0 \n", - "10 176 2 A 8 남 북 180.0 \n", - "11 176 2 B 3 남 서 180.0 \n", - "12 176 3 A 5 서 북 270.0 \n", - "13 176 3 B 18 NaN NaN NaN \n", - "\n", - " out_angle inc_edge out_edge node_id \n", - "8 0.0 -571542810_01 -571542797_02.99 i1 \n", - "9 180.0 571542797_02.99 571542810_01 i1 \n", - "10 0.0 -571542810_01 -571542797_02.99 i1 \n", - "11 270.0 -571542810_01 571543469_01 i1 \n", - "12 356.0 571543469_02 -571542797_02.99 i1 \n", - "13 NaN NaN NaN i1 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
inter_nomove_noinc_dirout_dirinc_edgeout_edgenode_id
131763-571542810_01571543469_01i1
141764571542797_02.99571542810_01i1
151765571543469_02-571542797_02.99i1
161768-571542810_01-571542797_02.99i1
1717621571542797_02.99571543469_01i1
1817621571543469_02571542810_01i1
\n", - "
" - ], - "text/plain": [ - " inter_no move_no inc_dir out_dir inc_edge out_edge \\\n", - "13 176 3 남 서 -571542810_01 571543469_01 \n", - "14 176 4 북 남 571542797_02.99 571542810_01 \n", - "15 176 5 서 북 571543469_02 -571542797_02.99 \n", - "16 176 8 남 북 -571542810_01 -571542797_02.99 \n", - "17 176 21 북 서 571542797_02.99 571543469_01 \n", - "18 176 21 서 남 571543469_02 571542810_01 \n", - "\n", - " node_id \n", - "13 i1 \n", - "14 i1 \n", - "15 i1 \n", - "16 i1 \n", - "17 i1 \n", - "18 i1 " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "inter_no = 176\n", - "display(nema)\n", - "display(plan[plan.inter_no==inter_no].iloc[:,:-2])\n", - "display(angle[angle.inter_no==inter_no])\n", - "display(inter_node[inter_node.inter_no==inter_no])\n", - "display(match1[match1.inter_no==inter_no])\n", - "display(match6[match6.inter_no==inter_no])\n", - "display(matching[matching.inter_no==inter_no])" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match1(path_move):\n", - " '''\n", - " 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다.\n", - " '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다.\n", - "\n", - " return : 통합된 이동류정보\n", - " - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보\n", - "\n", - " match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다.\n", - " '''\n", - " # [이동류번호] 불러오기 (약 1분의 소요시간)\n", - " csv_moves = os.listdir(path_move)\n", - " moves = [pd.read_csv(path_move + csv_move, index_col=0) for csv_move in tqdm(csv_moves)]\n", - " match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True)\n", - " match1.to_csv('../Intermediates/match1.csv')\n", - " return match1" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match2(match1):\n", - " '''\n", - " match1을 계층화함.\n", - " - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B\n", - " - match2의 컬럼 : inter_no, phase_no, ring_type, move_no\n", - " '''\n", - " # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no')\n", - " matchA = match1[['inter_no', 'phas_A', 'move_A']].copy()\n", - " matchA.columns = ['inter_no', 'phase_no', 'move_no']\n", - " matchA['ring_type'] = 'A'\n", - " matchB = match1[['inter_no', 'phas_B', 'move_B']].copy()\n", - " matchB.columns = ['inter_no', 'phase_no', 'move_no']\n", - " matchB['ring_type'] = 'B'\n", - " match2 = pd.concat([matchA, matchB]).drop_duplicates()\n", - " match2 = match2[['inter_no', 'phase_no', 'ring_type', 'move_no']]\n", - " match2 = match2.sort_values(by=list(match2.columns))\n", - " return match2" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match3(match2, nema):\n", - " '''\n", - " 각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함.\n", - " - match2의 컬럼 : inter_no, phase_no, ring_type, move_no\n", - " - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir\n", - "\n", - " nema : \n", - " - 컬럼 : move_no, inc_dir, out_dir\n", - " - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블\n", - " - 이동류번호 : 1 ~ 16, 17, 18, 21\n", - " - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서\n", - " '''\n", - " # nema 정보 불러오기 및 병합\n", - " match3 = pd.merge(match2, nema, how='left', on='move_no').drop_duplicates()\n", - " return match3" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match4(match3, angle):\n", - " '''\n", - " 방위각 정보를 매칭시켜 추가함.\n", - " - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir\n", - " - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle\n", - "\n", - " angle_original : \n", - " - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8)\n", - " - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블\n", - " - 이동류번호 : 1 ~ 16, 17, 18, 21\n", - " - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서\n", - " '''\n", - "\n", - " # 계층화\n", - " angles = []\n", - " for i, row in angle.iterrows():\n", - " angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]\n", - " new = pd.DataFrame({'inter_no':[row.inter_no] * 16, 'phase_no':list(range(1, 9))*2, 'ring_type':['A'] * 8 + ['B'] * 8, 'angle_code':angle_codes.to_list()})\n", - " angles.append(new)\n", - " angles = pd.concat(angles)\n", - " angles = angles.dropna().reset_index(drop=True)\n", - "\n", - " # 병합\n", - " six_chars = angles.angle_code.apply(lambda x:len(x)==6)\n", - " angles.loc[six_chars,'inc_angle'] = angles.angle_code.apply(lambda x:x[:3])\n", - " angles.loc[six_chars,'out_angle'] = angles.angle_code.apply(lambda x:x[3:])\n", - " angles = angles.drop('angle_code', axis=1)\n", - " match4 = pd.merge(match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'],\n", - " right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates()\n", - " return match4" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match5(match4, net, inter_node, inter_info):\n", - " '''\n", - " 진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로).\n", - " - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle\n", - " - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id\n", - " \n", - " 사용된 데이터 : \n", - " (1) net\n", - " - 성남시 정자동 부근의 샘플 네트워크\n", - " (2) inter_node\n", - " - 교차로번호와 노드id를 매칭시키는 테이블.\n", - " - parent/child 정보도 포함되어 있음\n", - " - 컬럼 : inter_no, node_id, inter_type\n", - " (3) inter_info\n", - " - 교차로 정보. 여기에서는 위도와 경도가 쓰임.\n", - " - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no\n", - "\n", - " 진입엣지id, 진출엣지id를 얻는 과정 :\n", - " - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함.\n", - " * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음\n", - " - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장.\n", - " * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge\n", - " - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄.\n", - " - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄.\n", - " - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함.\n", - " '''\n", - "\n", - " # parent node만 가져옴.\n", - " inter_node1 = inter_node[inter_node.inter_type == 'parent'].drop('inter_type', axis=1)\n", - " inter_info1 = inter_info[['inter_no', 'inter_lat', 'inter_lon']]\n", - " inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'],\n", - " right_on=['inter_no']).drop_duplicates()\n", - "\n", - " inter2node = dict(zip(inter['inter_no'], inter['node_id']))\n", - "\n", - " match5 = match4.copy()\n", - " # 진입진출ID 매칭\n", - " for index, row in match5.iterrows():\n", - " node_id = inter2node[row.inter_no]\n", - " node = net.getNode(node_id)\n", - " # 교차로의 모든 (from / to) edges\n", - " inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges\n", - " out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges\n", - " # 교차로의 모든 (from / to) directions\n", - " inc_dirs = []\n", - " for inc_edge in inc_edges:\n", - " start = inc_edge.getShape()[-2]\n", - " end = inc_edge.getShape()[-1]\n", - " inc_dir = np.array(end) - np.array(start)\n", - " inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5\n", - " inc_dirs.append(inc_dir)\n", - " out_dirs = []\n", - " for out_edge in out_edges:\n", - " start = out_edge.getShape()[0]\n", - " end = out_edge.getShape()[1]\n", - " out_dir = np.array(end) - np.array(start)\n", - " out_dir = out_dir / (out_dir ** 2).sum() ** 0.5\n", - " out_dirs.append(out_dir)\n", - " # 진입각, 진출각 불러오기\n", - " if not pd.isna(row.inc_angle):\n", - " inc_angle = int(row.inc_angle)\n", - " out_angle = int(row.out_angle)\n", - " # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환\n", - " inc_angle = (-90 - inc_angle) % 360\n", - " inc_angle = inc_angle * np.pi / 180.\n", - " inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)])\n", - " out_angle = (90 - out_angle) % 360\n", - " out_angle = out_angle * np.pi / 180.\n", - " out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)])\n", - " # 매칭 엣지 반환\n", - " inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax()\n", - " out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax()\n", - " inc_edge_id = inc_edges[inc_index].getID()\n", - " out_edge_id = out_edges[out_index].getID()\n", - " match5.at[index, 'inc_edge'] = inc_edge_id\n", - " match5.at[index, 'out_edge'] = out_edge_id\n", - " match5['node_id'] = match5['inter_no'].map(inter2node)\n", - " match5 = match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True)\n", - " return match5" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def make_match6(match5, inter_node, uturn, coord):\n", - " '''\n", - " 진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로).\n", - " - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id\n", - " \n", - " 사용된 데이터 : \n", - " (1) inter_node\n", - " - 교차로번호와 노드id를 매칭시키는 테이블.\n", - " - parent/child 정보도 포함되어 있음\n", - " - 컬럼 : inter_no, node_id, inter_type\n", - " (2) uturn (유턴정보)\n", - " - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge\n", - " - parent_id, child_id : 주교차로id, 유턴교차로id\n", - " - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향)\n", - " - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나\n", - " - inc_edge, out_edge : 유턴에 대한 진입진출엣지\n", - " (3) coord (연동교차로정보)\n", - " - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge\n", - " - parent_id, child_id : 주교차로id, 연동교차로id\n", - " - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지\n", - "\n", - " 설명 :\n", - " - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음.\n", - " 여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함.\n", - " 유턴교차로 :\n", - " - directions를 정북기준 시계방향의 8방위로 정함.\n", - " - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음.\n", - " - 예) 진입방향(direction)이 '북'일 때, \n", - " - 직진 : (북, 남)\n", - " * 남 : directions[(ind + 4) % len(directions)]\n", - " - 좌회전 : (북, 동)\n", - " * 동 : directions[(ind + 2) % len(directions)]\n", - " - 보행 : (서, 동)\n", - " * 서 : directions[(ind - 2) % len(directions)]\n", - " - uturn의 각 행을 순회하면서 아래 과정을 반복함\n", - " - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch).\n", - " - condition 별로 진입방향, 진출방향A, 진출방향B 정함.\n", - " - 상술한 directions를 활용하여 정함.\n", - " - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함.\n", - " - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함.\n", - "\n", - " 연동교차로 :\n", - " - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음.\n", - " - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음.\n", - " - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음.\n", - "\n", - " match6 :\n", - " - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄.\n", - " '''\n", - "\n", - " node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", - "\n", - " child_ids = inter_node[inter_node.inter_type=='child'].node_id.unique()\n", - " ch2pa = {} # child to parent\n", - " for child_id in child_ids:\n", - " parent_no = inter_node[inter_node.node_id==child_id].inter_no.iloc[0]\n", - " sub_inter_node = inter_node[inter_node.inter_no==parent_no]\n", - " ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id\n", - " directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향\n", - "\n", - " # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여\n", - " cmatches = []\n", - " for _, row in uturn.iterrows():\n", - " child_id = row.child_id\n", - " parent_id = row.parent_id\n", - " direction = row.direction\n", - " condition = row.condition\n", - " inc_edge_id = row.inc_edge\n", - " out_edge_id = row.out_edge\n", - " # match5에서 parent_id에 해당하는 행들을 가져옴\n", - " cmatch = match5.copy()[match5.node_id==parent_id] # match dataframe for a child node\n", - " cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True)\n", - " cmatch['node_id'] = child_id\n", - " cmatch[['inc_edge', 'out_edge']] = np.nan\n", - "\n", - " # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함\n", - " ind = directions.index(direction)\n", - " if condition == \"좌회전시\":\n", - " inc_dire = direction\n", - " out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)]\n", - " elif condition == \"직진시\":\n", - " inc_dire = direction\n", - " out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)]\n", - " elif condition == \"보행신호시\":\n", - " inc_dire = directions[(ind + 2) % len(directions)]\n", - " out_dire_A = directions[(ind - 2) % len(directions)]\n", - " out_dire_B = directions[(ind - 2) % len(directions)]\n", - "\n", - " # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함\n", - " if condition == '보행신호시':\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다.\n", - " cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " else: # '직진시', '좌회전시'\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", - " # 유턴신호의 이동류번호를 19로 부여한다.\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19\n", - " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19\n", - " cmatches.append(cmatch)\n", - "\n", - " # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여\n", - " coord['inter_no'] = coord['parent_id'].map(node2inter)\n", - " coord = coord.rename(columns={'child_id':'node_id'})\n", - " coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan\n", - " coord['move_no'] = 20\n", - " coord = coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']]\n", - " \n", - " # display(coord)\n", - " cmatches = pd.concat(cmatches)\n", - " match6 = pd.concat([match5, cmatches, coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type'])\n", - " match6.to_csv('../Intermediates/match6.csv')\n", - " return match6" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def make_matching(match6, inter_node, nema):\n", - " '''\n", - " 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다.\n", - " 모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다. \n", - " - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id\n", - " \n", - " 설명 : \n", - " - 필요한 리스트, 딕셔너리 등을 정의\n", - " (1) 가능한 (진입방향, 진출방향) 목록 [리스트]\n", - " (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리]\n", - " (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리]\n", - " (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리]\n", - " (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리]\n", - " - matching은 빈 리스트로 지정.\n", - " - 모든 노드id에 대하여 다음 과정을 반복\n", - " - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복\n", - " - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄\n", - " - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append\n", - " '''\n", - "\n", - " match7 = match6.copy()\n", - " match7 = match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']]\n", - "\n", - " parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n", - " child_ids = sorted(inter_node[inter_node.inter_type=='child'].node_id.unique())\n", - "\n", - " # (1) 가능한 (진입방향, 진출방향) 목록 \n", - " flows = nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist()\n", - " # (2) 각 교차로별 방향 목록 : pdires (possible directions)\n", - " pdires = {}\n", - " for node_id in parent_ids:\n", - " dires = match7[match7.node_id == node_id][['inc_dir','out_dir']].values.flatten()\n", - " dires = {dire for dire in dires if type(dire)==str}\n", - " pdires[node_id] = dires\n", - " # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id)\n", - " inc2id = {}\n", - " for node_id in parent_ids:\n", - " for inc_dir in pdires[node_id]:\n", - " df = match7[(match7.node_id==node_id) & (match7.inc_dir==inc_dir)]\n", - " inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0]\n", - " # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id)\n", - " out2id = {}\n", - " for node_id in parent_ids:\n", - " for out_dir in pdires[node_id]:\n", - " df = match7[(match7.node_id==node_id) & (match7.out_dir==out_dir)]\n", - " out2id[(node_id, out_dir)] = df.out_edge.iloc[0]\n", - " # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows)\n", - " pflow = {}\n", - " for node_id in parent_ids:\n", - " pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])]\n", - " # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching\n", - " node2inter = dict(zip(match7['node_id'], match7['inter_no']))\n", - " dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow\n", - " matching = []\n", - " for node_id in parent_ids:\n", - " inter_no = node2inter[node_id]\n", - " # 좌회전과 직진(1 ~ 16)\n", - " for (inc_dir, out_dir) in pflow[node_id]:\n", - " move_no = nema[(nema.inc_dir==inc_dir) & (nema.out_dir==out_dir)].move_no.iloc[0]\n", - " inc_edge = inc2id[(node_id, inc_dir)]\n", - " out_edge = out2id[(node_id, out_dir)]\n", - " new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no],\n", - " 'inc_dir':[inc_dir], 'out_dir':[out_dir],\n", - " 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})\n", - " matching.append(new_row)\n", - " # 보행신호(17), 전적색(18)\n", - " new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18],\n", - " 'inc_dir':[None]*2, 'out_dir':[None]*2,\n", - " 'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2})\n", - " matching.append(new_row)\n", - " # 신호우회전(21)\n", - " for d in range(len(dires_right)-1):\n", - " inc_dir = dires_right[d]\n", - " out_dir = dires_right[d+1]\n", - " if {inc_dir, out_dir}.issubset(pdires[node_id]):\n", - " inc_edge = inc2id[(node_id, inc_dir)]\n", - " out_edge = out2id[(node_id, out_dir)]\n", - " new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21],\n", - " 'inc_dir':[inc_dir], 'out_dir':[out_dir],\n", - " 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})\n", - " matching.append(new_row)\n", - " matching.append(match7[match7.node_id.isin(child_ids)])\n", - " matching = pd.concat(matching)\n", - " matching = matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True)\n", - " matching['move_no'] = matching['move_no'].astype(int)\n", - " matching.to_csv('../Intermediates/matching.csv')\n", - " return matching" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def make_movements():\n", - " movements_path = '../Intermediates/movement/'\n", - " movements_list = [pd.read_csv(movements_path + file, index_col=0) for file in tqdm(os.listdir(movements_path))]\n", - " movements = pd.concat(movements_list)\n", - " movements = movements.drop(columns=['start_unix'])\n", - " movements = movements.drop_duplicates()\n", - " movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B'])\n", - " movements = movements.reset_index(drop=True)\n", - " movements.to_csv('../Intermediates/movements.csv')\n", - " return movements" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# node2num_cycles : A dictionary that maps a node_id to the number of cycles\n", - "def get_node2num_cycles(plan, inter_node):\n", - " node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", - " node_ids = sorted(inter_node.node_id.unique())\n", - "\n", - " Aplan = plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']]\n", - " grouped = Aplan.groupby('inter_no')\n", - " df = grouped.agg({'cycle': 'min'}).reset_index()\n", - " df = df.rename(columns={'cycle': 'min_cycle'})\n", - " df['num_cycle'] = 300 // df['min_cycle'] + 2\n", - " inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle']))\n", - " node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids}\n", - " with open('../Intermediates/node2numcycles.json', 'w') as file:\n", - " json.dump(node2numcycles, file, indent=4)\n", - " return node2numcycles" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def preprocess_daily():\n", - " path_move = '../Data/tables/move/'\n", - "\n", - " inter_info = pd.read_csv('../Data/tables/inter_info.csv', index_col=0)\n", - " angle = pd.read_csv('../Data/tables/angle.csv', index_col=0, dtype = {f'angle_{alph}{j}':'str' for alph in ['A', 'B'] for j in range(1,9)})\n", - " plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", - " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", - " uturn = pd.read_csv('../Data/tables/child_uturn.csv')\n", - " coord = pd.read_csv('../Data/tables/child_coord.csv')\n", - " nema = pd.read_csv('../Data/tables/nema.csv', encoding='cp949')\n", - "\n", - " net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n", - "\n", - " match1 = make_match1(path_move)\n", - " match2 = make_match2(match1)\n", - " match3 = make_match3(match2, nema)\n", - " match4 = make_match4(match3, angle)\n", - " match5 = make_match5(match4, net, inter_node, inter_info)\n", - " match6 = make_match6(match5, inter_node, uturn, coord)\n", - " matching = make_matching(match6, inter_node, nema)\n", - " movements = make_movements()\n", - " node2num_cycles = get_node2num_cycles(plan, inter_node)\n", - " return match6, matching, movements, node2num_cycles" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/17280 [00:00= 0) & (durations <= 100)).all(axis=1)\n", - "sorted(plan[~valid_indices].inter_no.unique())\n", - "# durations" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "rts", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Archives/Scripts/preprocess_daily_0.py b/Archives/Scripts/preprocess_daily_0.py deleted file mode 100644 index f0de51881..000000000 --- a/Archives/Scripts/preprocess_daily_0.py +++ /dev/null @@ -1,431 +0,0 @@ -import pandas as pd -import numpy as np -import os -import json -import sumolib -from tqdm import tqdm - -def check_inter_info(inter_info): - print(inter_info) - print('check') - -def make_match1(path_root): - ''' - 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다. - '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다. - - return : 통합된 이동류정보 - - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보 - - match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다. - ''' - # [이동류번호] 불러오기 (약 1분의 소요시간) - path_move = os.path.join(path_root, 'Data', 'tables', 'move') - csv_moves = os.listdir(path_move) - moves = [pd.read_csv(os.path.join(path_move, csv_move), index_col=0) for csv_move in tqdm(csv_moves, desc='이동류정보 불러오는 중 : match1')] - match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True) - match1.to_csv(os.path.join(path_root, 'Intermediates', 'match1.csv')) - return match1 - -def make_match2(match1): - ''' - match1을 계층화함. - - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B - - match2의 컬럼 : inter_no, phase_no, ring_type, move_no - ''' - # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no') - matchA = match1[['inter_no', 'phas_A', 'move_A']].copy() - matchA.columns = ['inter_no', 'phase_no', 'move_no'] - matchA['ring_type'] = 'A' - matchB = match1[['inter_no', 'phas_B', 'move_B']].copy() - matchB.columns = ['inter_no', 'phase_no', 'move_no'] - matchB['ring_type'] = 'B' - match2 = pd.concat([matchA, matchB]).drop_duplicates() - match2 = match2[['inter_no', 'phase_no', 'ring_type', 'move_no']] - match2 = match2.sort_values(by=list(match2.columns)) - return match2 - -def make_match3(match2, nema): - ''' - 각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함. - - match2의 컬럼 : inter_no, phase_no, ring_type, move_no - - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir - - nema : - - 컬럼 : move_no, inc_dir, out_dir - - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블 - - 이동류번호 : 1 ~ 16, 17, 18, 21 - - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서 - ''' - # nema 정보 불러오기 및 병합 - match3 = pd.merge(match2, nema, how='left', on='move_no').drop_duplicates() - return match3 - -def make_match4(match3, angle): - ''' - 방위각 정보를 매칭시켜 추가함. - - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir - - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle - - angle_original : - - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8) - - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블 - - 이동류번호 : 1 ~ 16, 17, 18, 21 - - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서 - ''' - - # 계층화 - angles = [] - for i, row in angle.iterrows(): - angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] - new = pd.DataFrame({'inter_no':[row.inter_no] * 16, 'phase_no':list(range(1, 9))*2, 'ring_type':['A'] * 8 + ['B'] * 8, 'angle_code':angle_codes.to_list()}) - angles.append(new) - angles = pd.concat(angles) - angles = angles.dropna().reset_index(drop=True) - - # 병합 - six_chars = angles.angle_code.apply(lambda x:len(x)==6) - angles.loc[six_chars,'inc_angle'] = angles.angle_code.apply(lambda x:x[:3]) - angles.loc[six_chars,'out_angle'] = angles.angle_code.apply(lambda x:x[3:]) - angles = angles.drop('angle_code', axis=1) - match4 = pd.merge(match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'], - right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates() - return match4 - -def make_match5(match4, net, inter_node, inter_info): - ''' - 진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로). - - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle - - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id - - 사용된 데이터 : - (1) net - - 성남시 정자동 부근의 샘플 네트워크 - (2) inter_node - - 교차로번호와 노드id를 매칭시키는 테이블. - - parent/child 정보도 포함되어 있음 - - 컬럼 : inter_no, node_id, inter_type - (3) inter_info - - 교차로 정보. 여기에서는 위도와 경도가 쓰임. - - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no - - 진입엣지id, 진출엣지id를 얻는 과정 : - - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함. - * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음 - - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장. - * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge - - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄. - - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄. - - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함. - ''' - - # parent node만 가져옴. - inter_node1 = inter_node[inter_node.inter_type == 'parent'].drop('inter_type', axis=1) - inter_info1 = inter_info[['inter_no', 'inter_lat', 'inter_lon']] - inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'], - right_on=['inter_no']).drop_duplicates() - - inter2node = dict(zip(inter['inter_no'], inter['node_id'])) - - match5 = match4.copy() - # 진입진출ID 매칭 - for index, row in match5.iterrows(): - node_id = inter2node[row.inter_no] - node = net.getNode(node_id) - # 교차로의 모든 (from / to) edges - inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges - out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges - # 교차로의 모든 (from / to) directions - inc_dirs = [] - for inc_edge in inc_edges: - start = inc_edge.getShape()[-2] - end = inc_edge.getShape()[-1] - inc_dir = np.array(end) - np.array(start) - inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5 - inc_dirs.append(inc_dir) - out_dirs = [] - for out_edge in out_edges: - start = out_edge.getShape()[0] - end = out_edge.getShape()[1] - out_dir = np.array(end) - np.array(start) - out_dir = out_dir / (out_dir ** 2).sum() ** 0.5 - out_dirs.append(out_dir) - # 진입각, 진출각 불러오기 - if not pd.isna(row.inc_angle): - inc_angle = int(row.inc_angle) - out_angle = int(row.out_angle) - # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환 - inc_angle = (-90 - inc_angle) % 360 - inc_angle = inc_angle * np.pi / 180. - inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)]) - out_angle = (90 - out_angle) % 360 - out_angle = out_angle * np.pi / 180. - out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)]) - # 매칭 엣지 반환 - inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax() - out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax() - inc_edge_id = inc_edges[inc_index].getID() - out_edge_id = out_edges[out_index].getID() - match5.at[index, 'inc_edge'] = inc_edge_id - match5.at[index, 'out_edge'] = out_edge_id - match5['node_id'] = match5['inter_no'].map(inter2node) - match5 = match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True) - return match5 - -def make_match6(match5, inter_node, uturn, coord, path_root): - ''' - 진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로). - - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id - - 사용된 데이터 : - (1) inter_node - - 교차로번호와 노드id를 매칭시키는 테이블. - - parent/child 정보도 포함되어 있음 - - 컬럼 : inter_no, node_id, inter_type - (2) uturn (유턴정보) - - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge - - parent_id, child_id : 주교차로id, 유턴교차로id - - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향) - - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나 - - inc_edge, out_edge : 유턴에 대한 진입진출엣지 - (3) coord (연동교차로정보) - - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge - - parent_id, child_id : 주교차로id, 연동교차로id - - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지 - - 설명 : - - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음. - 여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함. - 유턴교차로 : - - directions를 정북기준 시계방향의 8방위로 정함. - - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음. - - 예) 진입방향(direction)이 '북'일 때, - - 직진 : (북, 남) - * 남 : directions[(ind + 4) % len(directions)] - - 좌회전 : (북, 동) - * 동 : directions[(ind + 2) % len(directions)] - - 보행 : (서, 동) - * 서 : directions[(ind - 2) % len(directions)] - - uturn의 각 행을 순회하면서 아래 과정을 반복함 - - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch). - - condition 별로 진입방향, 진출방향A, 진출방향B 정함. - - 상술한 directions를 활용하여 정함. - - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함. - - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함. - - 연동교차로 : - - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음. - - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음. - - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음. - - match6 : - - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄. - ''' - - node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) - - child_ids = inter_node[inter_node.inter_type=='child'].node_id.unique() - ch2pa = {} # child to parent - for child_id in child_ids: - parent_no = inter_node[inter_node.node_id==child_id].inter_no.iloc[0] - sub_inter_node = inter_node[inter_node.inter_no==parent_no] - ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id - directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향 - - # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여 - cmatches = [] - for _, row in uturn.iterrows(): - child_id = row.child_id - parent_id = row.parent_id - direction = row.direction - condition = row.condition - inc_edge_id = row.inc_edge - out_edge_id = row.out_edge - # match5에서 parent_id에 해당하는 행들을 가져옴 - cmatch = match5.copy()[match5.node_id==parent_id] # match dataframe for a child node - cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True) - cmatch['node_id'] = child_id - cmatch[['inc_edge', 'out_edge']] = np.nan - - # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함 - ind = directions.index(direction) - if condition == "좌회전시": - inc_dire = direction - out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)] - elif condition == "직진시": - inc_dire = direction - out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)] - elif condition == "보행신호시": - inc_dire = directions[(ind + 2) % len(directions)] - out_dire_A = directions[(ind - 2) % len(directions)] - out_dire_B = directions[(ind - 2) % len(directions)] - - # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함 - if condition == '보행신호시': - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다. - cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - else: # '직진시', '좌회전시' - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - # 유턴신호의 이동류번호를 19로 부여한다. - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19 - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19 - cmatches.append(cmatch) - - # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여 - coord['inter_no'] = coord['parent_id'].map(node2inter) - coord = coord.rename(columns={'child_id':'node_id'}) - coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan - coord['move_no'] = 20 - coord = coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']] - - # display(coord) - cmatches = pd.concat(cmatches) - match6 = pd.concat([match5, cmatches, coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type']) - match6.to_csv(os.path.join(path_root, 'Intermediates', 'match6.csv')) - return match6 - -def make_matching(match6, inter_node, nema, path_root): - ''' - 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다. - 모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다. - - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id - - 설명 : - - 필요한 리스트, 딕셔너리 등을 정의 - (1) 가능한 (진입방향, 진출방향) 목록 [리스트] - (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리] - (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리] - (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리] - (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리] - - matching은 빈 리스트로 지정. - - 모든 노드id에 대하여 다음 과정을 반복 - - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복 - - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄 - - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append - ''' - - match7 = match6.copy() - match7 = match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']] - - parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique()) - child_ids = sorted(inter_node[inter_node.inter_type=='child'].node_id.unique()) - - # (1) 가능한 (진입방향, 진출방향) 목록 - flows = nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist() - # (2) 각 교차로별 방향 목록 : pdires (possible directions) - pdires = {} - for node_id in parent_ids: - dires = match7[match7.node_id == node_id][['inc_dir','out_dir']].values.flatten() - dires = {dire for dire in dires if type(dire)==str} - pdires[node_id] = dires - # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) - inc2id = {} - for node_id in parent_ids: - for inc_dir in pdires[node_id]: - df = match7[(match7.node_id==node_id) & (match7.inc_dir==inc_dir)] - inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0] - # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) - out2id = {} - for node_id in parent_ids: - for out_dir in pdires[node_id]: - df = match7[(match7.node_id==node_id) & (match7.out_dir==out_dir)] - out2id[(node_id, out_dir)] = df.out_edge.iloc[0] - # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) - pflow = {} - for node_id in parent_ids: - pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])] - # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching - node2inter = dict(zip(match7['node_id'], match7['inter_no'])) - dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow - matching = [] - for node_id in parent_ids: - inter_no = node2inter[node_id] - # 좌회전과 직진(1 ~ 16) - for (inc_dir, out_dir) in pflow[node_id]: - move_no = nema[(nema.inc_dir==inc_dir) & (nema.out_dir==out_dir)].move_no.iloc[0] - inc_edge = inc2id[(node_id, inc_dir)] - out_edge = out2id[(node_id, out_dir)] - new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no], - 'inc_dir':[inc_dir], 'out_dir':[out_dir], - 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]}) - matching.append(new_row) - # 보행신호(17), 전적색(18) - new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18], - 'inc_dir':[None]*2, 'out_dir':[None]*2, - 'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2}) - matching.append(new_row) - # 신호우회전(21) - for d in range(len(dires_right)-1): - inc_dir = dires_right[d] - out_dir = dires_right[d+1] - if {inc_dir, out_dir}.issubset(pdires[node_id]): - inc_edge = inc2id[(node_id, inc_dir)] - out_edge = out2id[(node_id, out_dir)] - new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21], - 'inc_dir':[inc_dir], 'out_dir':[out_dir], - 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]}) - matching.append(new_row) - matching.append(match7[match7.node_id.isin(child_ids)]) - matching = pd.concat(matching) - matching = matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True) - matching['move_no'] = matching['move_no'].astype(int) - matching.to_csv(os.path.join(path_root, 'Intermediates', 'matching.csv')) - return matching - -def make_movements(path_root): - movements_path = os.path.join(path_root, 'Intermediates', 'movement') - movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')] - movements = pd.concat(movements_list) - movements = movements.drop(columns=['start_unix']) - movements = movements.drop_duplicates() - movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B']) - movements = movements.reset_index(drop=True) - movements.to_csv(os.path.join(path_root, 'Intermediates', 'movements.csv')) - return movements - -# node2num_cycles : A dictionary that maps a node_id to the number of cycles -def get_node2num_cycles(plan, inter_node, path_root): - node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) - node_ids = sorted(inter_node.node_id.unique()) - - Aplan = plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']] - grouped = Aplan.groupby('inter_no') - df = grouped.agg({'cycle': 'min'}).reset_index() - df = df.rename(columns={'cycle': 'min_cycle'}) - df['num_cycle'] = 300 // df['min_cycle'] + 2 - inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle'])) - node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids} - with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file: - json.dump(node2numcycles, file, indent=4) - return node2numcycles - -def main(): - path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - inter_info = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'inter_info.csv')) - check_inter_info(inter_info) - angle = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'angle.csv'), dtype = {f'angle_{alph}{j}':'str' for alph in ['A', 'B'] for j in range(1,9)}) - plan = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'plan.csv')) - inter_node = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'inter_node.csv')) - uturn = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'child_uturn.csv')) - coord = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'child_coord.csv')) - nema = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'nema.csv'), encoding='cp949') - - net = sumolib.net.readNet(os.path.join(path_root, 'Data', 'networks', 'sn.net.xml')) - - match1 = make_match1(path_root) - match2 = make_match2(match1) - match3 = make_match3(match2, nema) - match4 = make_match4(match3, angle) - match5 = make_match5(match4, net, inter_node, inter_info) - match6 = make_match6(match5, inter_node, uturn, coord, path_root) - matching = make_matching(match6, inter_node, nema, path_root) - movements = make_movements(path_root) - node2num_cycles = get_node2num_cycles(plan, inter_node, path_root) - -if __name__ == '__main__': - main() diff --git a/Archives/Scripts/preprocess_daily_1.py b/Archives/Scripts/preprocess_daily_1.py deleted file mode 100644 index 23f80587a..000000000 --- a/Archives/Scripts/preprocess_daily_1.py +++ /dev/null @@ -1,587 +0,0 @@ -import pandas as pd -import numpy as np -import os, sys, traci -import json -import sumolib -from tqdm import tqdm - -class DailyPreprocessor(): - def __init__(self): - self.path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - self.issues = [] - - # 1. 데이터 불러오기 - def load_data(self): - self.load_networks() - self.load_tables() - self.check_networks() - self.check_tables() - print('1. 모든 데이터가 로드되었습니다.') - - # 1-1. 네트워크 불러오기 - def load_networks(self): - self.net = sumolib.net.readNet(os.path.join(self.path_root, 'Data', 'networks', 'sn.net.xml')) - print("1-1. 네트워크가 로드되었습니다.") - - # 1-2. 테이블 불러오기 - def load_tables(self): - # 모든 컬럼에 대하여 데이터타입 지정 - loading_dtype = { - 'inter_no':'int', 'start_hour':'int', 'start_minute':'int', 'cycle':'int','offset':'int', - 'node_id':'str', 'inter_type':'str', 'parent_id':'str','child_id':'str', - 'direction':'str', 'condition':'str', 'inc_edge':'str', 'out_edge':'str', - 'end_unix':'int', 'inter_name':'str', 'inter_lat':'float', 'inter_lon':'float', - 'group_no':'int', 'main_phase_no':'int', 'phase_no':'int','ring_type':'str' - } - for alph in ['A', 'B']: - for j in range(1,9): - loading_dtype[f'angle_{alph}{j}'] = 'str' - loading_dtype[f'dura_{alph}{j}'] = 'int' - - self.path_table = os.path.join(self.path_root, 'Data', 'tables') - - self.inter_info = pd.read_csv(os.path.join(self.path_table, 'inter_info.csv'), dtype=loading_dtype) - self.angle = pd.read_csv(os.path.join(self.path_table, 'angle.csv'), dtype=loading_dtype) - self.plan = pd.read_csv(os.path.join(self.path_table, 'plan.csv'), dtype=loading_dtype) - self.inter_node = pd.read_csv(os.path.join(self.path_table, 'inter_node.csv'), dtype=loading_dtype) - self.uturn = pd.read_csv(os.path.join(self.path_table, 'child_uturn.csv'), dtype=loading_dtype) - self.coord = pd.read_csv(os.path.join(self.path_table, 'child_coord.csv'), dtype=loading_dtype) - self.nema = pd.read_csv(os.path.join(self.path_table, 'nema.csv'), encoding='cp949', dtype=loading_dtype) - print("1-2. 테이블들이 로드되었습니다.") - - # 1-3. 테이블 불러오기 - def check_networks(self): - # https://sumo.dlr.de/docs/Netedit/neteditUsageExamples.html#simplify_tls_program_state_after_changing_connections - if 'SUMO_HOME' in os.environ: - tools = os.path.join(os.environ['SUMO_HOME'], 'tools') - if tools not in sys.path: - sys.path.append(tools) - else: - raise EnvironmentError("please declare environment variable 'SUMO_HOME'") - traci.start([sumolib.checkBinary('sumo'), "-n", os.path.join(self.path_root, 'Data', 'networks', 'sn.net.xml')]) - nodes = [node for node in self.net.getNodes() if node.getType()=='traffic_light'] - for node in nodes: - node_id = node.getID() - from_xml = len([c for c in node.getConnections() if c.getTLLinkIndex() >= 0]) - from_traci = len(traci.trafficlight.getRedYellowGreenState(node_id)) - if from_xml != from_traci: - sub = {'id': node_id, 'type': 'node', 'note': '유효하지 않은 연결이있음. netedit에서 clean states 필요.'} - self.issues.append(sub) - traci.close() - print("1-3. 네트워크의 모든 clean state requirement들을 체크했습니다.") - - # 1-4. 테이블의 무결성 검사 - def check_tables(self): - self.check_inter_info() - self.check_angle() - self.check_plan() - print("1-4. 모든 테이블들의 무결성을 검사했고 이상 없습니다.") - pass - - # 1-4-1. 교차로정보(inter_info) 검사 - def check_inter_info(self): - # 1-4-1-1. inter_lat, inter_lon 적절성 검사 - self.inter_info.loc[0, 'inter_lat'] = 38.0 # 에러 발생을 위한 코드 - self.max_lon, self.min_lon = 127.207888, 127.012492 - self.max_lat, self.min_lat = 37.480693, 37.337112 - for _, row in self.inter_info.iterrows(): - latbool = self.min_lat <= row['inter_lat'] <= self.max_lat - lonbool = self.min_lon <= row['inter_lon'] <= self.max_lon - if not(latbool and lonbool): - msg = f"1-4-1-1. 위도 또는 경도가 범위를 벗어난 교차로가 있습니다: inter_no : {row['inter_no']}" - self.issues.append(msg) - # 교차로목록 정의 - self.inter_nos = sorted(self.inter_info.inter_no.unique()) - - # 1-4-2. 방위각정보(inter_info) 검사 - def check_angle(self): - # 1-4-2-1. inter_no 검사 - # self.angle.loc[0, 'inter_no'] = '4' # 에러 발생을 위한 코드 - missing_inter_nos = set(self.angle.inter_no) - set(self.inter_nos) - if missing_inter_nos: - msg = f"1-4-2-1. angle의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" - self.issues.append(msg) - - # 1-4-3. 신호계획(plan) 검사 - def check_plan(self): - # 1-4-3-1. inter_no 검사 - # self.plan.loc[0, 'inter_no'] = '4' # 에러 발생을 위한 코드 - missing_inter_nos = set(self.plan.inter_no) - set(self.inter_nos) - if missing_inter_nos: - msg = f"1-4-3-1. plan의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" - self.issues.append(msg) - - # 1-4-3-2. 시작시각 검사 - # self.plan.loc[0, 'start_hour'] = 27 # 에러 발생을 위한 코드 - for _, row in self.plan.iterrows(): - start_hour = row.start_hour - start_minute = row.start_minute - if not (0 <= start_hour <= 23) or not (0 <= start_minute <= 59): - msg = f"1-4-3-2. plan에 잘못된 형식의 start_time이 존재합니다: {start_hour, start_minute}" - self.issues.append(msg) - - # 1-4-3-3. 현시시간 검사 - # self.plan.loc[0, 'dura_A1'] = -2 # 에러 발생을 위한 코드 - durations = self.plan[[f'dura_{alph}{j}' for alph in ['A','B'] for j in range(1, 9)]] - valid_indices = ((durations >= 0) & (durations <= 200)).all(axis=1) - invalid_inter_nos = sorted(self.plan[~ valid_indices].inter_no.unique()) - if invalid_inter_nos: - msg = f"1-4-3-3. 음수이거나 200보다 큰 현시시간이 존재합니다. : {invalid_inter_nos}" - - # 1-4-3-4. 주기 일관성 검사 - # self.plan.loc[0, 'cycle'] = 50 # 에러 발생을 위한 코드 - inconsistent_cycle = self.plan.groupby(['inter_no', 'start_hour', 'start_minute'])['cycle'].nunique().gt(1) - if inconsistent_cycle.any(): - inc_inter_no, start_hour, start_minute = inconsistent_cycle[inconsistent_cycle].index[0] - msg = f"1-4-3-4. inter_no:{inc_inter_no}, start_hour:{start_minute}, start_hour:{start_minute}일 때, cycle이 유일하게 결정되지 않습니다." - self.issues.append(msg) - - # 1-4-3-5. 현시시간 / 주기 검사 - # self.plan.loc[0, 'duration'] = 10 # 에러 발생을 위한 코드 - right_duration = True - for (inter_no, start_hour, start_minute), group in self.plan.groupby(['inter_no', 'start_hour', 'start_minute']): - A_sum = group[[f'dura_A{j}' for j in range(1, 9)]].iloc[0].sum() - B_sum = group[[f'dura_B{j}' for j in range(1, 9)]].iloc[0].sum() - # A_sum = group[group['ring_type']=='A']['duration'].sum() - # B_sum = group[group['ring_type']=='B']['duration'].sum() - cycle = group['cycle'].unique()[0] - if not (A_sum == B_sum == cycle): - right_duration = False - inc_inter_no = inter_no - if not right_duration: - msg = f"1-4-4-5. inter_no:{inc_inter_no}, A링현시시간의 합과 B링현시시간의 합이 일치하지 않거나, 현시시간의 합과 주기가 일치하지 않습니다." - self.issues.append(msg) - - # 2. 중간산출물 만들기 - def get_intermediates(self): - self.get_matches() - # self.get_movements() - self.get_node2num_cycles() - - # 2-1 매칭테이블들 생성 - def get_matches(self): - self.make_match1() - self.make_match2() - self.make_match3() - self.make_match4() - self.make_match5() - self.make_match6() - self.make_matching() - - # 2-1-1 - def make_match1(self): - ''' - 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다. - '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다. - - return : 통합된 이동류정보 - - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보 - - match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다. - ''' - # [이동류번호] 불러오기 (약 1분의 소요시간) - path_move = os.path.join(self.path_root, 'Data', 'tables', 'move') - csv_moves = os.listdir(path_move) - moves = [pd.read_csv(os.path.join(path_move, csv_move), index_col=0) for csv_move in tqdm(csv_moves, desc='이동류정보 불러오는 중 : match1')] - self.match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True) - self.match1.to_csv(os.path.join(self.path_root, 'Intermediates', 'match1.csv')) - - # 2-1-2 - def make_match2(self): - ''' - match1을 계층화함. - - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B - - match2의 컬럼 : inter_no, phase_no, ring_type, move_no - ''' - # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no') - matchA = self.match1[['inter_no', 'phas_A', 'move_A']].copy() - matchA.columns = ['inter_no', 'phase_no', 'move_no'] - matchA['ring_type'] = 'A' - matchB = self.match1[['inter_no', 'phas_B', 'move_B']].copy() - matchB.columns = ['inter_no', 'phase_no', 'move_no'] - matchB['ring_type'] = 'B' - self.match2 = pd.concat([matchA, matchB]).drop_duplicates() - self.match2 = self.match2[['inter_no', 'phase_no', 'ring_type', 'move_no']] - self.match2 = self.match2.sort_values(by=list(self.match2.columns)) - - # 2-1-3 - def make_match3(self): - ''' - 각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함. - - match2의 컬럼 : inter_no, phase_no, ring_type, move_no - - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir - - nema : - - 컬럼 : move_no, inc_dir, out_dir - - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블 - - 이동류번호 : 1 ~ 16, 17, 18, 21 - - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서 - ''' - # nema 정보 불러오기 및 병합 - self.match3 = pd.merge(self.match2, self.nema, how='left', on='move_no').drop_duplicates() - - # 2-1-4 - def make_match4(self): - ''' - 방위각 정보를 매칭시켜 추가함. - - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir - - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle - - angle_original : - - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8) - - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블 - - 이동류번호 : 1 ~ 16, 17, 18, 21 - - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서 - ''' - - # 계층화 - angles = [] - for i, row in self.angle.iterrows(): - angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] - new = pd.DataFrame({'inter_no':[row.inter_no] * 16, 'phase_no':list(range(1, 9))*2, 'ring_type':['A'] * 8 + ['B'] * 8, 'angle_code':angle_codes.to_list()}) - angles.append(new) - angles = pd.concat(angles) - angles = angles.dropna().reset_index(drop=True) - - # 병합 - six_chars = angles.angle_code.apply(lambda x:len(x)==6) - angles.loc[six_chars,'inc_angle'] = angles.angle_code.apply(lambda x:x[:3]) - angles.loc[six_chars,'out_angle'] = angles.angle_code.apply(lambda x:x[3:]) - angles = angles.drop('angle_code', axis=1) - self.match4 = pd.merge(self.match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'], - right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates() - - # 2-1-5 - def make_match5(self): - ''' - 진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로). - - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle - - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id - - 사용된 데이터 : - (1) net - - 성남시 정자동 부근의 샘플 네트워크 - (2) inter_node - - 교차로번호와 노드id를 매칭시키는 테이블. - - parent/child 정보도 포함되어 있음 - - 컬럼 : inter_no, node_id, inter_type - (3) inter_info - - 교차로 정보. 여기에서는 위도와 경도가 쓰임. - - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no - - 진입엣지id, 진출엣지id를 얻는 과정 : - - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함. - * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음 - - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장. - * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge - - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄. - - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄. - - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함. - ''' - - # parent node만 가져옴. - inter_node1 = self.inter_node[self.inter_node.inter_type == 'parent'].drop('inter_type', axis=1) - inter_info1 = self.inter_info[['inter_no', 'inter_lat', 'inter_lon']] - inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'], - right_on=['inter_no']).drop_duplicates() - - self.inter2node = dict(zip(inter['inter_no'], inter['node_id'])) - - self.match5 = self.match4.copy() - # 진입진출ID 매칭 - for index, row in self.match5.iterrows(): - node_id = self.inter2node[row.inter_no] - node = self.net.getNode(node_id) - # 교차로의 모든 (from / to) edges - inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges - out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges - # 교차로의 모든 (from / to) directions - inc_dirs = [] - for inc_edge in inc_edges: - start = inc_edge.getShape()[-2] - end = inc_edge.getShape()[-1] - inc_dir = np.array(end) - np.array(start) - inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5 - inc_dirs.append(inc_dir) - out_dirs = [] - for out_edge in out_edges: - start = out_edge.getShape()[0] - end = out_edge.getShape()[1] - out_dir = np.array(end) - np.array(start) - out_dir = out_dir / (out_dir ** 2).sum() ** 0.5 - out_dirs.append(out_dir) - # 진입각, 진출각 불러오기 - if not pd.isna(row.inc_angle): - inc_angle = int(row.inc_angle) - out_angle = int(row.out_angle) - # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환 - inc_angle = (-90 - inc_angle) % 360 - inc_angle = inc_angle * np.pi / 180. - inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)]) - out_angle = (90 - out_angle) % 360 - out_angle = out_angle * np.pi / 180. - out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)]) - # 매칭 엣지 반환 - inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax() - out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax() - inc_edge_id = inc_edges[inc_index].getID() - out_edge_id = out_edges[out_index].getID() - self.match5.at[index, 'inc_edge'] = inc_edge_id - self.match5.at[index, 'out_edge'] = out_edge_id - self.match5['node_id'] = self.match5['inter_no'].map(self.inter2node) - self.match5 = self.match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True) - - # 2-1-6 - def make_match6(self): - ''' - 진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로). - - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id - - 사용된 데이터 : - (1) inter_node - - 교차로번호와 노드id를 매칭시키는 테이블. - - parent/child 정보도 포함되어 있음 - - 컬럼 : inter_no, node_id, inter_type - (2) uturn (유턴정보) - - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge - - parent_id, child_id : 주교차로id, 유턴교차로id - - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향) - - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나 - - inc_edge, out_edge : 유턴에 대한 진입진출엣지 - (3) coord (연동교차로정보) - - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge - - parent_id, child_id : 주교차로id, 연동교차로id - - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지 - - 설명 : - - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음. - 여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함. - 유턴교차로 : - - directions를 정북기준 시계방향의 8방위로 정함. - - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음. - - 예) 진입방향(direction)이 '북'일 때, - - 직진 : (북, 남) - * 남 : directions[(ind + 4) % len(directions)] - - 좌회전 : (북, 동) - * 동 : directions[(ind + 2) % len(directions)] - - 보행 : (서, 동) - * 서 : directions[(ind - 2) % len(directions)] - - uturn의 각 행을 순회하면서 아래 과정을 반복함 - - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch). - - condition 별로 진입방향, 진출방향A, 진출방향B 정함. - - 상술한 directions를 활용하여 정함. - - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함. - - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함. - - 연동교차로 : - - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음. - - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음. - - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음. - - match6 : - - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄. - ''' - - self.node2inter = dict(zip(self.inter_node['node_id'], self.inter_node['inter_no'])) - - child_ids = self.inter_node[self.inter_node.inter_type=='child'].node_id.unique() - ch2pa = {} # child to parent - for child_id in child_ids: - parent_no = self.inter_node[self.inter_node.node_id==child_id].inter_no.iloc[0] - sub_inter_node = self.inter_node[self.inter_node.inter_no==parent_no] - ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id - directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향 - - # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여 - cmatches = [] - for _, row in self.uturn.iterrows(): - child_id = row.child_id - parent_id = row.parent_id - direction = row.direction - condition = row.condition - inc_edge_id = row.inc_edge - out_edge_id = row.out_edge - # match5에서 parent_id에 해당하는 행들을 가져옴 - cmatch = self.match5.copy()[self.match5.node_id==parent_id] # match dataframe for a child node - cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True) - cmatch['node_id'] = child_id - cmatch[['inc_edge', 'out_edge']] = np.nan - - # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함 - ind = directions.index(direction) - if condition == "좌회전시": - inc_dire = direction - out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)] - elif condition == "직진시": - inc_dire = direction - out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)] - elif condition == "보행신호시": - inc_dire = directions[(ind + 2) % len(directions)] - out_dire_A = directions[(ind - 2) % len(directions)] - out_dire_B = directions[(ind - 2) % len(directions)] - - # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함 - if condition == '보행신호시': - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다. - cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - else: # '직진시', '좌회전시' - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] - # 유턴신호의 이동류번호를 19로 부여한다. - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19 - cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19 - cmatches.append(cmatch) - - # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여 - self.coord['inter_no'] = self.coord['parent_id'].map(self.node2inter) - self.coord = self.coord.rename(columns={'child_id':'node_id'}) - self.coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan - self.coord['move_no'] = 20 - self.coord = self.coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']] - - # display(coord) - cmatches = pd.concat(cmatches) - self.match6 = pd.concat([self.match5, cmatches, self.coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type']) - self.match6.to_csv(os.path.join(self.path_root, 'Intermediates', 'match6.csv')) - - # 2-1-7 - def make_matching(self): - ''' - 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다. - 모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다. - - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id - - 설명 : - - 필요한 리스트, 딕셔너리 등을 정의 - (1) 가능한 (진입방향, 진출방향) 목록 [리스트] - (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리] - (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리] - (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리] - (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리] - - matching은 빈 리스트로 지정. - - 모든 노드id에 대하여 다음 과정을 반복 - - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복 - - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄 - - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append - ''' - - self.match7 = self.match6.copy() - self.match7 = self.match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']] - - parent_ids = sorted(self.inter_node[self.inter_node.inter_type=='parent'].node_id.unique()) - child_ids = sorted(self.inter_node[self.inter_node.inter_type=='child'].node_id.unique()) - - # (1) 가능한 (진입방향, 진출방향) 목록 - flows = self.nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist() - # (2) 각 교차로별 방향 목록 : pdires (possible directions) - pdires = {} - for node_id in parent_ids: - dires = self.match7[self.match7.node_id == node_id][['inc_dir','out_dir']].values.flatten() - dires = {dire for dire in dires if type(dire)==str} - pdires[node_id] = dires - # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) - inc2id = {} - for node_id in parent_ids: - for inc_dir in pdires[node_id]: - df = self.match7[(self.match7.node_id==node_id) & (self.match7.inc_dir==inc_dir)] - inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0] - # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) - out2id = {} - for node_id in parent_ids: - for out_dir in pdires[node_id]: - df = self.match7[(self.match7.node_id==node_id) & (self.match7.out_dir==out_dir)] - out2id[(node_id, out_dir)] = df.out_edge.iloc[0] - # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) - pflow = {} - for node_id in parent_ids: - pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])] - # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching - # node2inter = dict(zip(self.match7['node_id'], self.match7['inter_no'])) - dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow - self.matching = [] - for node_id in parent_ids: - inter_no = self.node2inter[node_id] - # 좌회전과 직진(1 ~ 16) - for (inc_dir, out_dir) in pflow[node_id]: - move_no = self.nema[(self.nema.inc_dir==inc_dir) & (self.nema.out_dir==out_dir)].move_no.iloc[0] - inc_edge = inc2id[(node_id, inc_dir)] - out_edge = out2id[(node_id, out_dir)] - new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no], - 'inc_dir':[inc_dir], 'out_dir':[out_dir], - 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]}) - self.matching.append(new_row) - # 보행신호(17), 전적색(18) - new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18], - 'inc_dir':[None]*2, 'out_dir':[None]*2, - 'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2}) - self.matching.append(new_row) - # 신호우회전(21) - for d in range(len(dires_right)-1): - inc_dir = dires_right[d] - out_dir = dires_right[d+1] - if {inc_dir, out_dir}.issubset(pdires[node_id]): - inc_edge = inc2id[(node_id, inc_dir)] - out_edge = out2id[(node_id, out_dir)] - new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21], - 'inc_dir':[inc_dir], 'out_dir':[out_dir], - 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]}) - self.matching.append(new_row) - self.matching.append(self.match7[self.match7.node_id.isin(child_ids)]) - self.matching = pd.concat(self.matching) - self.matching = self.matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True) - self.matching['move_no'] = self.matching['move_no'].astype(int) - self.matching.to_csv(os.path.join(self.path_root, 'Intermediates', 'matching.csv')) - - # 2-2 - def get_movements(self): - movements_path = os.path.join(self.path_root, 'Intermediates', 'movement') - movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')] - movements = pd.concat(movements_list) - movements = movements.drop(columns=['start_unix']) - movements = movements.drop_duplicates() - movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B']) - movements = movements.reset_index(drop=True) - movements.to_csv(os.path.join(self.path_root, 'Intermediates', 'movements.csv')) - return movements - - # 2-3 node2num_cycles : A dictionary that maps a node_id to the number of cycles - def get_node2num_cycles(self): - # node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) - self.node_ids = sorted(self.inter_node.node_id.unique()) - - Aplan = self.plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']] - grouped = Aplan.groupby('inter_no') - df = grouped.agg({'cycle': 'min'}).reset_index() - df = df.rename(columns={'cycle': 'min_cycle'}) - df['num_cycle'] = 300 // df['min_cycle'] + 2 - inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle'])) - node2numcycles = {node_id : inter2num_cycles[self.node2inter[node_id]] for node_id in self.node_ids} - with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file: - json.dump(node2numcycles, file, indent=4) - return node2numcycles - - # 3. 이슈사항 저장 - def write_issues(self): - path_issues = os.path.join(self.path_root, "Results", "issues_intermediates.txt") - with open(path_issues, "w", encoding="utf-8") as file: - for item in self.issues: - file.write(item + "\n") - if self.issues: - print("데이터 처리 중 발생한 특이사항은 다음과 같습니다. :") - for review in self.issues: - print(review) - - def main(self): - # 1. 데이터 불러오기 - self.load_data() - # 2. 중간산출물 만들기 - self.get_intermediates() - # 3. 이슈사항 저장 - self.write_issues() - -if __name__ == '__main__': - self = DailyPreprocessor() - self.main() \ No newline at end of file diff --git a/Archives/Scripts/scheduler_example.py b/Archives/Scripts/scheduler_example.py deleted file mode 100644 index 5d7c279ad..000000000 --- a/Archives/Scripts/scheduler_example.py +++ /dev/null @@ -1,20 +0,0 @@ -import sched -import time -from datetime import datetime - -# 스케줄러 객체 생성 -scheduler = sched.scheduler(time.time, time.sleep) - -def print_current_time(sc): - print("Current Time:", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - # 다음 실행을 위해 5초 후에 이 작업을 다시 예약 - sc.enter(5, 1, print_current_time, (sc,)) - -if __name__ == "__main__": - # 현재 초(second)를 기준으로 다음 5초 배수 시각까지의 지연 시간 계산 - now = datetime.now() - initial_delay = 5 - (now.second % 5) - # 초기 작업 예약 - scheduler.enter(initial_delay, 1, print_current_time, (scheduler,)) - # 스케줄러 실행 - scheduler.run()