First EDA Project
Intro¶
사람들의 일자리를 만들어 기업체를 운영한다는 것은 아주 어려운 일이다. 코로나로 일자리 파이가 쪼그라드는 이 시점에는 사업의 흥망에 대해 낙관적인 전망을 기대하기는 어렵다.
어려운 상황임에도 불구하고, 수십년간 많은 사람들의 일자리를 챙겨온 대단한 인물이 있다.
비주기, 상록시티의 관장이자, 1996년 2월 27일부터 20년 넘게 로켓단을 이끌어 오고 있는 인물이다.
그를 서술 하고 있는 작중 행적에 따르면, 그의 목표는 돈과 포켓몬에 의한 세계정복이다. 때문에 강한 포켓몬들을 모으는 것이 주로 하는 일이라 밝히고 있다. 정리하자면, 돈과 세계정복을 위해 로켓단이라는 전국적인 기업을 만들어 일자리를 창출해왔고, 피고용인들로 하여금 강한 포켓몬을 수집하고 있는 것이었다.
이런 목표로 움직이고 있는 로켓단인데, 왜 우리들의 기억에 남아있는 로켓단은 약한 모습인 걸까?
Purpose of executing EDA¶
- 대전 승리 확률과 승리 확률에 미치는 유의미한 변수를 확인하여 로켓단이 승리하기 위한 큰그림을 제시한다.
import numpy as np
import pandas as pd
import seaborn as sns
%matplotlib inline
import random
random.seed(1)
pokemon = pd.read_csv("./input/pokemon.csv")
combat = pd.read_csv("./input/combats.csv")
pokemon.head() #
# | Name | Type 1 | Type 2 | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Bulbasaur | Grass | Poison | 45 | 49 | 49 | 65 | 65 | 45 | 1 | False |
1 | 2 | Ivysaur | Grass | Poison | 60 | 62 | 63 | 80 | 80 | 60 | 1 | False |
2 | 3 | Venusaur | Grass | Poison | 80 | 82 | 83 | 100 | 100 | 80 | 1 | False |
3 | 4 | Mega Venusaur | Grass | Poison | 80 | 100 | 123 | 122 | 120 | 80 | 1 | False |
4 | 5 | Charmander | Fire | NaN | 39 | 52 | 43 | 60 | 50 | 65 | 1 | False |
combat.head()
First_pokemon | Second_pokemon | Winner | |
---|---|---|---|
0 | 266 | 298 | 298 |
1 | 702 | 701 | 701 |
2 | 191 | 668 | 668 |
3 | 237 | 683 | 683 |
4 | 151 | 231 | 151 |
print("pokemon: " + str(pokemon.shape))
print("combat: " + str(combat.shape))
pokemon: (800, 12) combat: (50000, 3)
결측치 확인¶
combat.isnull().sum()
First_pokemon 0 Second_pokemon 0 Winner 0 dtype: int64
pokemon.isnull().sum()
# 0 Name 1 Type 1 0 Type 2 386 HP 0 Attack 0 Defense 0 Sp. Atk 0 Sp. Def 0 Speed 0 Generation 0 Legendary 0 dtype: int64
- 이 값에서 Type 2에 Null값이 많은 이유는 포켓몬의 세대가 바뀌어오면서 (1세대 레드, 그린, 블루 -> 2세대 골드, 실버 -> 3세대 루비, 사파이어 ...) 메인 타입 말고 보조 타입을 정의하는 포켓몬이 생겨났기 때문이다. 보조타입이 없는 포켓몬에 대해서는 없는대로 진행하도록 한다.
- 이름 없는 포켓몬도 하나 있다.
print("==== Name = NaN, 누구냐 넌 ==== ")
if pokemon[pokemon['Name'].isnull()].size != 0:
who = pokemon[pokemon['Name'].isnull()]
print(who, '\n')
print('앞 포켓몬 : ', pokemon['Name'][int(who.index[0])-1])
print('뒤 포켓몬 : ', pokemon['Name'][int(who.index[0])+1])
pokemon[pokemon['#'] == 63]
else:
print("이름이 비어있는 포켓몬이 없습니다.")
# who.Name = "Primeape"
==== Name = NaN, 누구냐 넌 ==== # Name Type 1 Type 2 HP Attack Defense Sp. Atk Sp. Def Speed \ 62 63 NaN Fighting NaN 65 105 60 60 70 95 Generation Legendary 62 1 False 앞 포켓몬 : Mankey 뒤 포켓몬 : Growlithe
- 도감에 따르면, 망키와 가디 사이에는 성원숭이 있다. Type 1이 Fighting인 것을 보면 맞다.
pokemon.loc[(pokemon['#'] == 63), 'Name'] = "Primeape"
pokemon[pokemon['#'] == 63]
# | Name | Type 1 | Type 2 | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
62 | 63 | Primeape | Fighting | NaN | 65 | 105 | 60 | 60 | 70 | 95 | 1 | False |
totalWins = combat.Winner.value_counts() # 전체 승리
countWins = combat.groupby('Winner').count()
countFirst = combat.groupby('First_pokemon').count()
countSecond = combat.groupby('Second_pokemon').count()
print("Count by first winner shape: " + str(countFirst.shape))
print("Count by second winner shape: " + str(countSecond.shape))
print("Total Wins shape : " + str(totalWins.shape))
Count by first winner shape: (784, 2) Count by second winner shape: (784, 2) Total Wins shape : (783,)
- 확인했을 때 이긴 포켓몬 수와 토탈 승리 수가 다르다. 한 포켓몬이 승리를 한번도 하지 못한 것 같다.
find_pokemon= np.setdiff1d(countFirst.index.values, countWins.index.values)-1
found = pokemon.iloc[find_pokemon[0],]
print(found)
# 231 Name Shuckle Type 1 Bug Type 2 Rock HP 20 Attack 10 Defense 230 Sp. Atk 10 Sp. Def 230 Speed 5 Generation 2 Legendary False Name: 230, dtype: object
포켓몬GO에 이르러서는 단단지가 GO로켓단의 주축이 되었다. 알고보니 로켓단이 약하다는 것에 대한 고증이 이루어진 이벤트였던 것!
https://pokemongolive.com/post/gofestweeklychallenge-teamgorocket/?hl=ko
numberOfWins = countWins.sort_index()
numberOfWins['Total Fights'] = countFirst.Winner + countSecond.Winner
numberOfWins['Win Percentage']= numberOfWins.First_pokemon/numberOfWins['Total Fights']
mergedResult = pd.merge(pokemon, numberOfWins, left_on='#', right_index = True, how='left')
패배 기준¶
mergedResult[np.isfinite(mergedResult['Win Percentage'])].sort_values(by = ['Win Percentage']).head(10)
# | Name | Type 1 | Type 2 | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary | First_pokemon | Second_pokemon | Total Fights | Win Percentage | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
289 | 290 | Silcoon | Bug | NaN | 50 | 35 | 55 | 25 | 25 | 15 | 3 | False | 3.0 | 3.0 | 138.0 | 0.021739 |
189 | 190 | Togepi | Fairy | NaN | 35 | 20 | 65 | 40 | 65 | 20 | 2 | False | 3.0 | 3.0 | 122.0 | 0.024590 |
638 | 639 | Solosis | Psychic | NaN | 45 | 30 | 40 | 105 | 50 | 20 | 5 | False | 4.0 | 4.0 | 129.0 | 0.031008 |
236 | 237 | Slugma | Fire | NaN | 40 | 40 | 40 | 70 | 40 | 20 | 2 | False | 4.0 | 4.0 | 123.0 | 0.032520 |
576 | 577 | Munna | Psychic | NaN | 76 | 25 | 45 | 67 | 55 | 24 | 5 | False | 5.0 | 5.0 | 128.0 | 0.039062 |
188 | 189 | Igglybuff | Normal | Fairy | 90 | 30 | 15 | 40 | 20 | 15 | 2 | False | 5.0 | 5.0 | 115.0 | 0.043478 |
394 | 395 | Wynaut | Psychic | NaN | 95 | 23 | 48 | 23 | 48 | 23 | 3 | False | 6.0 | 6.0 | 130.0 | 0.046154 |
209 | 210 | Wooper | Water | Ground | 55 | 45 | 45 | 25 | 25 | 15 | 2 | False | 6.0 | 6.0 | 125.0 | 0.048000 |
291 | 292 | Cascoon | Bug | NaN | 50 | 35 | 55 | 25 | 25 | 15 | 3 | False | 7.0 | 7.0 | 133.0 | 0.052632 |
752 | 753 | Spritzee | Fairy | NaN | 78 | 52 | 60 | 63 | 65 | 23 | 6 | False | 8.0 | 8.0 | 133.0 | 0.060150 |
승리 기준¶
mergedResult[np.isfinite(mergedResult['Win Percentage'])].sort_values(by = ['Win Percentage'], ascending = False ).head(10)
# | Name | Type 1 | Type 2 | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary | First_pokemon | Second_pokemon | Total Fights | Win Percentage | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
154 | 155 | Mega Aerodactyl | Rock | Flying | 80 | 135 | 85 | 70 | 95 | 150 | 1 | False | 127.0 | 127.0 | 129.0 | 0.984496 |
512 | 513 | Weavile | Dark | Ice | 70 | 120 | 65 | 45 | 85 | 125 | 4 | False | 116.0 | 116.0 | 119.0 | 0.974790 |
703 | 704 | Tornadus Therian Forme | Flying | NaN | 79 | 100 | 80 | 110 | 90 | 121 | 5 | True | 121.0 | 121.0 | 125.0 | 0.968000 |
19 | 20 | Mega Beedrill | Bug | Poison | 65 | 150 | 40 | 15 | 80 | 145 | 1 | False | 115.0 | 115.0 | 119.0 | 0.966387 |
153 | 154 | Aerodactyl | Rock | Flying | 80 | 105 | 65 | 60 | 75 | 130 | 1 | False | 136.0 | 136.0 | 141.0 | 0.964539 |
476 | 477 | Mega Lopunny | Normal | Fighting | 65 | 136 | 94 | 54 | 96 | 135 | 4 | False | 124.0 | 124.0 | 129.0 | 0.961240 |
726 | 727 | Greninja | Water | Dark | 72 | 95 | 67 | 103 | 71 | 122 | 6 | False | 122.0 | 122.0 | 127.0 | 0.960630 |
716 | 717 | Meloetta Pirouette Forme | Normal | Fighting | 100 | 128 | 90 | 77 | 77 | 128 | 5 | False | 118.0 | 118.0 | 123.0 | 0.959350 |
164 | 165 | Mega Mewtwo Y | Psychic | NaN | 106 | 150 | 70 | 194 | 120 | 140 | 1 | True | 119.0 | 119.0 | 125.0 | 0.952000 |
349 | 350 | Mega Sharpedo | Water | Dark | 70 | 140 | 70 | 110 | 65 | 105 | 3 | False | 114.0 | 114.0 | 120.0 | 0.950000 |
import matplotlib.pyplot as plt
ax = sns.countplot(x="Type 1", data=mergedResult)
plt.xticks(rotation= 90)
plt.xlabel('Type 1')
plt.ylabel('Total ')
plt.title("Pokemon MainType")
Text(0.5, 1.0, 'Pokemon MainType')
ax = sns.countplot(x="Type 2", data=mergedResult)
plt.xticks(rotation= 90)
plt.xlabel('Type 2')
plt.ylabel('Total ')
plt.title("Pokemon SubType")
Text(0.5, 1.0, 'Pokemon SubType')
타입별 승률¶
mergedResult.groupby('Type 1').agg({"Win Percentage": "mean"}).sort_values(by = "Win Percentage")
Win Percentage | |
---|---|
Type 1 | |
Fairy | 0.329300 |
Rock | 0.404852 |
Steel | 0.424529 |
Poison | 0.433262 |
Bug | 0.439006 |
Ice | 0.439604 |
Grass | 0.440364 |
Water | 0.469357 |
Fighting | 0.475616 |
Ghost | 0.484027 |
Normal | 0.535578 |
Ground | 0.541526 |
Psychic | 0.545747 |
Fire | 0.579215 |
Dark | 0.629726 |
Electric | 0.632861 |
Dragon | 0.633587 |
Flying | 0.765061 |
이 정도로 정리가 된다.
특이한 점은 요정 타입이 압도적으로 약하고, 비행속성이 압도적으로 강하다는 점이다.
약한 속성 : 요정, 돌, 강철, 독, 벌레
강한 속성 : 비행, 용, 전기, 악, 불
산점도¶
산점도의 경우 메인타입을 기준으로 각 능력치를 산점도로 표현하였다.
col = ['Type 1','HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed', 'Win Percentage']
sns.pairplot(mergedResult.loc[:,col].dropna())
<seaborn.axisgrid.PairGrid at 0x7f90c513e208>
히트맵¶
# 레퍼런스 https://datascience.stackexchange.com/questions/10459/calculation-and-visualization-of-correlation-matrix-with-pandas
def correlation_matrix(df):
from matplotlib import cm as cm
fig = plt.figure()
ax1 = fig.add_subplot(111)
cmap = cm.get_cmap('jet', 50)
cax = ax1.imshow(df.corr(), interpolation="nearest", cmap=cmap)
ax1.grid(True)
plt.title('relations')
labels=['Type 1','HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed', 'Win %']
ax1.set_xticklabels(labels,fontsize=7)
ax1.set_yticklabels(labels,fontsize=7)
fig.colorbar(cax, ticks=[0.00,.05,.10,.15,.20,.25,.30,.35,.40,.45,.50,.55,.60,.65,.70,.75,.8,.85,.90,.95,1])
plt.show()
correlation_matrix(mergedResult.loc[:,col])
/Users/jaeuk/Documents/ppamppamman/programmers-KDT/programmers_kdt_II/kdt-env/lib/python3.6/site-packages/ipykernel_launcher.py:12: UserWarning: FixedFormatter should only be used together with FixedLocator if sys.path[0] == '': /Users/jaeuk/Documents/ppamppamman/programmers-KDT/programmers_kdt_II/kdt-env/lib/python3.6/site-packages/ipykernel_launcher.py:13: UserWarning: FixedFormatter should only be used together with FixedLocator del sys.path[0]
이 중 특징이 두드러지게 나타나는 speed에 대해서 확인해보면 다음과 같다
sns.regplot(x="Speed", y="Win Percentage", data=mergedResult, logistic=True).set_title("Speed : Win Percentage")
sns.lmplot(x="Speed", y="Win Percentage", data=mergedResult, hue = 'Type 1', logistic=True)
<seaborn.axisgrid.FacetGrid at 0x7f90ad103198>
속도 능력치의 경우 승률에 있어 거의 비례하는 모습을 보여주고 있다.
결론¶
https://namu.wiki/w/로켓단 문서에 따르면 로켓단이 사용한 포켓몬 개체와 그 수가 작성되어 있다.
7.1. 레드/그린/블루
꼬렛 11마리, 레트라 15마리 - 노말타입, 악타입
주뱃 26마리, 골뱃 7마리 - 독타입, 비행타입
아보 11마리, 아보크 3마리 - 독타입
또가스 10마리, 또도가스 1마리 - 독타입, 페어리타입
슬리프 9마리, 슬리퍼 3마리 - 에스퍼타입
알통몬 7마리, 근육몬 2마리 - 격투타입
모래두지 4마리, 고지 2마리 - 땅타입
탕구리 5마리, 텅구리 1마리 - 땅타입
승률이 5할이 안되는 타입은 bold처리 하였다.
상위권 타입에 해당하는 용타입, 전기타입, 불타입은 전혀 활용하지 않는 모습을 보여준다.
-
물, 노말, 벌레, 풀 타입이 가장 흔한 메인타입이고 비행, 땅, 독이 가장 흔한 서브타입이다.
- 로켓단이 지속가능한 경영을 하기 위해서는 해당 타입들에 대해 상성인 타입의 포켓몬을 수집해야 한다.
-
가장 많이 이기는 포켓몬 타입은 비행, 용, 전기, 악이다. 가장 적게 이기는 포켓몬 유형은 요정, 바위, 강철, 독, 벌레이다.
- 로켓단이 세계적인 기업이 되기 위해서는 특히 비행타입을 많이 모아야하며 차순위로 용타입 전기타입 악타입에 관심을 가져야 한다.
-
승률과 가장 높은 상관관계를 가지는 속성은 속도이며 그다음으로는 공격력이라 할 수 있다. 반대로 방어력, 체력은 승률에 비교적 적은 상관관계를 가지는 것을 볼 수 있다.
- 로켓단은 빠르고 강력한 공격력을 지닌 포켓몬을 모아야 한다. 단단지 같은 방어력과 체력이 높은 포켓몬은 피해야 할 것이다.
여담¶
볼 때는 아무 생각이 없었지만, 데이터를 보고 나니 전기 타입에게 당하고 있는 이 장면은 철저하게 고증된 장면인 것이었다.
Comments