SCMOPTのケーススタディ 2
df.tail()
def simple_cycles2(G, limit):
subG = type(G)(G.edges())
sccs = list(nx.strongly_connected_components(subG))
while sccs:
scc = sccs.pop()
startnode = scc.pop()
path = [startnode]
blocked = set()
blocked.add(startnode)
stack = [(startnode, list(subG[startnode]))]
while stack:
thisnode, nbrs = stack[-1]
if nbrs and len(path) < limit:
nextnode = nbrs.pop()
if nextnode == startnode:
yield path[:]
elif nextnode not in blocked:
path.append(nextnode)
stack.append((nextnode, list(subG[nextnode])))
blocked.add(nextnode)
continue
if not nbrs or len(path) >= limit:
blocked.remove(thisnode)
stack.pop()
path.pop()
subG.remove_node(startnode)
H = subG.subgraph(scc)
sccs.extend(list(nx.strongly_connected_components(H)))
dem = od_df.SHASHU_SAIDAISEKISAIRYO_CD_1.astype(int, errors="ignore")
dem.hist()
%matplotlib inline
#地点名も出すようにする!
data =[go.Scattermapbox(
lat=lat_head,
lon=lon_head,
marker = {'size': num},
hoverinfo='text',
hovertext= head,
text= head,
mode="markers",
name="車庫"
)]
data.append(
go.Scattermapbox(
lat=lat_switch,
lon=lon_switch,
marker = {'size': 5},
hoverinfo='text',
text= switch,
mode="markers",
name="中継地点"
)
)
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig);
cust_df = pd.read_csv(folder+ "Cust.csv")
cust_df.columns= ["name","lat","lon"]
cust_df["lat"] = cust_df["lat"].astype(float)
cust_df["lon"] = cust_df["lon"].astype(float)
#cust_df.head()
fig = plot_cust(cust_df)
plotly.offline.plot(fig);
total_demand_df = pd.read_csv(folder+ "Cust-Prod.csv")
total_demand_df.columns=["cust","prod","demand"]
#total_demand_df.head()
dc_df = pd.read_csv(folder+ "DC.csv")
dc_df.columns=["name","fc","vc","lb","ub","lat","lon"]
dc_df.lb = 0.
#dc_df.head()
plnt_df = pd.read_csv(folder+ "Plnt.csv")
plnt_df.columns=["name","lat","lon"]
#plnt_df.head()
prod_df = pd.read_csv(folder+ "Prod.csv")
prod_df.columns=["name","weight"]
#prod_df
plnt_prod_df = pd.read_csv(folder+"Plnt-Prod.csv")
plnt_prod_df.columns=["plnt","prod","ub"]
#plnt_prod_df.head()
df = enumerate_near_points(cust_df, 0.0001)
#df
durations, distances, node_df = compute_durations(cust_df, plnt_df)
trans_df, graph, position = make_network_using_road(cust_df, dc_df, plnt_df, durations, distances, plnt_dc_threshold = 10000., dc_cust_threshold = 200. )
# 大圏距離で代用する場合には,以下を生かす
#trans_df, graph, position = make_network(cust_df, dc_df, plnt_df, plnt_dc_threshold = 10000., dc_cust_threshold = 500. )
fig = px.histogram(total_demand_df,"demand")
plotly.offline.plot(fig);
total_demand_df = total_demand_df[ total_demand_df.demand >= 2000. ]
print(len(cust_df))
cust_df = remove_zero_total_demand(total_demand_df, cust_df)
print(len(cust_df))
flow_df, dc_df, cost_df, violation_df, status = solve_lnd(prod_df, cust_df, dc_df, plnt_df, plnt_prod_df, total_demand_df,
trans_df, dc_num= (5,13), single_sourcing=True, max_cpu = 100)
cost_df
fig = px.bar(cost_df, y="cost", x="value", orientation='h')
plotly.offline.plot(fig);
cost_df.value.sum()
fig = show_optimized_network(cust_df, dc_df, plnt_df, prod_df, flow_df, position)
plotly.offline.plot(fig);
weight_of_prod ={p:w for (p,w) in zip(prod_df.name, prod_df.weight) } #prepare weight dic.
weight_of_cust = defaultdict(int)
for row in total_demand_df.itertuples():
weight_of_cust[row.cust] += weight_of_prod[row.prod]*row.demand
weight = [weight_of_cust[i] for i in weight_of_cust]
durations, distances, node_df = compute_durations(cust_df, None)
fig = elbow_method(cust_df, weight, n_lb = 3, n_ub = 60, repetitions=1, method="hierarchical", durations=durations)
plotly.offline.plot(fig);
X, Y, partition, cost = hierarchical_clusterning(cust_df, weight, durations, num_of_facilities = 50, linkage="complete")
print(cost)
fig = show_optimized_continuous_network(cust_df, X, Y, partition, weight= weight)
plotly.offline.plot(fig);
aggregated_cust_df, aggregated_total_demand_df = make_aggregated_df(cust_df, None, total_demand_df, X, Y, partition, weight)
aggregated_dc_df = generate_dc(aggregated_cust_df, aggregated_total_demand_df, prod_df, num_dc=10, lb_ratio=0.0, ub_ratio=10.3,vc_bounds=(19,21),fc_bounds=(0,1))
durations, distances, node_df = compute_durations(aggregated_cust_df, plnt_df)
aggregated_trans_df, graph, position = make_network_using_road(aggregated_cust_df, aggregated_dc_df, plnt_df, durations, distances, plnt_dc_threshold = 10000., dc_cust_threshold = 500. )
flow_df, dc_df, cost_df, violation_df, status = solve_lnd(prod_df, aggregated_cust_df,aggregated_dc_df, plnt_df, plnt_prod_df, aggregated_total_demand_df,
aggregated_trans_df, dc_num= (5,13), single_sourcing=False, max_cpu = 100)
fig = show_optimized_network(aggregated_cust_df, aggregated_dc_df, plnt_df, prod_df, flow_df, position)
plotly.offline.plot(fig);
cost_df
上の費用のうち,顧客への配送費用は,集約した点に対するものなので,本来の費用ではない.本来の費用を再計算する.
from_dc = {} #集約した顧客に入る倉庫の名称を入れた辞書(集約した顧客名をキーとする)
for row in flow_df.itertuples():
if row.to_node[:5]=="Cust_":
from_dc[row.to_node[5:]] = row.from_node[3:]
#開設した集約倉庫に最も近い顧客を探索
from scipy.spatial import KDTree
points = [(i,j) for (i,j) in zip(cust_df.lat, cust_df.lon)]
tree = KDTree(points)
dc_cust_dic={} #集約した倉庫の名前を入れると,それに最も近い,もとの顧客indexを返す辞書
for row in dc_df[ dc_df.open_close==1 ].itertuples():
dis, near = tree.query( (row.lat, row.lon) )
dc_cust_dic[row.name] = cust_df.loc[int(near),"index"]
durations, distances, node_df = compute_durations(cust_df, None)
#dc_set = set(from_dc.values()) #from_dc: 各集約顧客が,どのDCから来ているかを表す辞書
#agg_cust_set = set(from_dc.keys())
dc_per_dis = 10./4000
dc_per_time = 8000./4000
total_del = 0.
for row in aggregated_cust_df.itertuples():
dc_name = from_dc[row.name]
#print(dc_name, row.name)
i = dc_cust_dic[dc_name]
for c in row.customers:
j = cust_df.loc[c,"index"]
if distances[i][j] < 9999999:
dis = distances[i][j]/1000.
time = durations[i][j]/3600.
cost_per_kg = dis*dc_per_dis + time*dc_per_time
total_del += weight[j]*cost_per_kg
total_del
配送費用は,クラスタリングした場合より大きくなり,総費用は以下のように増加する. これは,需要が2000未満のものを削除して最適化した場合の1.4%増であり,良い近似になっていると考えられる.
total = cost_df.value[0] + cost_df.value[3] + total_del
print(total, total/1986774926)
durations, distances, node_df = compute_durations(cust_df, None)
X, Y, partition, best_ub, lb_list, ub_list, phi_list = solve_k_median(cust_df, weight, durations, num_of_facilities=51,
max_iter=10000, max_lr=0.01, moms=(0.85,0.95), convergence=1e-5, lr_find = True, adam=False)
fig = plot_k_median_lr_find(phi_list, lb_list)
plotly.offline.plot(fig);
#lr_finder => 0.01 , adam => 50000
X, Y, partition, best_ub, lb_list, ub_list, phi_list = solve_k_median(cust_df, weight, durations, num_of_facilities=51,
max_iter=2000, max_lr=0.05, moms=(0.85,0.95), convergence=1e-5, lr_find = False, adam=False)
fig = plot_k_median(lb_list, ub_list)
plotly.offline.plot(fig);
fig = show_optimized_continuous_network(cust_df, X, Y, partition, weight= weight)
plotly.offline.plot(fig);
aggregated_cust_df, aggregated_total_demand_df = make_aggregated_df(cust_df, None, total_demand_df, X, Y, partition, weight)
aggregated_dc_df = generate_dc(aggregated_cust_df, aggregated_total_demand_df, prod_df, num_dc=10, lb_ratio=0.0, ub_ratio=10.3,vc_bounds=(19,21),fc_bounds=(0,1))
durations, distances, node_df = compute_durations(aggregated_cust_df, plnt_df)
trans_df, graph, position = make_network_using_road(aggregated_cust_df, aggregated_dc_df, plnt_df, durations, distances, plnt_dc_threshold = 10000., dc_cust_threshold = 500. )
flow_df, dc_df, cost_df, violation_df, status = solve_lnd(prod_df, aggregated_cust_df,aggregated_dc_df, plnt_df, plnt_prod_df, aggregated_total_demand_df,
trans_df, dc_num= (5,13), single_sourcing=True, max_cpu = 100)
cost_df
fig = show_optimized_network(aggregated_cust_df, aggregated_dc_df, plnt_df, prod_df, flow_df, position)
plotly.offline.plot(fig);
aggregated_cust_df.head()
try:
cust_df.reset_index(inplace=True)
except:
pass
from_dc = {} #集約した顧客に入る倉庫の名称を入れた辞書(集約した顧客名をキーとする)
for row in flow_df.itertuples():
if row.to_node[:5]=="Cust_":
from_dc[row.to_node[5:]] = row.from_node[3:]
#開設した集約倉庫に最も近い顧客を探索
from scipy.spatial import KDTree
points = [(i,j) for (i,j) in zip(cust_df.lat, cust_df.lon)]
tree = KDTree(points)
dc_cust_dic={} #集約した倉庫の名前を入れると,それに最も近い,もとの顧客indexを返す辞書
for row in dc_df[ dc_df.open_close==1 ].itertuples():
dis, near = tree.query( (row.lat, row.lon) )
dc_cust_dic[row.name] = cust_df.loc[int(near),"index"]
durations, distances, node_df = compute_durations(cust_df, None)
#dc_set = set(from_dc.values()) #from_dc: 各集約顧客が,どのDCから来ているかを表す辞書
#agg_cust_set = set(from_dc.keys())
dc_per_dis = 10./4000
dc_per_time = 8000./4000
total_del = 0.
for row in aggregated_cust_df.itertuples():
try:
dc_name = from_dc[row.name]
except:
continue
i = dc_cust_dic[dc_name]
for c in row.customers:
j = cust_df.loc[c,"index"]
if distances[i][j] < 9999999:
dis = distances[i][j]/1000.
time = durations[i][j]/3600.
cost_per_kg = dis*dc_per_dis + time*dc_per_time
total_del += weight[j]*cost_per_kg
total_del
cost_df.value[0] + cost_df.value[3] + total_del
2005167825/1986774926
weight_of_prod ={p:w for (p,w) in zip(prod_df.name, prod_df.weight) } #prepare weight dic.
weight_of_cust = defaultdict(int)
for row in total_demand_df.itertuples():
weight_of_cust[row.cust] += weight_of_prod[row.prod]*row.demand
weight = [weight_of_cust[i] for i in weight_of_cust]
durations, distances, node_df = compute_durations(cust_df, None)
durations, distances, node_df = compute_durations(cust_df, None)
X, Y, partition, cost = kmeans(cust_df, weight, num_of_facilities =50)
fig = show_optimized_continuous_network(cust_df, X, Y, partition, weight= weight)
plotly.offline.plot(fig);
aggregated_cust_df, aggregated_total_demand_df = make_aggregated_df(cust_df, None, total_demand_df, X, Y, partition, weight)
aggregated_dc_df = generate_dc(aggregated_cust_df, aggregated_total_demand_df, prod_df, num_dc=10, lb_ratio=0.0, ub_ratio=10.3,vc_bounds=(19,21),fc_bounds=(0,1))
durations, distances, node_df = compute_durations(aggregated_cust_df, plnt_df)
trans_df, graph, position = make_network_using_road(aggregated_cust_df, aggregated_dc_df, plnt_df, durations, distances, plnt_dc_threshold = 10000., dc_cust_threshold = 500. )
flow_df, dc_df, cost_df, violation_df, status = solve_lnd(prod_df, aggregated_cust_df,aggregated_dc_df, plnt_df, plnt_prod_df, aggregated_total_demand_df,
trans_df, dc_num= (5,13), single_sourcing=True, max_cpu = 100)
cost_df
#cust_df.reset_index(inplace=True)
from_dc = {} #集約した顧客に入る倉庫の名称を入れた辞書(集約した顧客名をキーとする)
for row in flow_df.itertuples():
if row.to_node[:5]=="Cust_":
from_dc[row.to_node[5:]] = row.from_node[3:]
#開設した集約倉庫に最も近い顧客を探索
from scipy.spatial import KDTree
points = [(i,j) for (i,j) in zip(cust_df.lat, cust_df.lon)]
tree = KDTree(points)
dc_cust_dic={} #集約した倉庫の名前を入れると,それに最も近い,もとの顧客indexを返す辞書
for row in dc_df[ dc_df.open_close==1 ].itertuples():
dis, near = tree.query( (row.lat, row.lon) )
dc_cust_dic[row.name] = cust_df.loc[int(near),"index"]
durations, distances, node_df = compute_durations(cust_df, None)
#dc_set = set(from_dc.values()) #from_dc: 各集約顧客が,どのDCから来ているかを表す辞書
#agg_cust_set = set(from_dc.keys())
dc_per_dis = 10./4000
dc_per_time = 8000./4000
total_del = 0.
for row in aggregated_cust_df.itertuples():
#try:
dc_name = from_dc[row.name]
#except:
# print("error")
# continue
i = dc_cust_dic[dc_name]
for c in row.customers:
j = cust_df.loc[c,"index"]
if distances[i][j] < 9999999:
dis = distances[i][j]/1000.
time = durations[i][j]/3600.
cost_per_kg = dis*dc_per_dis + time*dc_per_time
total_del += weight[j]*cost_per_kg
else:
print(i,j,"error")
#total_del
cost_df.value[0] + cost_df.value[3] + total_del
道路距離が非常に大きい地点間で配送を行っている. これは,離島(対馬)への道路距離が計算できないためである.
cost_df.value[0] + cost_df.value[3] + total_del
fig = show_optimized_network(aggregated_cust_df, aggregated_dc_df, plnt_df, prod_df, flow_df, position)
plotly.offline.plot(fig);
durations, distances, node_df = compute_durations(cust_df, None)
%%time
X, Y, partition, cost = repeated_weiszfeld(cust_df, weight,
num_of_facilities = 50, epsilon=0.0001, max_iter = 30, numpy=True, seed=2)
print(cost)
fig = show_optimized_continuous_network(cust_df, X, Y, partition, weight= weight)
plotly.offline.plot(fig);
aggregated_cust_df, aggregated_total_demand_df = make_aggregated_df(cust_df, None, total_demand_df, X, Y, partition, weight)
aggregated_dc_df = generate_dc(aggregated_cust_df, aggregated_total_demand_df, prod_df, num_dc=10, lb_ratio=0.0, ub_ratio=10.3,vc_bounds=(19,21),fc_bounds=(0,1))
durations, distances, node_df = compute_durations(aggregated_cust_df, plnt_df)
trans_df, graph, position = make_network_using_road(aggregated_cust_df, aggregated_dc_df, plnt_df, durations, distances, plnt_dc_threshold = 10000., dc_cust_threshold = 500. )
flow_df, dc_df, cost_df, violation_df, status = solve_lnd(prod_df, aggregated_cust_df,aggregated_dc_df, plnt_df, plnt_prod_df, aggregated_total_demand_df,
trans_df, dc_num= (5,13), single_sourcing=True, max_cpu = 100)
cost_df
fig = show_optimized_network(aggregated_cust_df, aggregated_dc_df, plnt_df, prod_df, flow_df, position)
plotly.offline.plot(fig);
cust_df.reset_index(inplace=True)
from_dc = {} #集約した顧客に入る倉庫の名称を入れた辞書(集約した顧客名をキーとする)
for row in flow_df.itertuples():
if row.to_node[:5]=="Cust_":
from_dc[row.to_node[5:]] = row.from_node[3:]
#開設した集約倉庫に最も近い顧客を探索
from scipy.spatial import KDTree
points = [(i,j) for (i,j) in zip(cust_df.lat, cust_df.lon)]
tree = KDTree(points)
dc_cust_dic={} #集約した倉庫の名前を入れると,それに最も近い,もとの顧客indexを返す辞書
for row in dc_df[ dc_df.open_close==1 ].itertuples():
dis, near = tree.query( (row.lat, row.lon) )
dc_cust_dic[row.name] = cust_df.loc[int(near),"index"]
durations, distances, node_df = compute_durations(cust_df, None)
#dc_set = set(from_dc.values()) #from_dc: 各集約顧客が,どのDCから来ているかを表す辞書
#agg_cust_set = set(from_dc.keys())
dc_per_dis = 10./4000
dc_per_time = 8000./4000
total_del = 0.
for row in aggregated_cust_df.itertuples():
try:
dc_name = from_dc[row.name]
except:
print("error")
continue
i = dc_cust_dic[dc_name]
for c in row.customers:
j = cust_df.loc[c,"index"]
if distances[i][j] < 9999999:
dis = distances[i][j]/1000.
time = durations[i][j]/3600.
cost_per_kg = dis*dc_per_dis + time*dc_per_time
total_del += weight[j]*cost_per_kg
else:
print(i,j,"error")
#total_del
cost_df.value[0] + cost_df.value[3] + total_del
k-means法と同様に,道路距離が非常に大きい地点間で配送を行っている. これは,離島(対馬)への道路距離が計算できないためである.