본문 바로가기
Analysis/Example

물류 비용 최소화 - 네트워크 가시화, 최적화

by 5ole 2021. 2. 15.

1. 전제조건 

 

  • 제품의 부품을 보관하는 창고에서 공장까지 운송 비용을 낮추기
  • 어떤 창고에서 어떤 공장으로 어느 정도의 양을 운송할 지 검토

 

2. 데이터 정보

 

  • trans_route.csv :  운송 정보 - 공장에서 창고까지 얼마나 운송했는지
  • trans_route_pos.csv : 노드 좌표
  • trans_cost.csv : 각 경로에 필요한 비용
  • demand.csv : 공장 수요량
  • supply.csv : 창고 공급량
  • trans_route_new.csv : trans_route에서 변경된 데이터 (W1에서 F4 운송을 줄이고, W2에서 F4로 운송 보충)
import os
import pandas as pd
import numpy as np

import networkx as nx
import matplotlib.pyplot as plt

os.chdir('C:\\Users\\leeso\\Downloads\\pyda100-master\\6장')

df_tr = pd.read_csv('trans_route.csv',index_col='공장')
df_pos = pd.read_csv('trans_route_pos.csv')
df_tc = pd.read_csv('trans_cost.csv',index_col='공장')
df_tr = pd.read_csv('trans_route.csv', index_col="공장")
df_demand = pd.read_csv('demand.csv')
df_supply = pd.read_csv('supply.csv')
df_tr_new = pd.read_csv('trans_route_new.csv',index_col='공장')

 

 

3. NetworkX 라이브러리 기초

 

(1) 네트워크 작성 기초

 

  • 객체 선언 후 노드와 엣지 추가, 노드의 좌표를 설정해 네트워크를 그릴 수 있다.
G = nx.Graph()

G.add_node('nodeA')
G.add_node('nodeB')
G.add_node('nodeC')

G.add_edge('nodeA','nodeB')
G.add_edge('nodeA','nodeC')
G.add_edge('nodeB','nodeC')

pos={}
pos['nodeA']=(0,0)
pos['nodeB']=(1,1)
pos['nodeC']=(0,1)

nx.draw(G,pos,with_labels=True)

plt.show()

 

 

(2) 경로 가중치 부여

 

  • 가중치를 부여해 노드 사이의 엣지 굵기를 바꾸기
  • 가중치 정보 ( df_w )
  • 각 엣지의 위치 ( df_p )

 

df_w = pd.read_csv('network_weight.csv')
df_p = pd.read_csv('network_pos.csv')


# 엣지 가중치 만들기

size = 10
weight = []
for i in range(len(df_w)) :
    for j in range(len(df_w.columns)):
        weight.append(df_w.iloc[i][j]*size)
        
     
# 노드, 엣지 추가
        
G = nx.Graph()

for i in range(len(df_w.columns)):
    G.add_node(df_w.columns[i])
    
    
for i in range(len(df_w.columns)) :
    for j in range(len(df_w.columns)):
        G.add_edge(df_w.columns[i],df_w.columns[j])
        
    
# 노드 좌표 설정
        
pos ={}

for i in range(len(df_w.columns)):
    node = df_w.columns[i]
    pos[node] = (df_p[node][0],df_p[node][1])
    
    
    
nx.draw(G,pos,with_labels=True,font_size=16, node_size=1000, node_color='k', font_color='w',width=weight)

plt.show()

 

 

 

 

4.  공장 데이터로 운송 경로 가시화 

 

  • 어느 창고에서 어느 공장이로든 골고루 운송 경로가 있으므로 줄이는 것이 개선방향
G = nx.Graph()


# 노드 추가 7개

for i in range(len(df_pos.columns)):
    G.add_node(df_pos.columns[i])
    
    
# 엣지 설정, 가중치 리스트화    
    
num_pre = 0
edge_weights =[]
size = 0.1

for i in range(len(df_pos.columns)):
    for j in range(len(df_pos.columns)):
        if not (i==j):
            
            # 엣지 추가
            G.add_edge(df_pos.columns[i],df_pos.columns[j])
            
            # 엣지 가중치 추가
            if num_pre<len(G.edges):
                num_pre = len(G.edges)
                weight = 0
                
                if (df_pos.columns[i] in df_tr.columns)and(df_pos.columns[j] in df_tr.index):
                    if df_tr[df_pos.columns[i]][df_pos.columns[j]]:
                        weight = df_tr[df_pos.columns[i]][df_pos.columns[j]]*size
                elif(df_pos.columns[j] in df_tr.columns)and(df_pos.columns[i] in df_tr.index):
                    if df_tr[df_pos.columns[j]][df_pos.columns[i]]:
                        weight = df_tr[df_pos.columns[j]][df_pos.columns[i]]*size
                edge_weights.append(weight)
                


# 좌표 설정

pos = {}
for i in range(len(df_pos.columns)):
    node = df_pos.columns[i]
    pos[node] = (df_pos[node][0],df_pos[node][1])
    
# 그리기

nx.draw(G, pos, with_labels=True,font_size=16, node_size = 1000, node_color='k', font_color='w', width=edge_weights)
plt.show()

 

 

5. 운송 비용 최소화하기위한 최적화

 

  • 최적화 문제를 푸는 패턴  - 다양한 조합 중에 제약 조건을 만족시키며 목적함수를 최대, 최소화 시키는 조합 선택

(1) 최적화 하고싶은 것을 함수로 정의 - 목적함수

(2) 최적화 하기 위해 지켜야 할 조건 정의 - 제약 조건

 

(1) 운송 비용 함수

 

  • 운송량과 비용 곱의 총합으로 1493만원의 총 운송비용 집계
def trans_cost(df_tr,df_tc):
    cost = 0
    for i in range(len(df_tc.index)):
        for j in range(len(df_tr.columns)):
            cost += df_tr.iloc[i][j]*df_tc.iloc[i][j]
    return cost

trans_cost(df_tr,df_tc)

 

 

(2) 제약 조건

 

  • 실제 운송량과 가능한 수요량, 공급량 비교해보기
# 수요측 제약조건
for i in range(len(df_demand.columns)):
    temp_sum = sum(df_tr[df_demand.columns[i]])
    print(str(df_demand.columns[i])+"실제 운송량:"+str(temp_sum)+" (수요량:"+str(df_demand.iloc[0][i])+")")
    
    if temp_sum>=df_demand.iloc[0][i]:
        print("수요량을 만족시키고있음")
    else:
        print("수요량을 만족시키지 못하고 있음. 운송경로 재계산 필요")

# 공급측 제약조건
for i in range(len(df_supply.columns)):
    temp_sum = sum(df_tr.loc[df_supply.columns[i]])
    print(str(df_supply.columns[i])+"실제 운송량:"+str(temp_sum)+" (공급 범위:"+str(df_supply.iloc[0][i])+")")
    if temp_sum<=df_supply.iloc[0][i]:
        print("공급 범위내")
    else:
        print("공급 범위 초과. 운송경로 재계산 필요")

 

 

(3) 운송경로 변경 테스트

 

  • 위의 비교문을 사용해 조건을 만족하면 1, 만족하지 않으면 0 출력하도록 함
  • 비용 절감은 했으나, 공급 가능 범위를 넘어버려 실행 불가능 
trans_cost(df_tr_new,df_tc)


# 수요측
def condition_demand(df_tr,df_demand):
    flag = np.zeros(len(df_demand.columns))
    for i in range(len(df_demand.columns)):
        temp_sum = sum(df_tr[df_demand.columns[i]])
        if (temp_sum>=df_demand.iloc[0][i]):
            flag[i] = 1
    return flag
            
# 공급측
def condition_supply(df_tr,df_supply):
    flag = np.zeros(len(df_supply.columns))
    for i in range(len(df_supply.columns)):
        temp_sum = sum(df_tr.loc[df_supply.columns[i]])
        if temp_sum<=df_supply.iloc[0][i]:
            flag[i] = 1
    return flag

print("수요조건 계산결과:"+str(condition_demand(df_tr_new,df_demand)))
print("공급조건 계산결과:"+str(condition_supply(df_tr_new,df_supply)))
        

 

 

 

 

+ 참고 자료 및 출처

 

 

댓글