텀프로젝트 코드 분석-2

보기전에 주의점

2022년 4월 14일 데이터를 기준으로 프로젝트가 작성되어있습니다.

프로젝트 전체 코드 Github


각 물자별 손실량 그래프

fig = px.bar(military_total_losses_cost, x ='equipment_name', y='losses_cost',text_auto=True,
             title=f'Russian Equipment Losses cost in Ukraine War \n({last_date})')
fig.update_traces(textfont_size=18, textangle=0, cliponaxis=False)
fig.update_layout(
    xaxis_title="Equipment Name",
    yaxis_title="Cost of Equipment Losses",
    font_size = 18
)
fig.update_yaxes(tickformat='$,',ticksuffix='M')
fig.show()

이 그래프는 plotly로 그렸다. 데이터는 military_total_losses_cost 값을, x 축을 equipment_name칼럼으로, y축을 losses_cost값으로 했다.
데이터 기준 날짜가 매우 중요하므로 타이틀에 미리 계산해둔 last_date 값을 표시하도록 해주었다.

update_traces에서 textfont_size로 제목 값을 조정해주었고, update_layout 값으로 x,y축 이름과 각 폰트 사이즈도 똑같이 18pt로 설정해주었다.

마지막으로 내가 계산한 값은 $M 단위이므로 y축에도 그에 맞는 단위를 넣어줘야 그래프를 직관적으로 이해할 수 있기 때문에, update_yaxex라는 메서드로 조정을 해주었다.

  • tickformat은 단위를 설정하는 옵션인데, $를 넣으면 달러 단위 표시를 넣어주고, ,를 넣으면 1000 단위로 세미콜론을 넣어준다. tickformat에 어떠한 옵션을 넣을 수 있고 어떻게 바뀌는지는 여기서 확인할 수 있다.
  • ticksuffix옵션으로 마지막에 M 표시를 추가했다. 만약 맨앞에 넣고싶으면 tickprefix로 하면 된다.



각 군 편제별 총 손실량 그래프

fig = px.bar(
    military_total_losses_cost, x='unit_type', y='losses_cost',color='equipment_name',text_auto=True,
    title=f'Russian Equipment Losses cost in Ukraine War \n({last_date})'
)

fig.update_layout(
    xaxis_title="Unit Type",
    yaxis_title="Cost of Equipment Losses",
    font_size = 18
)
fig.update_yaxes(tickformat='$,',ticksuffix='M')
fig.show()

위의 Bar 그래프와 다를건 하나도 없다. 그저 x축을 equipment_name대신에 unit_type으로 정의했을 뿐인데, 각 유닛 타입에 맞는 것들이 stack 되어 이런 식으로 바로 보여주는 것이다.
매우 편리하고 직관적인 방식인것 같다.



각 군 편제별 총 손실량 Pie 그래프

fig = px.pie(military_total_losses_cost, values='losses_cost', names=military_total_losses_cost['unit_type'], 
             title=f'Percentages of Russian Equipment Losses cost in Ukraine War \n({last_date})')
fig.update_layout(
    font_size = 22
)
fig.show()

이 그래프도 plotly의 pie 메서드를 이용해서 그렸다. values값과 names값으로 내가 원하는 종류의 값을 자동으로 비율을 계산해서 그래프로 보여준다. 설명이 필요없는 직관적인 그래프인것 같다.

만약 군 편제별이 아니라 군 물자별로 비율을 알고 싶다면? (갑자기 생각나서 추가로 작성해본다.)

fig = px.pie(military_total_losses_cost, values='losses_cost', names=military_total_losses_cost['equipment_name'], 
             title=f'Percentages of Russian Equipment Losses cost in Ukraine War \n({last_date})')
fig.update_layout(
    font_size = 22
)
fig.show()

이런식으로 나온다.



일자별 군 물자 손실 그래프

rank_list = military_total_losses_cost['equipment_name'].values.tolist()

plt.figure(figsize=(14,8))
sns.set_style("darkgrid")
for equip in rank_list:
    sns.lineplot(x='date', y=equip, data=loss_eq_Calc, marker='o')
    

plt.xlabel('Date',fontsize=18)
plt.ylabel('Cost of Equipment Losses', fontsize=18)
plt.title('Russian Equipment Losses Cost in Ukraine War 2022')
plt.legend(labels=rank_list)

plt.gca().yaxis.set_major_formatter(mticker.FormatStrFormatter('$%.1fM')) # matplotlib에서 y축 단위설정 함수
plt.show()

line 그래프는 seaborn의 그래프로 그렸다.
먼저 여러 개의 선을 넣기 위해서 각 선분의 이름을 리스트화하여 가질 rank_list를 만들었다. military_total_losses_cost에서 equipment_name칼럼의 value.tolist() 하여 리스트로 들고있게 된다.

그리고 plt.figure로 그래프를 그릴 준비를 하고, .set_styledarkgrid스타일을 적용했다.

그 다음 for문을 이용해서 rank_list를 하나씩 받아와 각각 y축으로, x축은 date로 지정하여 그렸다.
xlabel, ylabel로 x,y축 이름 지정, 그리고 ply.legend()로 범례를 표시하였다.

matplotlib에서 plotly의 tickformat같이 단위를 설정하는 메서드는 .gca().yaxis.set_major_formatter이다.
이 안에서 mticker.FormatStrFormatter 로 설정을 해줘야 하는데, mticker코드분석-1에서 라이브러리 임포트에 있는 import matplotlib.ticker as mticker 이다.
그리고 mtickerFormatStrFormatter 메서드에서 하는 포메팅은 조금 다른데, 한번에 표시가 가능하다.

  • 먼저 앞뒤에 원하는 문자를 넣는다. 나의 경우는 $M 표시인데, 만약 이것만 넣은 mticker.FormatStrFormatter('$M') 이라면 실제 값은 아래와 같이 나올 것이다.

  • 그러므로 중간에는 실제 들어갈 값을 넣어줘야 하는데, 그게 %표시이고, 원하는 소수점 표시를 위해 .1f를 넣어준 것이다. 만약 정수를 원한다면 %i를 써주면 된다. 이에 대한 예시는 참고한 사이트를 보면 될 것이다.
  • 지금 생각해보면 정수표시가 맞는것 같다... 그래서 코드만 수정했다.



일자별 군 편제별 손실 그래프

# 각 일자별 손실량 합산
total_loss_cost_per_day = loss_eq_Calc.iloc[:,:1].copy()
total_loss_cost_per_day['ground_units'] = loss_eq_Calc[ground_units].sum(axis=1)
total_loss_cost_per_day['naval_units'] = loss_eq_Calc[naval_units].sum(axis=1)
total_loss_cost_per_day['air_units'] = loss_eq_Calc[air_units].sum(axis=1)

print(total_loss_cost_per_day.tail())

> 실행결과 
         date  ground_units  naval_units  air_units
43 2022-04-09      7831.585         4200    4976.73
44 2022-04-10      7855.969         4200    5010.96
45 2022-04-11      7875.017         4200    5053.82
46 2022-04-12      8804.554         4200    5156.51
47 2022-04-13      8952.336         4200    5216.34

여기서 조금의 전처리가 있는데, 군 편제별로 일자별 손실누적량을 계산하려면 새로운 데이터프레임이 필요하다는 결론이 나와서 새로 구성했다.

먼저 내가 필요한 값은 행은 전부, 열은 date와, 미리 계산해둔 값의 합산값만 있으면 되기 때문에, 먼저 total_loss_cost_per_day 객체에 행 전체와 열 datecopy()해왔다.
그리고 ground_units라는 새로운 열에, 미리 만들어둔 ground_units 객체에 있는, 육군 군물자들의 값에 대한 sum()값을 axis=1으로 하였으므로 ground_units에 존재하는 'APC', 'military auto', 'tank', 'field artillery', 'MRL','anti-aircraft warfare', 'mobile SRBM system' 이 시리즈들을 전부 들고와서, 각 행별로 각 열들이 전부 더해져서 total_loss_cost_per_day['ground_units'] 의 각 행에 전부 들어가게 된다.

나머지 naval_units, air units들도 같은 작업으로 해주었고, 그 값을 출력한 값이 정상적으로 나옴을 알 수 있다.


# 그래프 그리기
units = ['ground_units', 'naval_units', 'air_units']
plt.figure(figsize=(14,8))
sns.set_style("darkgrid")
for unit in units:
    sns.lineplot(x='date', y=unit, data=total_loss_cost_per_day, marker='o')



plt.xlabel('Date',fontsize=18)
plt.ylabel('Cost of Equipment Losses Per Unit',fontsize=18)
plt.title('Russian Military Units Equipment Losses Cost in Ukraine War 2022')
plt.legend(labels=units)
plt.gca().yaxis.set_major_formatter(mticker.FormatStrFormatter('$%iM')) 

plt.show()

위에서 느낀것처럼 지금은 정수형태로 y축을 수정한 상태의 그래프이다. 그 외에는 일자별 군 물자 손실 그래프와 다른점은 없다.



일자별 각 군물자 순 손실량 그래프

cost_per_day = loss_eq_Calc.iloc[:,:1].copy()
cost_per_day['losses'] = loss_eq_Calc.iloc[:,2:].copy().sum(axis=1)
cost_per_day['losses_day'] = cost_per_day['losses'].copy()
for i in range(1,last_index+1):
    cost_per_day['losses_day'][i] = (cost_per_day['losses_day'][i] - cost_per_day['losses'][i-1])
    
cost_per_day.drop('losses',axis=1,inplace=True)
print(cost_per_day.head())

> 실행결과
        date  losses_day
0 2022-02-25    1677.224
1 2022-02-26     649.720
2 2022-02-27       4.000
3 2022-02-28     757.310
4 2022-03-01     268.910


fig = px.bar(cost_per_day, x ='date', y='losses_day',text_auto=True,
             title=f'Russian Equipment Losses cost per Day in Ukraine War \n({last_date})')
fig.update_traces(textfont_size=18, textangle=0, cliponaxis=False, textposition='outside')
fig.update_layout(
    xaxis_title="Date",
    yaxis_title="Cost of Equipment Losses",
    font_size = 18
)
fig.update_yaxes(tickformat='$,.0f',ticksuffix='M')
fig.show()

순 손실량을 알려면 또 새로운 Dataframe으로 값을 저장해야 한다고 생각해서 cost_per_day 객체를 새로 만들었다. 먼저 date열을 copy()해오고, 임시 losses행을 만들어 총 군물자의 합산값을 넣어주었다. 그리고 losses_day행을 만들어 현재 날짜의 손실값 - 그 전날의 합산값 으로 간단하게 순 손실량을 구해주었다.

하지만 여기서 문제가 있었는데, SettingWithCopyWarning 에러였다. 찾아보니 본 데이터프레임에서 데이터를 가져온 서브 데이터프레임의 값을 변경할 경우, 본 데이터프레임에 존재하는 View에 대해 어떠한 방식으로 처리해야 할지 정해진 답이 없기 때문에, 무조건 본 데이터 프레임에서 가져오는 서브 데이터프레임은 .copy() 메서드를 사용해서 들고 오라는 것이다. 그래서 새로운 View를 생성한 뒤에 처리를 하라고 하는데...

여러가지 방법을 시도해 본 결과 그냥 데이터프레임에 있는 데이터를 변경하려는 행위 자체에서 발생하는 터라 결국 해결방법은 못찾았다...

어쨌든 결과는 무사히 잘 나오고, 그걸 plotly의 Bar 그래프로 나타냈다. 아쉬운 점으로는 date의 갯수가 많아서 Bar의 폭이 좁고 그래서 y축 데이터의 값이 얼마인지 각 Bar에 조그맣게 나와서 식별하기가 어렵다는 점이다.



📜결론

텀프를 마무리할때는 다 잘 한줄 알았는데, 포스트를 작성하면서 미진했던 점도 많이 발견했고. 여러모로 부족한 점이 있어서 수정을 했다. 역시 테스팅은 여러번 해야 하는 것 같다는 작은 깨달음도 얻었고,
이러한 방식으로 내가 작성한 코드를 다시 분석하니 코드를 쓰면서 느꼈던 내용, 왜 이러한 코드를 썼는지 등등이 체득이 된것 같다. 처음에는 이러한 시간보다 코드 더 쓰는게 맞지 않나 라는 안일한 생각을 했지만. 이렇게 리뷰하고 되돌아보는 과정이 매우 중요함을 다시금 깨달았다.
이러한 프로젝트 뿐만 아니라 내가 배울 다른 데이터관련 정보들을 포스팅해보는 것도 매일은 아니더라도 일주일에 한번 이상은 해야겠다 싶은 생각을 했다.

좋은 웹페이지 즐겨찾기