五王之战分析 - 冰与火之歌-Udacity

简介

来源于Udacity数据分析试学项目。

五王之战(War of the Five Kings)是著名严肃奇幻小说《冰与火之歌》中的著名内战。这是一场规模空前、波及七大王国的内乱。顾名思义,前后共有五人在战争中称王:乔佛里、史坦尼斯、蓝礼均声称自己是铁王座的合法继承人。除此之外,罗柏·史塔克被北境众封臣推选为北境之王,巴隆·葛雷乔伊亦再度掀起独立大旗,欲摆脱铁王座的统治,自称为铁群岛之王。

本数据集(battles.csv)包含了五王之战期间的战争,它是所有战斗的大集合。该数据是Kaggle中Game of Thrones的一部分。

数据中的变量含义解释:

name: 战争的名称,字符变量。
year: 战争发生的年份,数值变量。
battle_number: 本数据中的unique id,对应每一场独立的战役,数值变量。
attacker_king: 攻击方的国王,"/"表示了国王的更换。例如:"Joffrey/Tommen Baratheon"意味着Tomen Baratheon继承了Joffrey的王位,分类变量。
defender_king: 防守方的国王,分类变量。
attacker_1: 攻击方将领,字符变量。
attacker_2: 攻击方将领,字符变量。
attacker_3: 攻击方将领,字符变量。
attacker_4: 攻击方将领,字符变量。
defender_1: 防守方将领,字符变量。
defender_2: 防守方将领,字符变量。
defender_3: 防守方将领,字符变量。
defender_4: 防守方将领,字符变量。
attacker_outcome: 从攻击方角度来看的战争结果,分别有:win, loss, draw,分类变量。
battle_type: 战争的类别。pitched_battle: 双方军队在一个地点相遇并战斗,这也是最基本的战争类别;ambush: 以隐身或诡计为主要攻击手段的战争;siege: 阵地战;razing: 对未设防位置的攻击。分类变量。
major_death: 是否有重要人物的死亡,二进制变量。
major_capture: 是否有重要人物的被捕,二进制变量。
attacker_size: 攻击方力量的大小,并未对骑兵、步兵等士兵种类有所区分,数值变量。
defender_size: 防守方力量的大小,并未对骑兵、步兵等士兵种类有所区分,数值变量。
attacker_commander: 攻击方的主要指挥官。指挥官的名字中并没有包含头衔,不同的指挥官名字用逗号隔开,字符变量。
defender_commander: 防守方的主要指挥官。指挥官的名字中并没有包含头衔,不同的指挥官名字用逗号隔开,字符变量。
summer: 战争是否发生于夏天,二进制变量。
location: 战争发生的地点,字符变量。
region: 战争发生的地域,包括:Beyond the Wall, The North, The Iron Islands, The Riverlands, The Vale of Arryn, The Westerlands, The Crownlands, The Reach, The Stormlands, Dorne,分类变量。
note: 注释,字符变量。

提出问题

在此项目中,你将以一名数据分析师的身份执行数据的探索性分析。你将了解数据分析过程的基本流程。在你分析数据之前,请先思考几个你需要理解的关于这些战斗的问题,例如,哪一个区域发生了最多的战争?哪一个国王获得了最多的胜利?战争的胜利与否受那些关键因素的影响?

问题:请写下你感兴趣的问题,请确保这些问题能够由现有的数据进行回答。
(为了确保学习的效果,请确保你的数据分析报告中能够包含2幅可视化和1个相关性分析。)

我提出的问题:

1.这些战役共涉及几位国王参战,各自参战的比例是多少?
2.每个国王的胜率是多少?
3.哪个国王更具侵略性?
4.哪种战争类型最多,哪种胜率更高?
5.除了几位国王势力,还有其他那些家族参与了战争,他们是哪位国王的联盟?
6.所有战争前后经历了几年,每年的夏天的比例是多少?

在提出了问题之后,我们将开始导入数据,并对数据进行探索性分析,来回答上面提出的问题。

数据评估和清理

# TO DO: load pacakges
#coding=utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
# TO DO: load the dataset
got_df = pd.read_csv('battles.csv')
got_df 

Out[3]:由于移动端显示表格不理想,这里只放了原始数据的图片供参考


# TO DO: check the dataset general info
got_df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 38 entries, 0 to 37
Data columns (total 25 columns):
name 38 non-null object
year 38 non-null int64
battle_number 38 non-null int64
attacker_king 36 non-null object
defender_king 35 non-null object
attacker_1 38 non-null object
attacker_2 10 non-null object
attacker_3 3 non-null object
attacker_4 2 non-null object
defender_1 37 non-null object
defender_2 2 non-null object
defender_3 0 non-null float64
defender_4 0 non-null float64
attacker_outcome 37 non-null object
battle_type 37 non-null object
major_death 37 non-null float64
major_capture 37 non-null float64
attacker_size 24 non-null float64
defender_size 19 non-null float64
attacker_commander 37 non-null object
defender_commander 28 non-null object
summer 37 non-null float64
location 37 non-null object
region 38 non-null object
note 5 non-null object
dtypes: float64(7), int64(2), object(16)
memory usage: 7.5+ KB

可以看出,数据集中共收录了38场战役,defender3 4,无数据, note没有分析的意义,所以去掉这些字段。

# TO DO: clean the data (optional: only there are problems)
got_df.drop([ 'defender_3','defender_4', 'note'], axis=1, inplace=True)

另外,本次数据分析主要针对五位国王进行分析,在数据集中,第23场战役和第30场战役均无对垒双方国王的信息,所以在这里删除这两场战役

got_df.drop([22,29], axis=0, inplace=True)
got_df
got_df.info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 36 entries, 0 to 37
Data columns (total 22 columns):
name 36 non-null object
year 36 non-null int64
battle_number 36 non-null int64
attacker_king 36 non-null object
defender_king 35 non-null object
attacker_1 36 non-null object
attacker_2 10 non-null object
attacker_3 3 non-null object
attacker_4 2 non-null object
defender_1 36 non-null object
defender_2 2 non-null object
attacker_outcome 35 non-null object
battle_type 35 non-null object
major_death 35 non-null float64
major_capture 35 non-null float64
attacker_size 24 non-null float64
defender_size 19 non-null float64
attacker_commander 36 non-null object
defender_commander 28 non-null object
summer 35 non-null float64
location 36 non-null object
region 36 non-null object
dtypes: float64(5), int64(2), object(15)
memory usage: 6.5+ KB

数据探索分析

1.这些战役共涉及几位国王参战,各自参战的比例是多少?

# 攻击方国王
attacker=got_df.groupby('attacker_king').size()
# 防御方国王
defender=got_df.groupby('defender_king').size()
# 两者相加统计国王参战次数
king_total= attacker.add(defender,fill_value=0)

king_total

Balon/Euron Greyjoy 11.0
Joffrey/Tommen Baratheon 27.0
Mance Rayder 1.0
Renly Baratheon 1.0
Robb Stark 24.0
Stannis Baratheon 7.0
dtype: float64

# 绘制饼图
king_total.plot.pie(labels=['Balon/Euron Greyjoy', 'Joffrey/Tommen Baratheon', 'Mance Rayder', 'Renly Baratheon', 'Robb Stark', 'Stannis Baratheon'], 
                                             autopct='%.1f%%', fontsize=16, figsize=(6, 6),style = dict, title =u'参战国王')

共有六位国王参与了战争,除了五王之外,Mance作为塞外之王带领长城以北的野人参与了黑城堡之战Battle of Castle Black,这里我也发现了数据集中的一个明显的错误,如下表,在Battle of Castle Black一战中,Mance Rayder应该是攻击一方,应该是attacker_king,而不应该是defender_king。根据右面的attacker_1字段是Free folk,以及attacker_commander字段包含Mance也可以看出来。不过这里依然保留原始数据,不再对数据做修正。

作为七国国王,Joffrey参与了最多的战争,参战率为38.0%,紧随其后的是北境之王Robb,参战率为33.8%。

Renly 仅参加了一场战斗Siege of Storm's End,就命丧于风息堡。

2.每个国王的胜率是多少?

# 攻击方获胜
attacker_win = got_df[got_df['attacker_outcome'] == 'win'].groupby('attacker_king').size()
# 防御方获胜
defender_win = got_df[got_df['attacker_outcome'] == 'loss'].groupby('defender_king').size()
# 总共胜利战役数量
king_win = attacker_win.add(defender_win,fill_value=0)

king_win

Balon/Euron Greyjoy 7.0
Joffrey/Tommen Baratheon 16.0
Mance Rayder 1.0
Robb Stark 9.0
Stannis Baratheon 2.0
dtype: float64

# 胜率
king_win.div(king_total,fill_value=0)

Balon/Euron Greyjoy 0.636364
Joffrey/Tommen Baratheon 0.592593
Mance Rayder 1.000000
Renly Baratheon 0.000000
Robb Stark 0.375000
Stannis Baratheon 0.285714
dtype: float64

# 柱状图
king_win.div(king_total,fill_value=0).plot.bar(figsize=(6,5),fontsize=16,title = u'各王胜率')

从柱状图可知,Mance仅参加一场战斗而且获胜,胜率最高,实际前面也提到,这条数据应该是错误的,实际Mance应该是战败方,所以并不具备参考价值。

其余五王中,铁群岛Balond 的胜率最高为63.6%,铁民天生就是打架的材料;Joffrey依托君临城御林军和Lannister家族的强大实力,在参战次数最多的前提下,依然保持了较高的胜率;可怜的Renly仅参战一场并且失败,胜率为0最低,不过Stannis利用红袍女Melisandre的魔法将Renly杀死,的确也胜之不武;耿直的狼家Robb虽然参战次数不少,但胜率却不高,的确缺乏战略天赋。

3.哪个国王更具侵略性?

# 各王作为攻击方的次数柱状图
attacker.plot.bar(figsize=(6,5),fontsize=16,title = u'各王攻击次数')

从图中可以看出,Joffrey坐拥铁王座,成为了最具侵略性的国王。

4.哪种战争类型最多,哪种胜率更高?

# 战争类别统计
battle_type=got_df.groupby('battle_type').size()
battle_type.plot.pie(labels=['ambush', 'pitched battle', 'razing', 'siege'], 
                                             autopct='%.1f%%', fontsize=16, figsize=(6, 6),style = dict, title =u'战争类别')

可以看出,pitched battle比例最高。

# 获胜的战争类别统计
battle_type_win = got_df[got_df['attacker_outcome']== 'win'].groupby('battle_type').size()

# 不同战争类别胜率
battle_type_win.div(battle_type,fill_value=0)

battle_type_win.div(battle_type,fill_value=0).plot.bar(figsize=(6,5),fontsize=16,title = u'不同战争类别胜率')

可以看出,ambush 和 razing都保持了100%的胜率,但是razing的数量太小,不具备一般性,所以还是认为ambush的胜率最高。

5.除了几位国王势力,还有其他那些家族参与了战争,他们是哪位国王的联盟?

## 从attacker_1 2 3 4和defender_1 2中提取所有的家族
a1=got_df.groupby('attacker_1').size()
a2=got_df.groupby('attacker_2').size()
a3=got_df.groupby('attacker_3').size()
a4=got_df.groupby('attacker_4').size()
d1=got_df.groupby('defender_1').size()
d2=got_df.groupby('defender_2').size()
# 所有series相加
a1.add(a2,fill_value=0).add(a3,fill_value=0).add(a4,fill_value=0).add(d1,fill_value=0).add(d2,fill_value=0)

aratheon 11.0
Blackwood 1.0
Bolton 4.0
Bracken 1.0
Brave Companions 1.0
Darry 2.0
Free folk 1.0
Frey 4.0
Giants 1.0
Glover 2.0
Greyjoy 11.0
Karstark 2.0
Lannister 18.0
Mallister 1.0
Mormont 2.0
Night's Watch 1.0
Stark 16.0
Thenns 1.0
Tully 7.0
Tyrell 2.0
dtype: float64
以上是所有参与战争的家族或势力,可以看到参与次数最多的家族Baratheon,Greyjoy,Lannister,Stark正是五王出生的家族,(Joffrey/Tommen 虽然姓Baratheon,但实际上是纯种的Lannister)。

由于家族太多,这里选择两个来举例分析他们是谁的同盟 Tully 以及 Tyrell

Tully 家族:

# Tully 为attacker时的attacker_king是谁?
attacker_king_tully = got_df[(got_df['attacker_1']=='Tully')|(got_df['attacker_2']=='Tully')|(got_df['attacker_3']=='Tully')|
       (got_df['attacker_4']=='Tully')].groupby('attacker_king').size()

# Tully 为defender时的defender_king是谁?
defender_king_tully = got_df[(got_df['defender_1']=='Tully')|(got_df['defender_2']=='Tully')].groupby('defender_king').size()

# Tully 家族在战争中支持的国王
attacker_king_tully.add(defender_king_tully,fill_value=0)

attacker_king
Robb Stark 7
dtype: int64
由此可见,Tully家族在7次战争中都支持了Robb Stark,这是理所应当的,因为Robb的母亲是Catelyn Tully...

Tyrell家族:

# Tyrell 为attacker时的attacker_king是谁?
attacker_king_tyrell = got_df[(got_df['attacker_1']=='Tyrell')|(got_df['attacker_2']=='Tyrell')|(got_df['attacker_3']=='Tyrell')|
       (got_df['attacker_4']=='Tyrell')].groupby('attacker_king').size()

# Tyrell 为defender时的defender_king是谁?
defender_king_tyrell = got_df[(got_df['defender_1']=='Tyrell')|(got_df['defender_2']=='Tyrell')].groupby('defender_king').size()

# Tyrell家族在战争中支持的国王
attacker_king_tyrell.add(defender_king_tyrell,fill_value=0)

defender_king
Joffrey/Tommen Baratheon 2.0
dtype: float64

显然,Tyrell家族在2次战争中都支持了Joffrey。

其实一开始,Tyrell家族是拥护Renly的,在Renly死后,Tyrell家族与Lannister家族结盟,小玫瑰Margaery Tyrell被许配给了Joffrey成为七国王后,在Joffrey死后又再度许配给了Tommen...

6.所有战争前后经历了几年,每年的夏天的比例是多少?

totalday = got_df.groupby('year').size()
totalday

year
298 7
299 19
300 10
dtype: int64

战争前后经历了3年。

# 每年的夏天的占比
summer = got_df[got_df.summer == 1].groupby('year').size()
summer.div(totalday,fill_value=0)

year
298 1.000000
299 0.947368
300 0.000000
dtype: float64
可以看出,298年和299年都是夏天,而300年没有夏天,凛冬将至。

冰与火的小说中提到了,维斯特洛大陆的季节并没有规律,之前已经经历了十年长夏,而天气逐渐寒冷,所以这才印证了Stark家族的族语

“Winter is coming”

得出结论

问题:上面的分析能够回答你提出的问题?通过这些分析你能够得出哪些结论?

答案

1.六位国王参战,比例如之前饼图所示
2.每位国王的胜率如之前的柱状图所示,Balond胜率最高
3.Joffrey更具侵略性,攻击别人次数最多
4.pitched battle战争类型最多,ambush类型胜率最高
5.具体家族及联盟关系详见分析
6.战争前后经历了3年,300年已经没有夏天,凛冬将至

反思

问题:在你的分析和总结过程中是否存在逻辑严谨。是否有改进的空间? 你可以从下面的一些角度进行思考:

  1. 数据集是否完整,包含所有想要分析的数据?
  2. 在对数据进行处理的时候,你的操作(例如删除/填充缺失值)是否可能影响结论?
  3. 是否还有其他变量(本数据中没有)能够对你的分析有帮助?
  4. 在得出结论时,你是否混淆了相关性和因果性?

答案

数据包含了38场战斗,有些数据缺失,从统计学意义上,这个样本数量其实很小,并不能得出一般性结论。NaN的值采取了直接剔除的方式,也许并非最科学的方法,可能会对结果产生一定影响。数据来源源自奇幻小说,数据集内容本身就是虚构的,有些并不一定符合客观的因果关系。如前所述,有些数据存在明显的错误,也会影响统计结果。另外,战争行为本就是复杂的,任何偶然的因素都会影响结果,更何况这些战争是发生在文学作品里,并非真实的,比如Stannis击败Renly一役,最初Stannis在人数兵力上不占优,打不下风息堡,后来Stannis依靠黑魔法隔空就把Renly杀死了……像这种作者让谁死谁就得死的本身就没有什么逻辑因果关系,再深入用统计学来研究本身意义也不大。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容