사용 안내

처음 쓰시는 분을 위한 안내입니다. 이 도구가 무엇을 계산하는지, 무엇을 입력하고 결과를 어떻게 읽는지 순서대로 설명합니다.

처음 쓰신다면

세 단계면 끝납니다 — 실행하고, 기다리고, 결과를 봅니다. 위쪽 메뉴의 1 실행 → 2 결과가 이 순서이고, 3 데이터는 CSV 원본이 필요할 때만 씁니다.

  1. 계획 실행 — 값을 그대로 두고 “계획 실행”을 누릅니다 기본값이 이미 채워져 있습니다. 처음에는 아무것도 바꾸지 마세요. 실행 화면 →
  2. 끝날 때까지 기다립니다 — 진행 단계가 화면에 나옵니다 대여소를 묶는 단계가 전체 시간의 3분의 2 이상을 차지합니다. 화면은 3초마다 진행 상황을 받아 바뀐 칸만 고칩니다. 대여소 수집·순수요 계산은 한 번만 하고, 군집·경로·지표는 시간대마다 다시 계산하기 때문에 고른 만큼 늘어납니다. 아직 이 서버에 실행 기록이 없어 숫자를 적지 않습니다 — 한 번 돌리고 나면 그 기록에서 뽑아 보여 줍니다. 기록이 쌓이면 여기에 나옵니다
  3. 결과를 봅니다 — 작업지시서를 기사에게 넘기고, 지도로 경로를 확인합니다 현장에 내보낼 것은 작업지시서입니다(차량 한 대 = 한 장, 출발 직전 “지금 재고와 대조하기”). 지도는 경로를 눈으로 확인할 때, 지표는 “이번 계획이 쓸 만한가”를 판단할 때 봅니다. 작업지시서 →
계획은 한 번에 하나만 돌아갑니다. 여러 단계가 같은 파일을 고쳐 쓰기 때문입니다. 이미 돌고 있으면 새 실행 버튼이 사라지고 진행 상황 링크가 대신 나옵니다.

무엇을 계산하나

한 문장으로: 지금 자전거가 남는 곳에서 빼내 모자랄 곳으로 미리 옮기는 계획을 만듭니다. 계산은 다섯 단계를 거칩니다.

  1. 지금 재고를 확인합니다 타슈 API에서 대여소마다 자전거가 몇 대 있는지 실시간으로 받아옵니다.
  2. 얼마나 필요할지 추정합니다 — 목표 재고 과거 같은 조건(같은 시간대, 같은 평일/휴일)에서 이 대여소의 순수요가 평균 얼마였고 얼마나 들쭉날쭉했는지를 보고, 넉넉하게 잡습니다.
  3. 대여소를 덩어리로 묶습니다 — 군집 차량 한 대가 한 덩어리를 맡습니다. 가까운 것끼리, 일감이 고르게 나뉘도록 묶습니다. 몇 덩어리로 나눌지는 그 회차의 일감이 정합니다 — 옮길 자전거가 많고 들를 대여소가 흩어져 있으면 더 많은 차가 나갑니다. 지난 실행에서는 회차당 14대가 나갔습니다(중앙값).
  4. 덩어리 안에서 어디서 얼마를 옮길지 정합니다 한 덩어리 안에서 남는 대여소와 모자란 대여소를 짝지어, 가까운 곳끼리 많이 옮기도록(옮기는 대수 × 이동시간의 합이 가장 작게) 배분합니다. 덩어리 경계를 넘는 이동은 없습니다.
  5. 차량이 도는 순서를 짭니다 — 경로 차고지에서 출발해 맡은 대여소를 다 들르고 돌아옵니다. 순서와 예상 시간은 대여소 사이 직선거리에 실측 도로 계수를 적용해 계산합니다 — 구간마다 고정 5분 20초 + 32 km/h로, TMAP 실도로 소요시간에 맞춘 값입니다. 지도에 그리는 선은 가능하면 실제 도로(TMAP)를 따릅니다.

지금 설정

보유 차량21대
한 회차 투입 상한최대 21대
실제로 나간 대수회차당 14대
한 대 적재 용량최대 10대
이동시간 모형고정 5분 20초 + 32 km/h (실도로 계수)
한 회차 시간 예산120분

투입 상한은 말 그대로 상한입니다. 실제로 몇 대가 나갈지는 그 회차의 일감이 정합니다. 보유 차량 대수와 회차당 투입 상한은 실행할 때마다 폼에서 바꿀 수 있고, 적재 용량·주행 속도·시간 예산은 코드 설정값입니다.

오늘은 평일입니다

휴일 = 주말 + 공휴일. 달력으로 자동 판정합니다.

평일과 휴일은 자전거가 흐르는 방향 자체가 다릅니다. 평일 아침에 사람이 몰리는 대여소가 휴일에는 텅 비기도 합니다. 그래서 두 날을 한 통계로 섞으면 서로 상쇄돼 “아무 데도 옮길 필요가 없다”는 잘못된 답이 나옵니다. 한 번에 한쪽만 계획합니다.

입력 항목

실행 화면의 각 칸이 무슨 뜻인지입니다. 처음에는 기본값 그대로 두시면 됩니다.

항목무엇인가언제 바꾸나
실행 이름 이번 계획에 붙일 이름. 결과 파일과 기록에 그대로 들어갑니다. 조건을 바꿔 여러 번 돌리고 서로 비교할 때. 이름이 같으면 덮어씁니다.
순수요 기간 대여소별 평균·편차를 뽑을 달. 계산해 둔 달만 목록에 나옵니다 (25년 11월 표기). 기본값은 가진 것 중 가장 최근 달입니다.
한 달치만 보는 것이 아닙니다 — 계획 대상 달(웹 실행은 오늘이 속한 달)의 첫 14일 순수요가 계산돼 있으면 계절 배율을 곱해 수준을 맞추고, 없으면 이 달 통계를 그대로 씁니다. 적용됐는지는 실행 로그의 ‘계절 보정’ 줄에서 확인하세요.
과거를 더 넣어 보면 오히려 나빠집니다(직전 1달 → 같은 계절 → 전체 순으로 예측 오차가 커집니다). 값이 다른 달이 섞여 중심이 틀어지기 때문이고, 12개월 자료로 확인했습니다. 그러니 최근 1달을 쓰는 것은 자료가 모자라서가 아니라 재 보고 고른 것입니다.
계절이 바뀌었을 때. 여름 계획에 겨울 수요를 쓰면 맞지 않습니다. 평소엔 기본값(최근 달) 그대로 두세요.
시간대 하루 중 어느 회차를 계획할지. 창은 넷입니다(낮 셋은 5시간, 자정을 넘기는 _20_05는 9시간): _05_10(05~10시 (출근)), _10_15(10~15시 (낮)), _15_20(15~20시 (퇴근)), _20_05(20~05시 (야간)). 고르지 않습니다. 재고를 받은 시각에 출발 앞당김 30분을 더한 시각이 드는 회차 하나를 세웁니다(23시 11분이면 23시 41분이 드는 20~05시). 계획은 그 실행에서 받은 재고 한 장으로 세웁니다. 그래서 그 재고로 곧 출발할 회차만 맞습니다. 다음 회차 계획은 그 회차 출발 무렵에 다시 실행하세요. 'API 수집 생략'을 켜면 물려받는 재고의 시각으로 정합니다.
요일 구분 평일용인지 휴일용인지. 자동이면 계획 대상일을 달력으로 판정합니다. 평소엔 자동. 미리 휴일 계획을 만들어 둘 때만 직접 고릅니다.
보유 차량 대수 오늘 실제로 굴릴 수 있는 트럭 수. 정비나 결원으로 차가 빠졌을 때. 줄이면 남는 차량은 배정에서 빠집니다.
회차당 투입 상한 한 번에 동시에 내보낼 수 있는 최대 대수. 실제 대수는 그 회차의 일감(옮길 자전거 수·들를 대여소 수)이 정합니다. 현장에서 한 번에 낼 수 있는 차·기사가 정해져 있을 때 줄입니다. 줄여서 일감을 다 못 담으면 몇 대가 모자란지 실행 기록에 남습니다.
원천 대여 이력 CSV 대여이력이 DB에 아직 없는 기간을 계산할 때만 읽는 원본 CSV. 그 기간 한 달치만 담긴 파일이어야 합니다 — 여러 달이 섞인 파일을 넣으면 전부 그 한 달로 계산됩니다. 목록의 기간이 이미 DB에 있으면 이 칸은 쓰이지 않습니다. 평소엔 건드리지 않습니다. 새 달은 순수요를 먼저 계산해야 기간 목록에 나오므로, 그 계산을 터미널에서 한 뒤 이 폼에서 고르세요.
API 수집 생략 실시간 재고를 새로 받지 않고 가장 최근 실행의 재고를 물려받습니다. 어느 실행에서 물려받았는지는 로그에 남습니다. 방금 받아 뒀고 재고가 안 변했을 때만. 평소엔 끄세요 — 그 시점 재고로 계획을 세우게 됩니다.
EDA 생략 탐색용 분석 그래프를 만들지 않습니다. 계획 결과에는 영향이 없고 시간만 줄어듭니다. 켜 두세요.

결과 읽는 법

화면 세 곳에 결과가 나뉘어 있습니다. 목적이 서로 다릅니다.

🗺 지도 — 경로를 눈으로 확인

대여소를 어떤 순서로 들르는지 지도 위에 그린 것입니다. 기사에게 줄 수량표는 옆의 작업지시서입니다. 지도는 새 창으로 열어 인쇄하거나 화면째 전달할 수 있습니다.

📋 작업지시서 — 기사에게 줄 종이

차량 한 대가 한 장입니다. 들를 순서·대여소·싣고 내릴 수량·차에 남는 대수가 표로 나옵니다. 인쇄하면 차량마다 쪽이 나뉩니다. 출발 직전에 “지금 재고와 대조하기”를 누르면 계획 대상 대여소만 다시 조회해 집행 가능한지 알려 줍니다. 작업 중에는 기사가 현장 앱 경로 화면에서 “지금 재고로 재판정”을 누르면 끝낸 곳은 빼고 남은 대여소만 다시 판정합니다 — 앞에서 덜 실었으면 뒤에서 내릴 수 있는 대수도 그만큼 줄여 셉니다.

📊 성과 지표 — 이 계획이 쓸 만한가

계획이 목표를 얼마나 채웠는지, 그러려고 얼마나 움직였는지입니다. 지난 실행과 견줘 이번 조건이 나은지 판단할 때 봅니다.

지표가 뜻하는 것

지표뜻어떻게 볼까
평균 개선률 목표 재고까지 모자란 양을 계획이 몇 % 메웠는가. “계획을 얼마나 지켰나”이지 “수요를 맞췄나”가 아닙니다. 아래 한 번에 닿는 범위와 같이 보세요.
한 번에 닿는 범위 · 격차 계획 전에 목표까지 모자란 양이 트럭 적재 용량(최대 10대) 안이라 한 번 방문으로 채울 수 있었던 대여소의 비율과, 모자란 양의 중앙값 · 최댓값. 성과가 아니라 일감의 크기입니다 — 계획을 세우기 전에 정해집니다. 낮으면 한 회차로는 못 푸는 일이 많았다는 뜻이라 개선률이 낮게 나와도 계획 탓이 아닙니다. 그래서 맨 위 타일이 아니라 표의 참고 열(‘모든 열 보기’)에 둡니다.
결품 시간 자전거가 한 대도 없어 빌리려던 사람이 그냥 돌아가는 시간 (대여소·하루 평균). 이용자 불편에 가장 가까운 지표입니다. 다만 집행 후 관측이 아니라 시뮬레이션입니다.
이동거리 모든 차량이 움직인 거리의 합. 연료·인건비에 직결됩니다. 개선률이 같다면 짧은 쪽이 낫습니다.
km당 개선 1km 움직여 불균형을 몇 대 줄였는가(대/km). 투입 대비 효율입니다. 조건을 바꿔 가며 비교할 때 유용합니다.
최장 작업 · 예산 준수 가장 오래 걸린 차량의 작업 시간과, 120분 안에 작업을 끝낸 차량의 비율. 넘긴 회차는 붉은 배경에 ⚠로 표시됩니다. 시간 예산은 운영 제약이 아니라 연구에서 세운 목표라 넘겨도 집행은 됩니다 — 다만 겨냥한 시간대를 지나 효과가 줄 수 있습니다.
차량 간 작업량 차이 가장 적게 일한 차량과 많이 일한 차량의 누적 시간 차이. 작을수록 고르게 나뉜 것입니다. 특정 차량에 일이 몰리는지 봅니다.
조건이 다른 실행끼리 개선률을 그대로 비교하지 마세요. 목표 재고를 넉넉하게 잡을수록 기준 자체가 높아져, 똑같이 일해도 개선률은 낮게 나옵니다. 비교는 같은 조건에서 한 가지만 바꿨을 때에만 뜻이 있습니다.

이럴 땐

상황어떻게 하나
성과 지표의 "수요 예측은 얼마나 맞나"가 비어 있습니다 그 절은 목표 재고의 전제(평균 순수요 mu)가 다음 달을 얼마나 맞히는지 재는 것이라 계획 실행과 별개로, 터미널에서 한 번 돌려야 채워집니다.
python tools/backtest_demand.py
한 달 자료로 만든 mu가 다음 달 수요를 얼마나 맞혔는지 월쌍으로 쌓아 둡니다. 평일·휴일은 --day-type으로 따로 재고, 값이 남으면 docs/분석/KPI.md E장에서 읽는 법을 설명합니다.
실행이 실패했습니다 실행 화면에서 그 작업을 열면 진행 단계에 붉게 표시된 곳이 있습니다. 거기서 멈춘 것이니, 아래 로그의 마지막 부분에서 원인을 봅니다. 앞 단계 결과가 다음 단계 입력이라 중간부터 다시 돌릴 수는 없습니다.
새 실행 버튼이 없습니다 이미 하나가 돌고 있습니다. 같은 자리에 진행 상황 링크가 나옵니다. 기다리거나, 그 화면에서 실행 중단을 누르세요.
예산을 넘긴 회차가 있습니다 먼저 폼의 회차당 투입 상한을 보유 대수보다 줄여 두었는지 보세요 — 줄였다면 일감보다 적게 나가 한 대가 맡는 덩어리가 커집니다(몇 대가 모자랐는지 실행 기록에 남습니다). 기본값에서는 상한이 보유 대수(21대)와 같아 상한이 원인이 아니고, 그 회차의 한 덩어리에 일감이 몰린 것입니다. 시간 예산은 운영 제약이 아니라 목표라 넘겨도 집행은 됩니다. 넘기지 않게 짜려면 터미널에서 --enforce-time-budget으로 실행하세요(예산을 넘는 작업은 미집행으로 남습니다). 상한을 10 → 15~18대로 올려 초과가 60% → 2%로 떨어진 실측은 상한이 10대이던 1.19.1 이전 이야기입니다.
결과가 비어 있습니다 아직 한 번도 끝까지 돌지 않았거나, 도중에 실패했습니다. 실행 이력에서 마지막 작업의 상태를 확인하세요.
차가 몇 대 빠졌습니다 실행할 때 보유 차량 대수를 실제 대수로 줄이세요. 남는 차량은 배정에서 자동으로 빠지고, 나중에 늘리면 다시 들어옵니다.
계획을 세워 뒀는데 재고가 바뀌었습니다 작업지시서에서 “지금 재고와 대조하기”를 누르세요. 계획 대상 대여소만 다시 조회해 가능·부족·넘침·불가로 나눠 줍니다. 계획을 자동으로 바꾸지는 않습니다 — 몇 대를 조정할지는 현장에서 정합니다. 차이가 너무 크면 계획을 다시 실행하는 편이 낫습니다.
표를 원하는 순서로 보고 싶습니다 열 제목을 누르면 그 열로 정렬됩니다. 한 번 더 누르면 반대 방향입니다. 숫자 열은 크기순, 글자 열은 가나다순이고 빈칸은 늘 맨 뒤로 갑니다. 링크만 있는 열(‘열기’·‘바로 보기’)은 정렬되지 않습니다.
지난 결과를 다시 보고 싶습니다 지우지 않는 한 남아 있습니다. 실행 화면의 저장된 실행 표에서 이름으로 찾거나, 지표·차량 화면 위쪽의 실행 이름 버튼으로 걸러 보세요.

재고 수집 — 실측 재고를 쌓는 일

계획과는 별개로, 대여소 재고를 10분마다 받아 쌓는 수집기가 따로 돕니다. 쌓인 기록은 재고 수집 화면에서 보고, 계획이 복원한 결품을 실제 관측과 맞대어 보는 데 씁니다.

어떻게 시작하나 저장소 폴더의 PowerShell에서 한 번만 등록합니다 — .\scripts\collector.ps1 install -Window 07:00-23:00 -IncludeHolidays. ⚠️ 인자를 빼면 기본값인 평일 09:00–17:00으로 등록되고, 이미 등록된 작업까지 그 값으로 덮어씁니다.
멈추고 다시 켜기 pause(작업은 남기고 안 깨움) · resume · uninstall(작업 삭제). 셋 다 모은 데이터는 지우지 않습니다. 상태는 status로 봅니다.
언제 비나 PC가 꺼져 있거나 절전으로 내려가면 그 구간이 통째로 빕니다. 망이 끊기면 스크립트는 돌지만 값을 못 받아 실패로 남습니다 — 수집 화면이 둘을 갈라 보여 줍니다.

용어

말뜻
순수요 한 대여소에서 빌려 나간 수 − 반납된 수. 양수면 자전거가 빠져나가는 곳(채워 줘야 함), 음수면 쌓이는 곳(빼내야 함)입니다.
목표 재고 그 시간대를 버티려면 몇 대가 있어야 하는가. 평균 수요에 여유분을 더해 잡습니다 — 평균만 맞추면 절반은 모자라기 때문입니다.
군집 가까운 대여소를 묶은 덩어리. 차량 한 대가 한 덩어리를 맡습니다. 덩어리 수 = 그 회차에 나가는 차량 수이고, 일감에 따라 회차마다 달라집니다.
회차 한 번 나갔다 돌아오는 것. 화면의 _10_15 같은 표시는 그 회차가 겨냥한 시간대입니다.
작업 시간 차고지를 떠나 대여소를 다 돌고 차고지로 돌아올 때까지입니다. 이동 시간 + 자전거를 싣고 내리는 시간이고, 주행은 직선거리에 실도로 계수(고정 5분 20초 + 32 km/h)를 적용해 잡습니다.
시간 예산 한 회차를 마치는 목표 시간(120분). 연구에서 세운 값이라 운영 제약이 아닙니다 — 넘겨도 계획은 그대로 집행할 수 있고, 겨냥한 시간대를 지나 효과가 줄어들 뿐입니다.
실행 이름 한 번의 계산에 붙인 이름. 결과 파일과 기록을 이 이름으로 묶습니다.
휴일 주말 + 공휴일. 달력으로 자동 판정합니다.

계획이 쓰는 기준

현장에서 “왜 이 대여소는 안 건드리지?”, “왜 여기까지만 채우지?” 라는 질문이 나오는 자리입니다. 코드에만 있던 값을 여기에 밝혀 둡니다.

기준값왜
몇 곳까지 손대나 수거·배송 각 상위 50곳 작업량이 큰 순으로 싣는 곳·내리는 곳을 각 50곳까지만 보고, 그 뒤 싣는 양과 내리는 양 중 적은 쪽에 맞춰 한 번 더 자릅니다. 보유 차량 21대로 집행할 수 있는 규모에 맞춘 값이라, 모자라거나 남아도 순위 밖이면 빠집니다 — 실제로 대상을 가르는 것은 아래 문턱보다 이 제한입니다.
얼마부터 작업 대상인가 2대 초과 모자라거나 남는 양이 2대 이하면 손대지 않습니다. 한두 대를 맞추러 차를 보내는 것이 손해이고, 그 정도 차이는 이용자가 몇 시간 안에 스스로 메웁니다. (지금 자료에서는 위 순위 제한이 먼저 걸려 이 문턱으로 빠지는 대여소는 거의 없습니다.)
얼마까지 채울 수 있나 거치대 × 1.5 거치대 수를 그대로 상한으로 쓰지 않습니다. 자전거는 거치대 밖에도 세울 수 있고, 거치대가 5대뿐인 대여소에서 상한을 딱 맞추면 수요를 아예 못 담기 때문입니다. 재고 대조의 “넘침” 판정도 같은 값을 씁니다. ⚠️ 현장 확인이 안 된 값이라, 운영 규칙이 다르면 알려 주세요.
한 대에 몇 대를 싣나 최대 10대 대전교통공사 관제센터에 유선으로 확인한 최대치입니다 (답변: 통상 7대, 많이 실어야 10대). 경로는 이 최대치를 넘기지 않도록 싣고 내리는 순서를 섞으므로, 평소처럼 적게 실으면 차가 더 필요할 수 있습니다.
얼마나 넉넉히 잡나 평균 + 1.99σ 평균만 맞추면 절반은 모자랍니다. 12개월 실측 백테스트로 정한 값이며, 관행값 1.65가 아닙니다.

더 알아보기

계산 근거와 설계 판단은 프로젝트 폴더의 문서에 있습니다.

docs/분석/KPI.md 지표를 왜 이렇게 정의했고 무엇을 못 재는지
docs/구현/FLEET.md 차량 배정과 로테이션 규칙
docs/분석/DEMAND_DISTRIBUTION.md 목표 재고를 잡는 근거와 그 전제의 한계
docs/구현/WEBAPP.md 이 대시보드의 구조와 API
python -m webapp은 같은 네트워크의 다른 기기(휴대폰 등)에서도 접속되도록 0.0.0.0:8000으로 뜹니다. 로그인이 없으니 신뢰하는 사내망에서만 켜고, 이 PC에서만 쓰려면 uvicorn webapp.app:app --host 127.0.0.1 --port 8000으로 띄우세요.