#!/usr/bin/env python3 """依頼155:確かめの回のまとめ(依頼143・147・149・154 の check と並べる)。 python3 agg155.py <回のフォルダ> [<回のフォルダ> ...]""" import csv, json, os, sys, statistics from collections import Counter, defaultdict HOSP = "/Volumes/KIOXIA/r2r2r-data/service_sim/unity/hospital" R143 = {"hall": HOSP + "/20261110_floor143/runs/check143a_v06_hall_s0", "switch": HOSP + "/20261110_floor143/runs/check143c_v06_switch_s0"} R147 = {"hall": HOSP + "/20261110_people147/runs/check147a_v06_hall_s0", "switch": HOSP + "/20261110_people147/runs/check147c_v06_switch_s0"} R149 = {"hall": HOSP + "/20261111_flow149/runs/check149a_v06_hall_s0", "switch": HOSP + "/20261111_flow149/runs/check149c_v06_switch_s0"} R154 = {"hall": HOSP + "/20261111_flow154/runs/check154a_v06_hall_s0", "switch": HOSP + "/20261111_flow154/runs/check154c_v06_switch_s0"} EST = {"busy": (2, 8), "quiet": (1, 3)} # assumptions.json crowd_people.flow_estimate H1 = "/Users/shimazueiichi/Robotics_UX/ServiceSim/Assets/ServiceSim/Hospital/Data/hospital_h1-155.json" TN = {1: "急に止まった", 2: "ひやり", 3: "人がよけさせられた", 4: "立ち往生", 5: "横切り", 6: "止まりきれなかった"} LAY = json.load(open(H1, encoding="utf-8")) COR = {c["name"]: c["rect"] for c in LAY["layout"]["corridors"]} STAFF_COR = ["職員の中廊下", "西の縦廊下", "東の縦廊下", "外来の奥の職員通路", "東の縦廊下(北)"] WAIT = ["総合受付・会計の待合", "お薬の窓口の待合", "生理検査の待合", "化学療法の受付・待合", "救急の待合"] MIDW = ["内科の中待合", "ほかの外来の中待合"] SCOR = ["職員の中廊下", "西の縦廊下", "東の縦廊下", "外来の奥の職員通路"] ENT = ["正面玄関", "玄関の通り"] ELV = ["エレベーターホール", "エレベーター"] TOI = ["トイレ(西)", "トイレ(東)", "化学療法のトイレ"] ROLES = ("外来の患者", "化学療法の患者", "救急の患者", "看護師", "医師", "薬剤師", "検査技師", "事務") def inr(r, x, z, e=1e-4): return r[0] - e <= x <= r[2] + e and r[1] - e <= z <= r[3] + e def area3(x, z): if inr(COR["外来の大廊下"], x, z): return "外来の大廊下" if any(inr(COR[n], x, z) for n in STAFF_COR): return "職員の廊下" if inr(COR["玄関の通り"], x, z): return "玄関の通り" return "部屋の中" def rd(run, f): return list(csv.DictReader(open(os.path.join(run, f), encoding="utf-8"))) def one(run): s = json.load(open(os.path.join(run, "summary.json"), encoding="utf-8")) ev = rd(run, "events.csv") legs = {l["leg_id"]: l for l in rd(run, "legs.csv")} run_c, wait_c, ar = Counter(), Counter(), defaultdict(Counter) fast = 0 kinds = defaultdict(Counter) for e in ev: k = int(e["type"]) d = legs[e["leg_id"]]["dir"] if e["leg_id"] in legs else "" if d == "待ち": wait_c[k] += 1 continue run_c[k] += 1 kinds[k][e["partner_kind"] or "なし"] += 1 if k == 2 and e["note"] == "速いまま": fast += 1 ar[area3(float(e["robot_x"]), float(e["robot_z"]))][k] += 1 return dict(s=s, ev=ev, legs=legs, run=run_c, wait=wait_c, area=ar, fast=fast, kinds=kinds, trips=rd(run, "trips.csv"), moves=rd(run, "moves.csv"), resc=rd(run, "rescues.csv")) def line(c, ks=range(1, 7)): return "/".join("%d" % c[k] for k in ks) def hm(t): t = int(round(t)) + 7 * 3600 + 1800 return "%d:%02d" % (t // 3600, (t % 3600) // 60) def halls(run): return {float(r["t_s"]): int(r["hall_people"]) for r in rd(run, "crowd.csv")} def zones_table(run, s, run149): # run149 = 比べる相手(依頼154) zl = rd(run, "zones147.csv") byt = defaultdict(lambda: Counter()) for r in zl: t = float(r["t_s"]); n = int(r["patients"]) + int(r["staff"]) z = r["zone"] if z in WAIT: c = "待合" elif z in MIDW: c = "中待合" elif z in SCOR: c = "職員の廊下" elif z == "外来の大廊下": c = None elif z in ENT: c = "玄関" elif z in ELV: c = "エレベーターホール" # ホールと中(かご)を合わせる elif z in TOI: c = "トイレ" else: c = "部屋の中" if c: byt[t][c] += n byt[t]["all"] += n crowd = rd(run, "crowd.csv") hall = {float(r["t_s"]): (int(r["hall_people"]), int(r["hall_target"]), int(r["east_people"])) for r in crowd} h147 = halls(run149) e149 = {float(r["t_s"]): int(r["east_people"]) for r in rd(run149, "crowd.csv")} end = s["sim_s"] print("時刻ごと(30分ごとの平均。1分ごとに数えた人数。患者・付き添い・職員・エレベーターから来た人の全部)") print("時刻 大廊下x0〜70 依頼154 目安(flow_estimate) 大廊下x70〜120 依頼154 待合 中待合 部屋の中 職員の廊下 玄関・玄関の通り エレベーターホール トイレ 全部") t0 = 0.0 while t0 < end - 1: ts = [t for t in hall if t0 <= t < t0 + 1800] if not ts: break avg = lambda f: sum(f(t) for t in ts) / len(ts) hh = avg(lambda t: hall[t][0]); ee = avg(lambda t: hall[t][2]) tg = "%d〜%d" % (EST["busy"] if 1800 <= t0 < 12600 else EST["quiet"]) x49 = [e149[t] for t in e149 if t0 <= t < t0 + 1800] e49 = sum(x49) / len(x49) if x49 else float("nan") t47 = [h147[t] for t in h147 if t0 <= t < t0 + 1800] h47 = sum(t47) / len(t47) if t47 else float("nan") cs = {c: avg(lambda t: byt[t][c]) for c in ("待合", "中待合", "部屋の中", "職員の廊下", "玄関", "エレベーターホール", "トイレ", "all")} print("%s〜%s %6.1f %6.1f %8s %6.1f %6.1f %6.1f %6.1f %7.1f %7.1f %7.1f %7.1f %6.1f %6.1f" % ( hm(t0), hm(t0 + 1800), hh, h47, tg, ee, e49, cs["待合"], cs["中待合"], cs["部屋の中"], cs["職員の廊下"], cs["玄関"], cs["エレベーターホール"], cs["トイレ"], cs["all"])) t0 += 1800 busy = [hall[t][0] for t in hall if 1800 <= t < 12600] quiet = [hall[t][0] for t in hall if t < 1800 or t >= 12600] b47 = [h147[t] for t in h147 if 1800 <= t < 12600]; q47 = [h147[t] for t in h147 if t < 1800 or t >= 12600] allh = list(hall.values()) print("外来の大廊下 x0〜70:混んでいる時間帯(8:00〜11:00)平均 %.1f 人・最大 %d 人(依頼154 %.1f・%d/目安 2〜8)/空いている時間帯 平均 %.1f 人・最大 %d 人(依頼154 %.1f・%d/目安 1〜3)" % ( sum(busy) / len(busy), max(busy), sum(b47) / len(b47), max(b47), sum(quiet) / len(quiet), max(quiet), sum(q47) / len(q47), max(q47))) print("1日の平均 %.2f 人(依頼154 %.2f 人)" % (sum(h[0] for h in allh) / len(allh), sum(h147.values()) / len(h147))) pm = [hall[t][0] for t in hall if t >= 19800] # 13:00 から(午後) pm49 = [h147[t] for t in h147 if t >= 19800] print("午後(13:00〜)平均 %.1f 人・最大 %d 人(依頼154 %.1f・%d/目安 1〜3)" % (sum(pm) / len(pm), max(pm), sum(pm49) / len(pm49), max(pm49))) ea = [hall[t][2] for t in hall]; e49a = list(e149.values()) print("大廊下 x70〜120:1日の平均 %.2f 人・最大 %d 人(依頼154 %.2f 人・%d 人)" % (sum(ea) / len(ea), max(ea), sum(e49a) / len(e49a), max(e49a))) sc = [byt[t]["職員の廊下"] for t in byt] print("職員の廊下の人数:平均 %.2f 人・最大 %d 人" % (sum(sc) / len(sc), max(sc))) def people(run, s): rows = rd(run, "people147.csv") for what in ("外来の患者", "付き添い", "化学療法の患者", "救急の患者", "病棟の患者", "病棟の看護師", "見舞いの人", "ほかの階の職員"): rs = [r for r in rows if r["what"] == what] st = sorted(float(r["stay_s"]) / 60 for r in rs if r["stay_s"]) inside = sum(1 for r in rs if r["spawn_s"] and not r["exit_s"]) never = sum(1 for r in rs if not r["spawn_s"]) if len(st) >= 2: q = statistics.quantiles(st, n=10) print("%s %d 人:出た %d・回の終わりに中にいた %d・入らなかった %d/いた時間 中央値 %.1f 分・最短 %.1f・最長 %.1f(10〜90%%:%.1f〜%.1f)" % ( what, len(rs), len(st), inside, never, statistics.median(st), st[0], st[-1], q[0], q[-1])) else: print("%s %d 人:出た %d・回の終わりに中にいた %d・入らなかった %d" % (what, len(rs), len(st), inside, never)) pat = [r for r in rows if r["what"] in ("外来の患者",)] sk = Counter() for r in rows: for x in r["skipped"].split("|"): if x: sk[r["what"] + " " + x] += 1 print("飛ばした行き先(人):", "・".join("%s %d 人" % kv for kv in sk.most_common()) or "なし") print("初診・紹介 %d 人(外来の患者 %d 人)" % (sum(1 for r in pat if r["first_visit"] == "1"), len(pat))) # トイレ tw = Counter(r["what"] for r in rows if r["toilet_want"] == "1") td = Counter((r["what"], r["toilet"]) for r in rows if r["toilet"]) tl = rd(run, "toilet149.csv") print("トイレ:行くと決めた人 %s/行った人 %s" % ("・".join("%s %d" % kv for kv in tw.items()), "・".join("%s→%s %d" % (k[0], k[1], v) for k, v in sorted(td.items())))) if tl: dur = [float(r["back_s"]) - float(r["leave_seat_s"]) for r in tl] walk = [float(r["arrive_s"]) - float(r["leave_seat_s"]) for r in tl] print(" 席を立ってから戻るまで 中央値 %.1f 分(最短 %.1f・最長 %.1f)/席からトイレまで歩いた時間 中央値 %.0f 秒・最長 %.0f 秒" % ( statistics.median(dur) / 60, min(dur) / 60, max(dur) / 60, statistics.median(walk), max(walk))) st = Counter(r["step"] for r in tl) print(" どこで待っている間に行ったか:", "・".join("%s %d" % kv for kv in st.most_common())) print(" 行く時が来ないまま回が終わった %d・行けるトイレがない %d・戻るのを待って次へ行くのが遅れた %d 回(計 %.0f 秒)" % ( s["flow_toilet_not_due"], s["flow_toilet_no_way"], s["flow_toilet_extend_n"], s["flow_toilet_extend_s"])) # エレベーター el = rd(run, "elevator149.csv") ec = Counter((r["what"], r["dir"]) for r in el) print("エレベーター(種類ごと):", "・".join("%s %s %d" % (k[0], k[1], v) for k, v in sorted(ec.items()))) wk = Counter(r["kind"] for r in el if r["what"] == "病棟の患者" and r["dir"] == "エレベーターから出る") print(" 病棟の患者の形:", "・".join("%s %d" % kv for kv in wk.items())) print("出さなかった人(行き先へ歩いて行けない):", s.get("flow_not_spawned"), "/救急の患者 %d 人(正面玄関から行ける %d)" % (s["flow_er_not_spawned"], s["flow_er_reach_from_main"])) xl = rd(run, "er_cross154.csv") for z in ("救急の待合", "救急の処置の場所"): rs = [r for r in xl if r["zone"] == z] byw = Counter(r["what"] for r in rs) sec = sorted(float(r["seconds"]) for r in rs) if sec: print("%s を通り抜けた外来の患者の役割の人 %d 人(%s)/そこにいた時間 合計 %.0f 秒・1人 中央値 %.1f 秒・最長 %.1f 秒" % ( z, len(rs), "・".join("%s %d" % kv for kv in byw.items()), sum(sec), statistics.median(sec), sec[-1])) else: print("%s を通り抜けた外来の患者の役割の人 0 人" % z) print("病棟の看護師:検査の所の中のそばに立った %d・所の中の点 %d・大廊下の端で待った %d/化学療法のトイレへ行けず西・東にした %d" % ( s.get("flow_ward_nurse_inside", -1), s.get("flow_ward_nurse_inside_random", -1), s["flow_ward_nurse_hall_waits"], s.get("flow_chemo_toilet_fallback", -1))) print("案A:付き添いを呼んだ %d・付き添った %d・間に合わない %d・時間切れ %d/付き添いなしで裏の通路にいた患者 %.1f 秒" % ( s["flow_escort_calls"], s["flow_escorts"], s["flow_escort_late"], s["flow_escort_timeouts"], s["flow_back_no_escort_s"])) pr = [r for r in pat if "処置・検査室" in r["steps"]] went = sum(1 for r in pr if any(x.startswith("処置・検査室:") and x.split(":")[3] for x in r["steps"].split("|"))) print(" 処置・検査室・内視鏡室の行き先がある外来の患者 %d 人・着いた %d 人" % (len(pr), went)) def east_end(a): return sum(1 for e in a["ev"] if (a["legs"][e["leg_id"]]["dir"] if e["leg_id"] in a["legs"] else "") != "待ち" and float(e["robot_x"]) >= 95 and float(e["robot_z"]) >= 27) def prob(a, b143, b147, b149, b154): L = [a, b154, b149, b147, b143] f = lambda g: " / ".join(g(x) for x in L) print("問題の数(1 急に止まった/2 ひやり/3 人がよけさせられた/4 立ち往生/5 横切り/止まりきれなかった) 依頼155 / 依頼154 / 依頼149 / 依頼147 / 依頼143") print(" 走っている間 全部: " + f(lambda x: line(x["run"])) + " (合計 " + f(lambda x: "%d" % sum(x["run"].values())) + ")") for ar in ("外来の大廊下", "職員の廊下", "部屋の中", "玄関の通り"): print(" %s: " % ar + f(lambda x: line(x["area"][ar]))) print(" 台の前で待っている間: " + f(lambda x: line(x["wait"]))) print(" 速いままのひやり: " + f(lambda x: "%d" % x["fast"])) print(" 助け: " + f(lambda x: "%d" % len(x["resc"]))) print(" 大廊下の東の端(走っている間・ロボットの中心 x95 以上・z27 以上): " + f(lambda x: "%d" % east_end(x))) def chemo155(run, s): rows = rd(run, "people147.csv") ch = [r for r in rows if r["what"] == "化学療法の患者"] want = [r for r in ch if r["toilet_want"] == "1"] td = Counter(r["toilet"] or "(まだ・行く時が来ない)" for r in want) print("化学療法の患者 %d 人のうちトイレに行くと決まった %d 人:行き先 %s/化学療法のトイレへ行けず西・東にした %d 人" % ( len(ch), len(want), "・".join("%s %d" % (k, td.get(k, 0)) for k in ("化学療法のトイレ", "トイレ(西)", "トイレ(東)", "行けない", "(まだ・行く時が来ない)")), s.get("flow_chemo_toilet_fallback", -1))) ids = set(r["id"] for r in ch) tl = [r for r in rd(run, "toilet149.csv") if r["what"] == "化学療法の患者"] for r in tl: print(" id %s %s 席を立つ %s 秒・着く %s 秒(歩き %.1f 秒)・戻る %s 秒(帰り %.1f 秒)" % (r["id"], r["toilet"], r["leave_seat_s"], r["arrive_s"], float(r["arrive_s"]) - float(r["leave_seat_s"]), r["back_s"], float(r["back_s"]) - float(r["stay_end_s"]))) wy = rd(run, "chemo_way155.csv") bw = defaultdict(list) for r in wy: bw[r["what"]].append(r) print("トイレの前の通り道(x114〜115.5・z53〜60)にいた人 %d 人:" % len(wy)) for w, rs in bw.items(): print(" %s %d 人・いた時間 合計 %.0f 秒(1人 最長 %.1f 秒)・歩いている途中で止まっていた 合計 %.2f 秒・続けて止まった最長 %.2f 秒" % ( w, len(rs), sum(float(r["seconds"]) for r in rs), max(float(r["seconds"]) for r in rs), sum(float(r["stopped_s"]) for r in rs), max(float(r["longest_stop_s"]) for r in rs))) print(" 続けて止まった最長(全員)%.2f 秒" % s.get("flow_chemo_way_longest_stop_s", -1)) def staff155(run): st = rd(run, "staff147.csv") posts = defaultdict(set); own = {} for r in st: posts[r["role"]].add(r["zone"]); own[r["staff_id"] if "staff_id" in r else r["id"]] = r["zone"] mv = rd(run, "staff_moves147.csv") w = Counter(m["what"] for m in mv) print("職員の動き(終わったもの):", "・".join("%s %d" % kv for kv in w.items())) c = Counter(); non = 0; ownn = 0 for m in mv: if m["what"] != "持ち場から動く": continue c[(m["role"], m["dest"])] += 1 if m["dest"] not in posts[m["role"]]: non += 1 if m["dest"] == own[m["staff_id"]]: ownn += 1 print("ふだんの行き来(持ち場から動く)の行き先:役割ごと・所ごとの回数(*=その役割の持ち場がある所)") for role in ("医師", "看護師", "薬剤師", "検査技師", "事務"): ks = sorted(k for k in c if k[0] == role) print(" %s 計 %d:%s" % (role, sum(c[k] for k in ks), "・".join("%s%s %d" % (k[1], "*" if k[1] in posts[role] else "(持ち場でない)", c[k]) for k in ks))) print(" 持ち場がある所:" + "・".join(sorted(posts[role]))) print(" 持ち場でない所へ行った回数 %d/自分の持ち場の所が行き先 %d" % (non, ownn)) def main(): b143 = {k: one(v) for k, v in R143.items()} b147 = {k: one(v) for k, v in R147.items()} b149 = {k: one(v) for k, v in R149.items()} b154 = {k: one(v) for k, v in R154.items()} for run in sys.argv[1:]: a = one(run) s = a["s"] key = "switch" if s["plan_key"] == "switch" else "hall" print() print("== %s(%s)status %s/回の終わり %.1f 秒(%s)/最後の便が戻った %.1f 秒/実時間 %.1f 分" % ( os.path.basename(run), s["plan"], s["status"], s["sim_s"], hm(s["sim_s"]), s["last_trip_end_s"], s["wall_s"] / 60)) print("便 %d(走った %d・途中 %d・走れなかった %d)/移る %d(終えた %d)/出るのが遅れた 平均 %.1f 秒・最大 %.1f 秒" % ( s["trips_scheduled"], s["trips_done"], s["trips_unfinished_at_end"], s["trips_not_run_after_end"] + s["trips_not_run_other"], s["moves"], s["moves_done"], s["delay_mean_s"], s["delay_max_s"])) print("瞬間移動 ロボット %d(1コマ最大 %.3f m)・人 %d(最大 %.3f m)/人の引き直し(180秒動けない)%d/行き先の近くで止まって待った %d" % ( s["robot_jumps"], s["robot_max_step_m"], s["person_jumps"], s["person_max_step_m"], s.get("flow_stuck_regoes", 0), s.get("flow_near_stops", 0))) st = [e for e in a["ev"] if e["type"] == "4"] print("立ち往生 %d: %s" % (len(st), "/".join("%s〜%s秒 %s秒 (%s,%s) %s" % (e["t_s"], e["end_s"], e["value"], e["robot_x"], e["robot_z"], e["note"]) for e in st))) print("助け %d: %s" % (len(a["resc"]), "/".join("%s秒 (%s,%s) %s・%s" % (r["call_s"], r["x"], r["z"], r["partner"], r["outcome"]) for r in a["resc"]))) print("ロボットの入らない所に中心が入った時間 %.2f 秒/どの所にも入っていない時間 %.2f 秒" % (s["robot_forbidden_s"], s["robot_no_zone_s"])) if s.get("people_mode") == "flow": print("人が access で入れない所に入った時間(役割ごと):", "・".join("%s %.2f 秒" % (r, s["flow_forbidden_s_" + r]) for r in ROLES)) print("どの所にも入っていない時間(役割ごと):", "・".join("%s %.2f 秒" % (r, s["flow_no_zone_s_" + r]) for r in ROLES)) print("職員 %d 人/持ち場から動いた %d 回・看護師が呼びに出た %d 回(看護師なし %d・間に合わない %d)" % ( s["flow_staff_total"], s["flow_staff_moves"], s["flow_nurse_calls"], s["flow_nurse_calls_no_nurse"], s["flow_nurse_late"])) print("席 %d(使えない %d)・机などの横の点 %d・診察室 %d/所 %d 枚・区画 %d/壁の箱 %d・家具の箱 %d" % ( s["flow_seats"], s["flow_seats_dropped"], s["flow_spots"], s["flow_consult_rooms"], s["flow_pieces"], s["flow_areas"], s["flow_wall_boxes"], s["flow_furniture_boxes"])) print("飛ばした行き先(理由):", s["flow_skips"]) people(run, s) chemo155(run, s) staff155(run) zones_table(run, s, R154[key]) prob(a, b143[key], b147[key], b149[key], b154[key]) lc = Counter() for e in a["ev"]: d = a["legs"][e["leg_id"]]["dir"] if e["leg_id"] in a["legs"] else "" lc[d] += 1 print(" 区間ごとの数(全部の種類):", "・".join("%s %d" % kv for kv in lc.items())) for k in range(1, 7): if a["kinds"][k]: print(" 相手 %d %s: %s" % (k, TN[k], "・".join("%s %d" % kv for kv in a["kinds"][k].most_common()))) if __name__ == "__main__": main()