파이썬 데이터 분석 실무 테크닉 100 -최적화(4)

테크닉 067 : 최적 생산 계획이 제약 조건을 만족하는지 확인하자

이전 글에 이어 최적 생산 계획이 제약 조거늘 만족하는지 확인할 것이다.

최적화 문제를 풀 때 가장 주의할 점은 최적화 계산을 한 결과를 이해하지 않고 그냥 받아들이면 안 된다는 것이다.

생산 최적화에 있어 결과를 이해하는 방법에는 먼저 제약 조건으로 규정한 "각 원료의 사용량"이 어느 정도이고 그것이 "재고를 효율적으로 이용하고 있는가"를 알아보는 방법이다.

def condition_stock(df_plan, df_material, df_stock):
    flag = np.zeros(len(df_material.columns))
    for i in range(len(df_material.columns)):
        temp_sum = 0
        for j in range(len(df_material.index)):
            temp_sum = temp_sum + df_material.iloc[j][i] * float(df_plan.iloc[j])
        if (temp_sum <= float(df_stock.iloc[0][i])):
            flag[i] = 1
        print(df_material.columns[i] + "사용량 : "+str(temp_sum)+", 재고 : "+str(float(df_stock.iloc[0][i])))
    return flag

print('제약 조건 계산 결과 : '+str(condition_stock(df_plan_sol, df_material, df_stock)))

계산 결과를 보면 제약 조건은 모두 충족됐으며 원료2와 원료3은 재고를 모두 사용한다. 원료1이 조금 남아 있지만, 최적화 계산 전과 비교하면 원료의 사용 효율이 크게 개선된 것을 알 수 있다. 이번 개선은 나름 "합리적"이라 판단할 수 있다.


테크닉 068 : 물류 네트워크 설계 문제를 풀어보자

상품의 수요가 이미 결정되어 있으면 가장 중요한 것은 어떻게 비용을 낮출 것인가이다. 즉, 운송 비용과 제조 비용이 수요를 만족하면서 최소가 되게 정식화한다. 목적함수는 운송 비용과 제조 비용의 합으로 정의하고, 제약 조건은 각 대리점의 판매 수가 수요 수를 넘는 것으로 정의한다.

제품 = list('AB')
수요지 = list('PQ')
공장 = list('XY')
레인 = (2, 2)

# 운송비
tbdi = pd.DataFrame(((j, k) for j in 수요지 for k in 공장), columns = ['대리점', '공장'])
tbdi['운송비'] = [1, 2, 3, 1]
print(tbdi)

# 수요
tbde = pd.DataFrame(((j, i) for j in 수요지 for i in 제품), columns = ['대리점', '제품'])
tbde['수요'] = [10, 10, 20, 20]
print(tbde)

# 생산
tbfa = pd.DataFrame(((k, l, i, 0, np.inf) for k, nl in zip(공장, 레인) for l in range(nl) for i in 제품),
                   columns = ['공장', '레인', '제품', '하한', '상한'])
tbfa['생산비'] = [1, np.nan, np.nan, 1, 3, np.nan, 5, 3]
tbfa.dropna(inplace = True)
tbfa.loc[4, '상한'] = 10
print(tbfa)

from ortoolpy import logistics_network
_, tbdi2, _ = logistics_network(tbde, tbdi, tbfa, dep = '대리점', dem = '수요', fac = '공장', prd = '제품', tcs = '운송비', pcs = '생산비',
                               lwb = '하한', upb = '상한')
print(tbfa)
print(tbdi2)

함수 logistics_network를 이용하면 생산표에 ValY라는 항목이 만들어지면서 최적 생산량이 저장되고, 운송 비표에 ValX라는 항목이 만들어지고 최적 운송량이 저장된다.


테크닉 069 : 최적 네트워크의 운송 비용과 그 내역을 계산하자

운송 비용은 함수 logistics_network의 반환값으로 tbdi2에 저장된다. '운송비' 칼럼과 최적 운송량을 저장한 'ValX' 칼럼을 곱하면 운송 비용을 계산할 수 있다.

tbdi2 = tbdi2[['공장', '대리점', '운송비', '제품', 'VarX', 'ValX']]
tbdi2

trans_cost = 0
for i in range(len(tbdi2.index)):
    trans_cost += tbdi2['운송비'].iloc[i] * tbdi2['ValX'].iloc[i]
print('총 운송비 : '+str(trans_cost))

결과를 보면 총 운송 비용은 80만원으로 계산됐다. 내역을 보면 되도록 운송비가 적은 공장 X > 대리점 P, 공장 Y > 대리점 Q의 경로를 사용하고, 이것만으로는 대리점 Q에 의한 상품 A의 수요가 약간 부족하기 때문에 공장 X에서 대리점 Q로 제품 A를 10만큼 운송한다. 공장 Y의 생산에는 한계가 있고, 생산 표에 의하면 공장 X의 제품 A의 생산 비용이 다른 곳보다 낮기 때문에 이 조합은 대체로 타당하다고 볼 수 있다.


테크닉 070 : 최적 네트워크의 생산 비용과 그 내역을 계산하자

생산 비용은 logistics_network 함수 계산 후 tbfa에 저장된다. '생산비' 칼럼과 최적 생산량을 저장한 'ValY' 칼럼을 곱하면 생산 비용이 계산된다.

print(tbfa)
product_cost = 0
for i in range(len(tbfa.index)):
    product_cost += tbfa['생산비'].iloc[i] * tbfa['ValY'].iloc[i]
print('총 생산비 : ' +str(product_cost))

결과를 보면 총생산 비용은 120만 원으로 계산됐다. 되도록 생산 비용이 낮은 공장 X에서의 생산량을 늘려 공장 X에서 제품 A의 생산량을 20으로, 제품 B의 생산량을 10으로 하고 있다고 생각하면 합리적이다. 생산비용만을 생각하면 모든 제품을 공장 X에서 제조하면 되지만, 운송 비용까지 고려하면 수요량이 많은 대리점 Q까지의 운송 비용이 적은 공장 Y를 어느 정도 가동시키지 않을 수 없으며 결과적으로 공장 Y에서의 제품 A의 생산량은 10이고 제품 B의 생산량은 20이 된다. 생산 비용과 운송 비용의 균형을 생각하면 타당하고 판단할 수 있다.


지금까지 라이브러리를 이용해 최적화 계산을 진행했다. 최적화 계산을 하는 방법으론 여러 가지가 있지만 상황에 따라 맞게 사용하는 것이 중요하다고 생각한다.

TAGS.

Comments