# ④ 効率重視・小さめの店:向かい合わない3案(夜 20261108_traffic・80回)を、今のまま・1台・0台と同じ種どうしで比べる # 使い方: python3 traffic_compare.py (台帳は KIOXIA の unity_ledger_v1.csv を読むだけ) import csv, json, os, glob, random, statistics as st, collections U = os.path.expanduser('~/mnt/KIOXIA/r2r2r-data/service_sim/unity') R = list(csv.DictReader(open(f'{U}/unity_ledger_v1.csv'))) random.seed(0) def var(f): return "案B'" if '_noT26' in f else '案B' if '_lane' in f else '案A' if '_oneway' in f else '今のまま' def pick(prefix, rb, a, want=None): out = {} for r in R: f = r['run_folder'] if not f.startswith(prefix) or r['store'] != 'fam_p2' or r['robots'] != rb or r['arrivals_per_h'] != a or '_ruleb' in f: continue if want and var(f) != want: continue out[int(r['seed'])] = r return out M = [('entry_wait_mean', '入店待ち(分)'), ('rescues', '動かしに行った回数'), ('dish_wait_mean', '料理が届くまで(分)'), ('walk_km_per_staff', '歩く距離(km/人)'), ('staff_util', '手がふさがる割合')] def ci(df): bs = sorted(st.median(random.choices(df, k=len(df))) for _ in range(3000)) return bs[75], bs[2924] result = {} for a, old in [('28', '20261030_night'), ('36', '20261106_night_a36')]: print(f'\n==== 来客 {a}組/時(④・スタッフ2人) ====') sets = collections.OrderedDict() sets['0台(前の版)'] = pick(old, '0', a) sets['1台(前の版)'] = pick(old, '1', a) sets['2台(前の版)'] = pick(old, '2', a) for v in ['今のまま', '案A', '案B', "案B'"]: sets[f'2台 {v}'] = pick('20261108_tr', '2', a, v) base = sets['2台 今のまま']; one = sets['1台(前の版)'] for n, d in sets.items(): cs = d[min(d)]['unity_cs'] if d else '' row = {'n': len(d), 'cs': cs} txt = [] for k, lab in M: v = st.median(float(d[i][k]) for i in d) row[k] = v txt.append(f'{lab} {v:.2f}') if n.startswith('2台 ') and n != '2台 今のまま': s = sorted(set(d) & set(base)) for k, lab in M: df = [float(d[i][k]) - float(base[i][k]) for i in s] lo, hi = ci(df) row[k + '_vs_now'] = [st.median(df), lo, hi, sum(x < 0 for x in df), len(s)] txt.append(f'今のままとの差({lab}) 中央{st.median(df):+.2f} 95%[{lo:+.2f},{hi:+.2f}] 減った種{sum(x < 0 for x in df)}/{len(s)}') if n != '1台(前の版)': s = sorted(set(d) & set(one)) df = [float(d[i]['entry_wait_mean']) - float(one[i]['entry_wait_mean']) for i in s] row['entry_vs_one'] = [st.median(df), sum(x < 0 for x in df), len(s)] txt.append(f'1台との差(入店待ち) 中央{st.median(df):+.1f} 1台より短い種{sum(x < 0 for x in df)}/{len(s)}') print(f'-- {n}({len(d)}回・{cs})'); print(' ' + '\n '.join(txt)) result[f'{a}|{n}'] = row # 1本道で待った時間(案B・B') print('\n==== 1本道で待った時間(summary.json の traffic) ====') G = collections.defaultdict(list) for f in glob.glob(f'{U}/20261108_tr*_lane*/summary.json'): t = json.load(open(f))['traffic']; a = '36' if '_a36_' in f else '28'; v = "案B'" if 'noT26' in f else '案B' G[(a, v)].append(t) for (a, v), ts in sorted(G.items()): tot = [x['lane_wait_total_s'] for x in ts] print(f'来客{a} {v}: 待った合計 中央{st.median(tot):.0f}秒 最大{max(tot):.0f}秒 / 1回の最長 {max(x["lane_wait_max_s"] for x in ts):.0f}秒 / ' f'待つと決めた回数 中央{st.median(x["lane_holds"] for x in ts)} / 終わりに待ったまま {sum(x["still_holding_at_end"] for x in ts)}回 / 行けない卓 {sum(len(x["unreachable_tables"]) for x in ts)}') json.dump(result, open('traffic_compare.json', 'w'), ensure_ascii=False, indent=1)