import pandas as pd
import numpy as np
from matplotlib import pyplot
from sklearn.preprocessing import Normalizer
from sklearn.ensemble import IsolationForest
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import cross_validate
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
#from sklearn.linear_model import LogisticRegression
#from sklearn.metrics import classification_report
#from sklearn.metrics import confusion_matrix
#from sklearn.metrics import roc_auc_score as auc_score
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
Previously we loaded the data and created a sample out of it. From now on we are going to use it for our analysis and modeling.
#gtd_ori = pd.read_excel("globalterrorismdb_0617dist.xlsx")
# Because the dataset is highly imbalanced, I tried to get a stratified sample, but was unable to do so.
#stsmp = train_test_split(gtd_ori, train_size=0.25, startify=['gname'])
#stsmp.to_excel("stsmp.xlsx")
#gtd = pd.read_excel("stsmp.xlsx")
#smp = gtd_ori.sample(frac=0.25, random_state=4721)
#smp.to_excel("sample.xlsx")
gtd = pd.read_excel("sample.xlsx")
gtd.info(verbose=True, null_counts=True, max_cols=True)
<class 'pandas.core.frame.DataFrame'> Int64Index: 42588 entries, 145918 to 94344 Data columns (total 135 columns): eventid 42588 non-null int64 iyear 42588 non-null int64 imonth 42588 non-null int64 iday 42588 non-null int64 approxdate 1876 non-null object extended 42588 non-null int64 resolution 560 non-null datetime64[ns] country 42588 non-null int64 country_txt 42588 non-null object region 42588 non-null int64 region_txt 42588 non-null object provstate 38954 non-null object city 42485 non-null object latitude 41454 non-null float64 longitude 41454 non-null float64 specificity 42587 non-null float64 vicinity 42588 non-null int64 location 11964 non-null object summary 26054 non-null object crit1 42588 non-null int64 crit2 42588 non-null int64 crit3 42588 non-null int64 doubtterr 42588 non-null int64 alternative 6663 non-null float64 alternative_txt 6663 non-null object multiple 42588 non-null int64 success 42588 non-null int64 suicide 42588 non-null int64 attacktype1 42588 non-null int64 attacktype1_txt 42588 non-null object attacktype2 1440 non-null float64 attacktype2_txt 1440 non-null object attacktype3 89 non-null float64 attacktype3_txt 89 non-null object targtype1 42588 non-null int64 targtype1_txt 42588 non-null object targsubtype1 40231 non-null float64 targsubtype1_txt 40231 non-null object corp1 32000 non-null object target1 42430 non-null object natlty1 42225 non-null float64 natlty1_txt 42225 non-null object targtype2 2498 non-null float64 targtype2_txt 2498 non-null object targsubtype2 2384 non-null float64 targsubtype2_txt 2384 non-null object corp2 2241 non-null object target2 2463 non-null object natlty2 2422 non-null float64 natlty2_txt 2422 non-null object targtype3 256 non-null float64 targtype3_txt 256 non-null object targsubtype3 237 non-null float64 targsubtype3_txt 237 non-null object corp3 223 non-null object target3 256 non-null object natlty3 250 non-null float64 natlty3_txt 250 non-null object gname 42588 non-null object gsubname 1284 non-null object gname2 450 non-null object gsubname2 38 non-null object gname3 62 non-null object gsubname3 2 non-null object motive 12147 non-null object guncertain1 42492 non-null float64 guncertain2 438 non-null float64 guncertain3 62 non-null float64 individual 42588 non-null int64 nperps 24824 non-null float64 nperpcap 25175 non-null float64 claimed 26055 non-null float64 claimmode 4077 non-null float64 claimmode_txt 4077 non-null object claim2 407 non-null float64 claimmode2 140 non-null float64 claimmode2_txt 140 non-null object claim3 61 non-null float64 claimmode3 25 non-null float64 claimmode3_txt 25 non-null object compclaim 1227 non-null float64 weaptype1 42588 non-null int64 weaptype1_txt 42588 non-null object weapsubtype1 37659 non-null float64 weapsubtype1_txt 37659 non-null object weaptype2 3035 non-null float64 weaptype2_txt 3035 non-null object weapsubtype2 2668 non-null float64 weapsubtype2_txt 2668 non-null object weaptype3 427 non-null float64 weaptype3_txt 427 non-null object weapsubtype3 389 non-null float64 weapsubtype3_txt 389 non-null object weaptype4 20 non-null float64 weaptype4_txt 20 non-null object weapsubtype4 18 non-null float64 weapsubtype4_txt 18 non-null object weapdetail 27643 non-null object nkill 40101 non-null float64 nkillus 26464 non-null float64 nkillter 25936 non-null float64 nwound 38727 non-null float64 nwoundus 26396 non-null float64 nwoundte 25392 non-null float64 property 42588 non-null int64 propextent 15026 non-null float64 propextent_txt 15026 non-null object propvalue 8805 non-null float64 propcomment 13594 non-null object ishostkid 42539 non-null float64 nhostkid 3230 non-null float64 nhostkidus 3218 non-null float64 nhours 964 non-null float64 ndays 1902 non-null float64 divert 80 non-null object kidhijcountry 850 non-null object ransom 19191 non-null float64 ransomamt 349 non-null float64 ransomamtus 128 non-null float64 ransompaid 178 non-null float64 ransompaidus 125 non-null float64 ransomnote 135 non-null object hostkidoutcome 2605 non-null float64 hostkidoutcome_txt 2605 non-null object nreleased 2456 non-null float64 addnotes 6390 non-null object scite1 26041 non-null object scite2 17318 non-null object scite3 9771 non-null object dbsource 42588 non-null object INT_LOG 42588 non-null int64 INT_IDEO 42588 non-null int64 INT_MISC 42588 non-null int64 INT_ANY 42588 non-null int64 related 5614 non-null object dtypes: datetime64[ns](1), float64(53), int64(24), object(57) memory usage: 44.2+ MB
We list the unique values of attributes and group them by their datatype:
uniques = gtd.nunique()
types = gtd.dtypes
atts = pd.concat([uniques, types], axis=1)
#atts.rename(columns=['types', 'uniques'], inplace=True)
atts.columns = ['uniques', 'types']
for coltype in atts.types.unique():
clist = atts[atts.types == coltype].sort_values(by='uniques', ascending=False)
print("\n{}:\n\n{}\n".format(coltype, clist.uniques))
int64: eventid 42588 country 183 iyear 46 iday 32 targtype1 22 imonth 13 region 12 weaptype1 12 attacktype1 9 INT_MISC 3 INT_IDEO 3 INT_LOG 3 property 3 INT_ANY 3 doubtterr 3 vicinity 3 suicide 2 success 2 individual 2 crit3 2 crit2 2 crit1 2 extended 2 multiple 2 Name: uniques, dtype: int64 object: summary 25703 target1 23296 scite1 22616 scite2 15947 city 13031 location 10912 corp1 9994 scite3 9069 weapdetail 5795 propcomment 5587 related 5386 addnotes 4251 motive 3874 gname 1710 provstate 1625 target2 1364 approxdate 1005 corp2 819 gsubname 461 target3 195 natlty1_txt 190 gname2 185 country_txt 183 kidhijcountry 126 natlty2_txt 121 corp3 116 targsubtype1_txt 110 ransomnote 106 targsubtype2_txt 80 natlty3_txt 63 targsubtype3_txt 57 divert 56 gname3 40 weapsubtype1_txt 29 gsubname2 28 weapsubtype2_txt 26 dbsource 25 targtype1_txt 22 targtype2_txt 22 weapsubtype3_txt 21 targtype3_txt 20 region_txt 12 weaptype1_txt 12 weaptype2_txt 10 weaptype3_txt 10 claimmode_txt 10 attacktype2_txt 9 claimmode2_txt 9 attacktype1_txt 9 weapsubtype4_txt 8 attacktype3_txt 7 hostkidoutcome_txt 7 claimmode3_txt 6 alternative_txt 5 weaptype4_txt 4 propextent_txt 3 gsubname3 2 Name: uniques, dtype: int64 datetime64[ns]: resolution 531 Name: uniques, dtype: int64 float64: latitude 20568 longitude 20436 propvalue 245 natlty1 190 nwound 154 ransomamt 145 ndays 136 nkill 125 natlty2 121 targsubtype1 110 nhostkid 108 targsubtype2 80 nreleased 78 nperps 76 natlty3 63 targsubtype3 57 nkillter 57 ransompaid 39 nperpcap 32 weapsubtype1 29 nhours 27 nwoundte 27 weapsubtype2 26 nwoundus 25 targtype2 22 weapsubtype3 21 targtype3 20 nkillus 17 nhostkidus 15 weaptype2 10 claimmode 10 weaptype3 10 attacktype2 9 claimmode2 9 weapsubtype4 8 hostkidoutcome 7 ransomamtus 7 attacktype3 7 claimmode3 6 alternative 5 specificity 5 weaptype4 4 propextent 3 claimed 3 ishostkid 3 claim2 3 ransom 3 compclaim 3 ransompaidus 3 claim3 2 guncertain3 2 guncertain2 2 guncertain1 2 Name: uniques, dtype: int64
There are many categorical or even binomial attributes which however contain their own missing value codes which we need to recode later.
Based on the GTD Codebook (adjusted with own analysis), the dataset consists of the following attribute groups:
att_time = ['eventid', 'iyear', 'imonth', 'iday', 'approxdate', 'extended', 'resolution']
att_loc = ['country', 'country_txt', 'region', 'region_txt', 'provstate', 'city', 'latitude', 'longitude', 'specificity','vicinity', 'location']
att_incid = ['summary', 'crit1', 'crit2', 'crit3', 'doubtterr', 'alternative', 'alternative_txt', 'multiple', 'related']
att_attack = ['success', 'suicide', 'attacktype1', 'attacktype1_txt', 'attacktype2', 'attacktype2_txt', 'attacktype3', 'attacktype3_txt']
att_perp = ['gname', 'gsubname', 'gname2', 'gsubname2', 'gname3', 'gsubname3']
att_perval = ['motive', 'guncertain1', 'guncertain2', 'guncertain3', 'individual', 'nperps', 'nperpcap', 'claimed', 'claimmode', 'claimmode_txt', 'claim2', 'claimmode2', 'claimmode2_txt', 'claim3', 'claimmode3', 'claimmode3_txt', 'compclaim']
att_weap = ['weaptype1', 'weaptype1_txt', 'weapsubtype1', 'weapsubtype1_txt', 'weaptype2', 'weaptype2_txt', 'weapsubtype2', 'weapsubtype2_txt', 'weaptype3', 'weaptype3_txt', 'weapsubtype3', 'weapsubtype3_txt', 'weaptype4', 'weaptype4_txt', 'weapsubtype4', 'weapsubtype4_txt', 'weapdetail']
att_targ = ['targtype1', 'targtype1_txt', 'targsubtype1', 'targsubtype1_txt', 'corp1', 'target1', 'natlty1', 'natlty1_txt', 'targtype2', 'targtype2_txt', 'targsubtype2', 'targsubtype2_txt', 'corp2', 'target2', 'natlty2', 'natlty2_txt', 'targtype3', 'targtype3_txt', 'targsubtype3', 'targsubtype3_txt', 'corp3', 'target3', 'natlty3', 'natlty3_txt']
att_cons = ['nkill', 'nkillus', 'nkillter', 'nwound', 'nwoundus', 'nwoundte', 'property', 'propextent', 'propextent_txt', 'propvalue', 'propcomment', 'ishostkid', 'nhostkid', 'nhostkidus', 'nhours', 'ndays', 'divert', 'kidhijcountry', 'ransom', 'ransomamt', 'ransomamtus', 'ransompaid', 'ransompaidus', 'ransomnote', 'hostkidoutcome', 'hostkidoutcome_txt', 'nreleased']
att_info = ['addnotes', 'scite1', 'scite2', 'scite3', 'dbsource', 'INT_LOG', 'INT_IDEO', 'INT_MISC', 'INT_ANY',]
gtd[att_perp]
gname | gsubname | gname2 | gsubname2 | gname3 | gsubname3 | |
---|---|---|---|---|---|---|
145918 | Unknown | NaN | NaN | NaN | NaN | NaN |
15674 | Unknown | NaN | NaN | NaN | NaN | NaN |
58204 | Palestinians | NaN | NaN | NaN | NaN | NaN |
41784 | Unknown | NaN | NaN | NaN | NaN | NaN |
111973 | Al-Nusrah Front | NaN | NaN | NaN | NaN | NaN |
160502 | Islamic State of Iraq and the Levant (ISIL) | NaN | NaN | NaN | NaN | NaN |
400 | Unknown | NaN | NaN | NaN | NaN | NaN |
165154 | Unknown | NaN | NaN | NaN | NaN | NaN |
81740 | Islamic Companies | NaN | NaN | NaN | NaN | NaN |
26125 | Nicaraguan Democratic Force (FDN) | NaN | NaN | NaN | NaN | NaN |
38988 | Irish Republican Army (IRA) | NaN | NaN | NaN | NaN | NaN |
134029 | Unknown | NaN | NaN | NaN | NaN | NaN |
74831 | Lord's Resistance Army (LRA) | NaN | NaN | NaN | NaN | NaN |
64451 | Kamal Boulander Group | NaN | NaN | NaN | NaN | NaN |
124039 | Taliban | NaN | NaN | NaN | NaN | NaN |
103697 | Militants | NaN | NaN | NaN | NaN | NaN |
14002 | Counter-revolutionaries | NaN | NaN | NaN | NaN | NaN |
159852 | Bangsamoro Islamic Freedom Movement (BIFM) | NaN | NaN | NaN | NaN | NaN |
141756 | Allied Democratic Forces (ADF) | NaN | NaN | NaN | NaN | NaN |
61614 | Unknown | NaN | NaN | NaN | NaN | NaN |
102932 | Unknown | NaN | NaN | NaN | NaN | NaN |
19038 | Unknown | NaN | NaN | NaN | NaN | NaN |
133918 | Unknown | NaN | NaN | NaN | NaN | NaN |
51364 | Shining Path (SL) | NaN | NaN | NaN | NaN | NaN |
110658 | Unknown | NaN | NaN | NaN | NaN | NaN |
16094 | M-19 (Movement of April 19) | NaN | NaN | NaN | NaN | NaN |
13396 | Shining Path (SL) | NaN | NaN | NaN | NaN | NaN |
33974 | New People's Army (NPA) | NaN | NaN | NaN | NaN | NaN |
2882 | Montoneros (Argentina) | NaN | NaN | NaN | NaN | NaN |
39150 | Manuel Rodriguez Patriotic Front (FPMR) | NaN | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... |
83350 | Unknown | NaN | NaN | NaN | NaN | NaN |
104644 | Unknown | NaN | NaN | NaN | NaN | NaN |
91669 | Communist Party of India - Maoist (CPI-Maoist) | NaN | NaN | NaN | NaN | NaN |
94897 | Unknown | NaN | NaN | NaN | NaN | NaN |
153331 | Kurdistan Workers' Party (PKK) | NaN | NaN | NaN | NaN | NaN |
102862 | Al-Shabaab | NaN | NaN | NaN | NaN | NaN |
72222 | Unknown | NaN | NaN | NaN | NaN | NaN |
146239 | Islamic State of Iraq and the Levant (ISIL) | NaN | NaN | NaN | NaN | NaN |
124352 | Communist Party of India - Maoist (CPI-Maoist) | NaN | NaN | NaN | NaN | NaN |
78196 | Party for the Liberation of the Hutu People (P... | Forces for National Liberation (FNL) | NaN | NaN | NaN | NaN |
46567 | November 17 Revolutionary Organization (N17RO) | NaN | NaN | NaN | NaN | NaN |
80043 | Unknown | NaN | NaN | NaN | NaN | NaN |
22872 | African National Congress (South Africa) | NaN | NaN | NaN | NaN | NaN |
169580 | Jabha East Africa | NaN | NaN | NaN | NaN | NaN |
4524 | Independent Armed Revolutionary Commandos (CRIA) | NaN | NaN | NaN | NaN | NaN |
98216 | New People's Army (NPA) | Front Committee 6 (CPP-NPA North Central Minda... | NaN | NaN | NaN | NaN |
38810 | National Liberation Army of Colombia (ELN) | NaN | NaN | NaN | NaN | NaN |
170107 | Houthi extremists (Ansar Allah) | NaN | NaN | NaN | NaN | NaN |
56799 | Bodo Militants | NaN | NaN | NaN | NaN | NaN |
116556 | Unknown | NaN | NaN | NaN | NaN | NaN |
170338 | Unknown | NaN | NaN | NaN | NaN | NaN |
135835 | Al-Qaida in the Arabian Peninsula (AQAP) | NaN | NaN | NaN | NaN | NaN |
13994 | Iranians | NaN | NaN | NaN | NaN | NaN |
15718 | Unknown | NaN | NaN | NaN | NaN | NaN |
85256 | Al-Fatihin Army (AFA) | Abu Ayyub al-Ansari Brigade | NaN | NaN | NaN | NaN |
95832 | Unknown | NaN | NaN | NaN | NaN | NaN |
152849 | Kurdistan Workers' Party (PKK) | NaN | NaN | NaN | NaN | NaN |
94094 | Unknown | NaN | NaN | NaN | NaN | NaN |
69569 | Lashkar-e-Taiba (LeT) | NaN | NaN | NaN | NaN | NaN |
94344 | United Liberation Front of Assam (ULFA) | NaN | NaN | NaN | NaN | NaN |
42588 rows × 6 columns
More than 45% of the perpetrators are unknown:
gtd.gname.value_counts(normalize=True).head(1)
Unknown 0.460975 Name: gname, dtype: float64
known = gtd[gtd.gname != 'Unknown']
The proportion of recognized groups responsible only for a single incident:
len(known.gname.value_counts()[known.gname.value_counts() <= 1]) / len(known)
0.03859557414183656
The distribution of incidents among the known groups:
inc_grp = known.gname.value_counts()
print(inc_grp)
Taliban 1615 Islamic State of Iraq and the Levant (ISIL) 1108 Shining Path (SL) 1087 Farabundo Marti National Liberation Front (FMLN) 812 Al-Shabaab 688 Irish Republican Army (IRA) 683 Revolutionary Armed Forces of Colombia (FARC) 617 New People's Army (NPA) 607 Kurdistan Workers' Party (PKK) 563 Boko Haram 519 Basque Fatherland and Freedom (ETA) 512 Communist Party of India - Maoist (CPI-Maoist) 430 Liberation Tigers of Tamil Eelam (LTTE) 400 National Liberation Army of Colombia (ELN) 365 Maoists 349 Tehrik-i-Taliban Pakistan (TTP) 299 Palestinians 245 Al-Qaida in the Arabian Peninsula (AQAP) 245 Houthi extremists (Ansar Allah) 224 Nicaraguan Democratic Force (FDN) 212 Manuel Rodriguez Patriotic Front (FPMR) 201 Sikh Extremists 182 African National Congress (South Africa) 160 Al-Qaida in Iraq 157 Corsican National Liberation Front (FLNC) 155 Donetsk People's Republic 150 Tupac Amaru Revolutionary Movement (MRTA) 147 Separatists 145 Muslim extremists 138 M-19 (Movement of April 19) 121 ... SFT 1 Real Ulster Freedom Fighters (UFF) - Northern Ireland 1 United Kuki Liberation Front (UKLF) - India 1 Revolutionary Action Party 1 Revolutionary Action Front 1 Bande des Rats (the rat pack) Ukranian Group 1 Fatherland 1 Epanastatiki Anatropi (Revolutionary Overthrow) 1 Arakan Liberation Party (ALP) 1 Bahujan Samaj Party 1 pro-iranian terrorists 1 Young Brigade of Navarro 1 Drug-Related Terrorists 1 God's Army 1 313 Brigade (Syria) 1 Local Residents 1 Zulu Miners 1 Right-wing Terrorists 1 Suqour al-Ahvaz 1 Che Guevara Guerrillas 1 Fighting Women 1 Grupo Libertad 1 Kaitseliit Paramiltary Group 1 Iranian extremists 1 Kizilyurtovskiy Group 1 Kachin Insurgents 1 Revolutionary Road 1 Arbav Martyrs of Khuzestan 1 Popular Resistance Sabotage Group 1 U/I Private army of wral politician 1 Name: gname, Length: 1709, dtype: int64
Around a fifth of the groups is responsible for 90% of all known incidents and 40% of them for 95%.
inc_grp_csum = known.gname.value_counts(normalize=True).cumsum()
inc_grp_csum
Taliban 0.070352 Islamic State of Iraq and the Levant (ISIL) 0.118618 Shining Path (SL) 0.165970 Farabundo Marti National Liberation Front (FMLN) 0.201342 Al-Shabaab 0.231312 Irish Republican Army (IRA) 0.261065 Revolutionary Armed Forces of Colombia (FARC) 0.287942 New People's Army (NPA) 0.314384 Kurdistan Workers' Party (PKK) 0.338909 Boko Haram 0.361518 Basque Fatherland and Freedom (ETA) 0.383821 Communist Party of India - Maoist (CPI-Maoist) 0.402553 Liberation Tigers of Tamil Eelam (LTTE) 0.419977 National Liberation Army of Colombia (ELN) 0.435877 Maoists 0.451080 Tehrik-i-Taliban Pakistan (TTP) 0.464105 Palestinians 0.474778 Al-Qaida in the Arabian Peninsula (AQAP) 0.485450 Houthi extremists (Ansar Allah) 0.495208 Nicaraguan Democratic Force (FDN) 0.504443 Manuel Rodriguez Patriotic Front (FPMR) 0.513199 Sikh Extremists 0.521127 African National Congress (South Africa) 0.528097 Al-Qaida in Iraq 0.534936 Corsican National Liberation Front (FLNC) 0.541688 Donetsk People's Republic 0.548223 Tupac Amaru Revolutionary Movement (MRTA) 0.554626 Separatists 0.560943 Muslim extremists 0.566954 M-19 (Movement of April 19) 0.572225 ... SFT 0.998737 Real Ulster Freedom Fighters (UFF) - Northern Ireland 0.998780 United Kuki Liberation Front (UKLF) - India 0.998824 Revolutionary Action Party 0.998867 Revolutionary Action Front 0.998911 Bande des Rats (the rat pack) Ukranian Group 0.998955 Fatherland 0.998998 Epanastatiki Anatropi (Revolutionary Overthrow) 0.999042 Arakan Liberation Party (ALP) 0.999085 Bahujan Samaj Party 0.999129 pro-iranian terrorists 0.999172 Young Brigade of Navarro 0.999216 Drug-Related Terrorists 0.999259 God's Army 0.999303 313 Brigade (Syria) 0.999347 Local Residents 0.999390 Zulu Miners 0.999434 Right-wing Terrorists 0.999477 Suqour al-Ahvaz 0.999521 Che Guevara Guerrillas 0.999564 Fighting Women 0.999608 Grupo Libertad 0.999652 Kaitseliit Paramiltary Group 0.999695 Iranian extremists 0.999739 Kizilyurtovskiy Group 0.999782 Kachin Insurgents 0.999826 Revolutionary Road 0.999869 Arbav Martyrs of Khuzestan 0.999913 Popular Resistance Sabotage Group 0.999956 U/I Private army of wral politician 1.000000 Name: gname, Length: 1709, dtype: float64
len(inc_grp_csum[inc_grp_csum <= 0.90]) / len(gtd.gname.unique())
0.1976608187134503
len(inc_grp_csum[inc_grp_csum <= 0.95]) / len(gtd.gname.unique())
0.4046783625730994
The database also records second and third groups but only for around 1% of the total incidents:
names = known.loc[:, ['gname', 'gname2', 'gname3']]
names.count() / len(known)
gname 1.000000 gname2 0.019603 gname3 0.002701 dtype: float64
names.apply(lambda x: x.value_counts(dropna=False)).sort_values(by='gname', ascending=False)
gname | gname2 | gname3 | |
---|---|---|---|
Taliban | 1615.0 | 8.0 | NaN |
Islamic State of Iraq and the Levant (ISIL) | 1108.0 | 7.0 | 5.0 |
Shining Path (SL) | 1087.0 | NaN | NaN |
Farabundo Marti National Liberation Front (FMLN) | 812.0 | NaN | NaN |
Al-Shabaab | 688.0 | 1.0 | NaN |
Irish Republican Army (IRA) | 683.0 | NaN | NaN |
Revolutionary Armed Forces of Colombia (FARC) | 617.0 | 10.0 | NaN |
New People's Army (NPA) | 607.0 | 4.0 | NaN |
Kurdistan Workers' Party (PKK) | 563.0 | 3.0 | NaN |
Boko Haram | 519.0 | 3.0 | 1.0 |
Basque Fatherland and Freedom (ETA) | 512.0 | NaN | NaN |
Communist Party of India - Maoist (CPI-Maoist) | 430.0 | NaN | NaN |
Liberation Tigers of Tamil Eelam (LTTE) | 400.0 | NaN | NaN |
National Liberation Army of Colombia (ELN) | 365.0 | 7.0 | NaN |
Maoists | 349.0 | NaN | NaN |
Tehrik-i-Taliban Pakistan (TTP) | 299.0 | 7.0 | 2.0 |
Al-Qaida in the Arabian Peninsula (AQAP) | 245.0 | 6.0 | NaN |
Palestinians | 245.0 | NaN | NaN |
Houthi extremists (Ansar Allah) | 224.0 | 5.0 | NaN |
Nicaraguan Democratic Force (FDN) | 212.0 | NaN | NaN |
Manuel Rodriguez Patriotic Front (FPMR) | 201.0 | NaN | NaN |
Sikh Extremists | 182.0 | NaN | NaN |
African National Congress (South Africa) | 160.0 | NaN | NaN |
Al-Qaida in Iraq | 157.0 | 1.0 | NaN |
Corsican National Liberation Front (FLNC) | 155.0 | NaN | NaN |
Donetsk People's Republic | 150.0 | NaN | NaN |
Tupac Amaru Revolutionary Movement (MRTA) | 147.0 | 2.0 | NaN |
Separatists | 145.0 | NaN | NaN |
Muslim extremists | 138.0 | NaN | NaN |
M-19 (Movement of April 19) | 121.0 | 4.0 | NaN |
... | ... | ... | ... |
Niger Delta Patriotic Force | NaN | 1.0 | NaN |
Abdul Ghani Kikli Militia | NaN | 1.0 | NaN |
Other | NaN | 1.0 | NaN |
The Mukti Bahini | NaN | 1.0 | NaN |
Revolutionaries of the Streets | NaN | 1.0 | NaN |
All Nepal National Free Student Union-Revolutionary | NaN | 1.0 | NaN |
Al-Da'wah Party | NaN | 1.0 | NaN |
Shan State Army - South (SSA-S) | NaN | 1.0 | NaN |
Khasavyurt Group | NaN | 1.0 | NaN |
Vietnamese Organization to Exterminate Communists and Restore the Nation | NaN | 1.0 | NaN |
Boricuan Armed Anti-Imperialist Commandos | NaN | 1.0 | NaN |
Nduma Defense of Congo (NDC) | NaN | 1.0 | NaN |
Kurdish Islamic Group (KIG) | NaN | 1.0 | NaN |
Revolutionary Student Movement (MER) | NaN | 1.0 | NaN |
Rohingya Solidarity Organization | NaN | 1.0 | NaN |
Ansar Al-Mujahideen (Pakistan) | NaN | 1.0 | NaN |
Egyptian Tawhid and Jihad | NaN | 1.0 | NaN |
Abu Hafs Katibatul al-Ghurba al-Mujahideen | NaN | 1.0 | NaN |
Asif Raza Commandos | NaN | 1.0 | NaN |
Circle of Violators/Nucleus Lovers of Anomy | NaN | 1.0 | NaN |
Carlos the Jackal | NaN | 1.0 | NaN |
Black Banner Brigade | NaN | 1.0 | NaN |
Waning Abdusalam Group (WAG) | NaN | 1.0 | NaN |
Armed Forces of Popular Resistance (FARP) | NaN | NaN | 2.0 |
Mutassim Bellah Brigade | NaN | NaN | 1.0 |
Maoist Communist Central of Nepal (MCCN) | NaN | NaN | 1.0 |
Authenticity and Development Front | NaN | NaN | 1.0 |
Sirri Powz | NaN | NaN | 1.0 |
Guerrilla Column 29 September | NaN | NaN | 1.0 |
Armed Renaissance Group of Ahvaz | NaN | NaN | 1.0 |
1757 rows × 3 columns
The database also records subnames for perpetrators for around 3% of the perpetrators.
sub_names = gtd.loc[:,['gsubname', 'gsubname2', 'gsubname3']].dropna(how='all').groupby(by=gtd.loc[:,'gname']).count()
sub_names.sum() / len(gtd)
gsubname 0.030149 gsubname2 0.000892 gsubname3 0.000047 dtype: float64
sub_names.sort_values(by='gsubname', ascending=False)
gsubname | gsubname2 | gsubname3 | |
---|---|---|---|
gname | |||
Farabundo Marti National Liberation Front (FMLN) | 123 | 0 | 0 |
Revolutionary Armed Forces of Colombia (FARC) | 107 | 1 | 0 |
Sikh Extremists | 101 | 0 | 0 |
Islamic State of Iraq and the Levant (ISIL) | 56 | 4 | 0 |
New People's Army (NPA) | 53 | 0 | 0 |
Tehrik-i-Taliban Pakistan (TTP) | 36 | 0 | 0 |
Tamils | 35 | 0 | 0 |
Kurdistan Workers' Party (PKK) | 34 | 1 | 0 |
Chechen Rebels | 32 | 0 | 0 |
Opposition Group | 31 | 0 | 0 |
Hezbollah | 30 | 0 | 0 |
United Popular Action Movement | 27 | 0 | 0 |
Communist Party of India - Maoist (CPI-Maoist) | 25 | 0 | 0 |
Islamist extremists | 23 | 0 | 0 |
National Democratic Front of Bodoland (NDFB) | 22 | 0 | 0 |
Hutu extremists | 18 | 0 | 0 |
Al-Qaida in the Arabian Peninsula (AQAP) | 17 | 0 | 0 |
Palestinian Islamic Jihad (PIJ) | 15 | 0 | 0 |
Hamas (Islamic Resistance Movement) | 15 | 0 | 0 |
Free Syrian Army | 14 | 2 | 0 |
Basque Fatherland and Freedom (ETA) | 13 | 0 | 0 |
Dev Sol | 12 | 0 | 0 |
Communists | 12 | 0 | 0 |
National Liberation Army of Colombia (ELN) | 11 | 0 | 0 |
Jamaat-E-Islami (Bangladesh) | 11 | 0 | 0 |
Abu Nidal Organization (ANO) | 11 | 0 | 0 |
Lorenzo Zelaya Revolutionary Front (LZRF) | 9 | 0 | 0 |
Party for the Liberation of the Hutu People (PALIPEHUTU) | 9 | 1 | 0 |
Al-Qaida in the Islamic Maghreb (AQIM) | 9 | 0 | 0 |
Breton Liberation Front (FLB) | 8 | 0 | 0 |
... | ... | ... | ... |
Mayi Mayi | 1 | 0 | 0 |
May 19 Communist Order | 1 | 0 | 0 |
Muslim Demonstrators | 1 | 0 | 0 |
Democratic Front for the Liberation of Palestine (DFLP) | 1 | 0 | 0 |
Madhesi Mukti Tigers (MMT) | 1 | 0 | 0 |
Chechen Lone Wolf Group | 1 | 0 | 0 |
Narco-Terrorists | 1 | 0 | 0 |
Libya Shield Force | 1 | 0 | 0 |
Barisan Revolusi Nasional (BRN) | 1 | 0 | 0 |
Left-Wing Terrorists | 1 | 0 | 0 |
Lashkar-e-Taiba (LeT) | 1 | 0 | 0 |
Anarchist Faction | 1 | 0 | 0 |
National Socialist Council of Nagaland-Isak-Muivah (NSCN-IM) | 1 | 0 | 0 |
Ku Klux Klan | 1 | 0 | 0 |
Naxalites | 1 | 0 | 0 |
Al-Fatihin Army (AFA) | 1 | 0 | 0 |
Movement of the Revolutionary Left (MIR) (Peru) | 1 | 0 | 0 |
Al-Nasir Army (Syria) | 0 | 1 | 0 |
Aleppo Fatah Operations Room | 0 | 1 | 0 |
Sinai Province of the Islamic State | 0 | 1 | 0 |
Shura Council of Benghazi Revolutionaries | 0 | 1 | 0 |
Islamic State in Bangladesh | 0 | 1 | 1 |
Liwa Ahrar al-Sunna | 0 | 1 | 0 |
Libya Revolutionaries Operations Room (LROR) | 0 | 1 | 0 |
Lebanese National Resistance Front | 0 | 1 | 0 |
Asbat al-Ansar | 0 | 1 | 0 |
Jundallah (Pakistan) | 0 | 1 | 0 |
Sovereign Citizen | 0 | 1 | 0 |
Khorasan Chapter of the Islamic State | 0 | 1 | 1 |
Deccan Mujahideen | 0 | 1 | 0 |
214 rows × 3 columns
Around 15% of perpetrator information is in a 'suspected' status.
guncertcols = gtd.loc[:, ['guncertain1','guncertain2','guncertain3']]
guncertcols[guncertcols == 1].dropna(how='all').count() / len(known)
guncertain1 0.154469 guncertain2 0.005184 guncertain3 0.000697 dtype: float64
On the other hand, the coding book is not clear about what exactly the '1' of 'uncertainty' value means compared to the '0' and 'NaN' values.
guncertcols.apply(lambda x: x.value_counts(dropna=False))
guncertain1 | guncertain2 | guncertain3 | |
---|---|---|---|
0.0 | 38946 | 319 | 46 |
1.0 | 3546 | 119 | 16 |
NaN | 96 | 42150 | 42526 |
The ratio of 'unaffiliated' individuals (i.e. individuals who were recognised but were not affiliated to known groups) is 0.26%.
gtd.individual.value_counts().iloc[1] / len(gtd)
0.0026063679909833757
Where the perpetrator is an unaffiliated individual it is somehow also connected to a vague, broadly defined group:
gtd.gname[(gtd.individual == 1) & (gtd.gname != "Unknown")].value_counts()
Jihadi-inspired extremists 13 White extremists 9 Anti-Government extremists 8 Anti-Muslim extremists 8 Muslim extremists 7 Anti-Abortion extremists 7 Anti-Semitic extremists 5 Iraqi extremists 3 Anti-Police extremists 3 Anti-Nuclear extremists 3 Right-wing extremists 3 Marxists 2 Animal Rights extremists 2 Anti-Sikh extremists 1 Anti-Israeli extremists 1 African-American extremists 1 Neo-Nazi extremists 1 Moroccan extremists 1 Anti-Yanukovych extremists 1 Court Reform extremists 1 Gaddafi loyalists 1 Supporters of Saddam Hussein 1 Turkish radicals 1 Anti-Immigrant extremists 1 Anti-Environmentalists 1 Armenian extremists 1 Armenian nationalists 1 Name: gname, dtype: int64
Because of the relative low coverage, we will not try to predict second/third perpetrators and subnames. Later on, the process might be enhanced to become able to do so.
The data set also contains a number of special attributes, from among which we drop the eventid
column, addnotes
and the information about the record's data source:
temp = gtd.drop(['eventid', 'addnotes', 'scite1', 'scite2', 'scite3', 'dbsource'], axis=1, errors='ignore')
The miscodes
dictionary defines the attribute-missing value code pairs:
miscodes = {"0": ['imonth', 'iday'],
"-9": ['claim2', 'claimed', 'compclaim', 'doubtterr', 'INT_ANY', 'INT_IDEO', 'INT_LOG', 'INT_MISC',
'ishostkid', 'ndays', 'nhostkid', 'nhostkidus', 'nhours', 'nperpcap', 'nperps', 'nreleased',
'property', 'ransom', 'ransomamt', 'ransomamtus', 'ransompaid', 'ransompaidus', 'vicinity'],
"-99": ['ransompaid', 'nperpcap', 'compclaim', 'nreleased', 'nperps', 'nhostkidus', 'ransomamtus',
'ransomamt', 'nhours', 'ndays', 'propvalue', 'ransompaidus', 'nhostkid']}
temp[miscodes['0']].apply(lambda x: x.value_counts())
imonth | iday | |
---|---|---|
0 | 6.0 | 238 |
1 | 3522.0 | 1530 |
2 | 3234.0 | 1403 |
3 | 3606.0 | 1407 |
4 | 3493.0 | 1410 |
5 | 3843.0 | 1402 |
6 | 3689.0 | 1329 |
7 | 3826.0 | 1379 |
8 | 3665.0 | 1331 |
9 | 3371.0 | 1414 |
10 | 3727.0 | 1476 |
11 | 3476.0 | 1404 |
12 | 3130.0 | 1421 |
13 | NaN | 1440 |
14 | NaN | 1444 |
15 | NaN | 1531 |
16 | NaN | 1436 |
17 | NaN | 1389 |
18 | NaN | 1353 |
19 | NaN | 1381 |
20 | NaN | 1404 |
21 | NaN | 1366 |
22 | NaN | 1338 |
23 | NaN | 1336 |
24 | NaN | 1317 |
25 | NaN | 1320 |
26 | NaN | 1413 |
27 | NaN | 1421 |
28 | NaN | 1396 |
29 | NaN | 1250 |
30 | NaN | 1187 |
31 | NaN | 722 |
temp[miscodes['-9']].apply(lambda x: x.value_counts())
claim2 | claimed | compclaim | doubtterr | INT_ANY | INT_IDEO | INT_LOG | INT_MISC | ishostkid | ndays | ... | nperpcap | nperps | nreleased | property | ransom | ransomamt | ransomamtus | ransompaid | ransompaidus | vicinity | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
-9.900000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 991.0 | ... | 415.0 | 18216.0 | 820.0 | NaN | NaN | 54.0 | 8.0 | 74.0 | 10.0 | NaN |
-9.000000e+00 | 2.0 | 355.0 | 874.0 | 3464.0 | 19756.0 | 21950.0 | 21916.0 | 124.0 | 69.0 | NaN | ... | 3.0 | 1.0 | NaN | 4887.0 | 360.0 | 1.0 | NaN | NaN | NaN | 11.0 |
0.000000e+00 | 266.0 | 21646.0 | 254.0 | 32464.0 | 14054.0 | 15518.0 | 18972.0 | 37364.0 | 39240.0 | 54.0 | ... | 23817.0 | 36.0 | 531.0 | 15271.0 | 18493.0 | 104.0 | 114.0 | 62.0 | 114.0 | 39592.0 |
1.000000e+00 | 139.0 | 4054.0 | 99.0 | 6660.0 | 8778.0 | 5120.0 | 1700.0 | 5100.0 | 3230.0 | 199.0 | ... | 444.0 | 1887.0 | 503.0 | 22430.0 | 338.0 | 2.0 | NaN | NaN | NaN | 2985.0 |
2.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 97.0 | ... | 175.0 | 1422.0 | 177.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 84.0 | ... | 99.0 | 676.0 | 87.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 41.0 | ... | 69.0 | 530.0 | 61.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
5.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 42.0 | ... | 37.0 | 257.0 | 40.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
6.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 35.0 | ... | 23.0 | 181.0 | 27.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
7.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 32.0 | ... | 13.0 | 107.0 | 19.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
8.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 21.0 | ... | 16.0 | 92.0 | 10.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
9.000000e+00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 12.0 | ... | 5.0 | 35.0 | 14.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.000000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 7.0 | ... | 9.0 | 163.0 | 16.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.100000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 17.0 | ... | 4.0 | 7.0 | 8.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.200000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 11.0 | ... | 3.0 | 67.0 | 15.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.300000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 11.0 | ... | 6.0 | 16.0 | 5.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.400000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 16.0 | ... | 1.0 | 11.0 | 7.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.500000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10.0 | ... | 6.0 | 107.0 | 7.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.600000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4.0 | ... | 8.0 | 10.0 | 5.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.700000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3.0 | ... | 2.0 | 11.0 | 3.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.800000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4.0 | ... | 5.0 | 7.0 | 4.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1.900000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 6.0 | ... | NaN | 3.0 | 4.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.000000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 9.0 | ... | 2.0 | 132.0 | 3.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.100000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 7.0 | ... | NaN | 9.0 | 2.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.200000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5.0 | ... | NaN | 4.0 | 4.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.300000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 7.0 | ... | NaN | 1.0 | 2.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.400000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | ... | NaN | 22.0 | 4.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.500000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5.0 | ... | 2.0 | 54.0 | 4.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.600000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3.0 | ... | 1.0 | 1.0 | 2.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2.700000e+01 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2.0 | ... | 2.0 | 1.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1.980000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
2.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 6.0 | 1.0 | 2.0 | NaN | NaN |
2.200000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
2.421162e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
2.500000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN | NaN | NaN |
3.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 2.0 | 1.0 | NaN | NaN | NaN |
3.400000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
5.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 3.0 | NaN | NaN | NaN | NaN |
5.070000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
5.095500e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
5.107000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
5.600000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
5.800000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
6.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 4.0 | 2.0 | NaN | NaN | NaN |
6.862964e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
7.461682e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
8.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
9.476880e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.100000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.116390e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.156204e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.300000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.500000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN | NaN | NaN |
2.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | 1.0 | NaN | NaN | NaN |
2.500000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
3.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN | NaN | NaN |
3.283965e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
8.200000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
1.000000e+09 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN |
359 rows × 23 columns
temp[miscodes['-99']].apply(lambda x: x.value_counts())
ransompaid | nperpcap | compclaim | nreleased | nperps | nhostkidus | ransomamtus | ransomamt | nhours | ndays | propvalue | ransompaidus | nhostkid | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
-9.900000e+01 | 74.0 | 415.0 | NaN | 820.0 | 18216.0 | 14.0 | 8.0 | 54.0 | 487.0 | 991.0 | 6232.0 | 10.0 | 297.0 |
-9.000000e+00 | NaN | 3.0 | 874.0 | NaN | 1.0 | NaN | NaN | 1.0 | 3.0 | NaN | NaN | NaN | NaN |
0.000000e+00 | 62.0 | 23817.0 | 254.0 | 531.0 | 36.0 | 3110.0 | 114.0 | 104.0 | 326.0 | 54.0 | 46.0 | 114.0 | 1.0 |
1.000000e+00 | NaN | 444.0 | 99.0 | 503.0 | 1887.0 | 54.0 | NaN | 2.0 | 42.0 | 199.0 | NaN | NaN | 1299.0 |
2.000000e+00 | NaN | 175.0 | NaN | 177.0 | 1422.0 | 18.0 | NaN | NaN | 28.0 | 97.0 | NaN | NaN | 453.0 |
3.000000e+00 | NaN | 99.0 | NaN | 87.0 | 676.0 | 2.0 | NaN | NaN | 16.0 | 84.0 | NaN | NaN | 230.0 |
4.000000e+00 | NaN | 69.0 | NaN | 61.0 | 530.0 | 9.0 | NaN | NaN | 6.0 | 41.0 | NaN | NaN | 171.0 |
5.000000e+00 | NaN | 37.0 | NaN | 40.0 | 257.0 | 1.0 | NaN | NaN | 5.0 | 42.0 | NaN | NaN | 101.0 |
6.000000e+00 | NaN | 23.0 | NaN | 27.0 | 181.0 | 2.0 | NaN | NaN | 9.0 | 35.0 | NaN | NaN | 83.0 |
7.000000e+00 | NaN | 13.0 | NaN | 19.0 | 107.0 | 1.0 | NaN | NaN | 2.0 | 32.0 | NaN | NaN | 57.0 |
8.000000e+00 | NaN | 16.0 | NaN | 10.0 | 92.0 | NaN | NaN | NaN | 3.0 | 21.0 | NaN | NaN | 54.0 |
9.000000e+00 | NaN | 5.0 | NaN | 14.0 | 35.0 | 1.0 | NaN | NaN | 2.0 | 12.0 | NaN | NaN | 28.0 |
1.000000e+01 | NaN | 9.0 | NaN | 16.0 | 163.0 | 1.0 | NaN | NaN | 5.0 | 7.0 | NaN | NaN | 46.0 |
1.100000e+01 | NaN | 4.0 | NaN | 8.0 | 7.0 | 2.0 | NaN | NaN | NaN | 17.0 | NaN | NaN | 32.0 |
1.200000e+01 | NaN | 3.0 | NaN | 15.0 | 67.0 | NaN | NaN | NaN | 5.0 | 11.0 | NaN | NaN | 31.0 |
1.300000e+01 | NaN | 6.0 | NaN | 5.0 | 16.0 | NaN | NaN | NaN | 1.0 | 11.0 | NaN | NaN | 9.0 |
1.400000e+01 | NaN | 1.0 | NaN | 7.0 | 11.0 | NaN | NaN | NaN | NaN | 16.0 | NaN | NaN | 16.0 |
1.500000e+01 | NaN | 6.0 | NaN | 7.0 | 107.0 | NaN | NaN | NaN | 3.0 | 10.0 | NaN | NaN | 28.0 |
1.600000e+01 | NaN | 8.0 | NaN | 5.0 | 10.0 | NaN | NaN | NaN | 2.0 | 4.0 | NaN | NaN | 13.0 |
1.700000e+01 | NaN | 2.0 | NaN | 3.0 | 11.0 | 1.0 | NaN | NaN | 1.0 | 3.0 | NaN | NaN | 13.0 |
1.800000e+01 | NaN | 5.0 | NaN | 4.0 | 7.0 | NaN | NaN | NaN | 4.0 | 4.0 | NaN | NaN | 11.0 |
1.900000e+01 | NaN | NaN | NaN | 4.0 | 3.0 | NaN | NaN | NaN | 2.0 | 6.0 | NaN | NaN | 8.0 |
2.000000e+01 | NaN | 2.0 | NaN | 3.0 | 132.0 | NaN | NaN | NaN | 1.0 | 9.0 | NaN | NaN | 22.0 |
2.100000e+01 | NaN | NaN | NaN | 2.0 | 9.0 | NaN | NaN | NaN | NaN | 7.0 | NaN | NaN | 4.0 |
2.200000e+01 | NaN | NaN | NaN | 4.0 | 4.0 | NaN | NaN | NaN | 2.0 | 5.0 | NaN | NaN | 9.0 |
2.300000e+01 | NaN | NaN | NaN | 2.0 | 1.0 | NaN | NaN | NaN | 1.0 | 7.0 | NaN | NaN | 4.0 |
2.400000e+01 | NaN | NaN | NaN | 4.0 | 22.0 | NaN | NaN | NaN | 2.0 | 1.0 | NaN | NaN | 10.0 |
2.500000e+01 | NaN | 2.0 | NaN | 4.0 | 54.0 | NaN | NaN | NaN | NaN | 5.0 | NaN | NaN | 10.0 |
2.600000e+01 | NaN | 1.0 | NaN | 2.0 | 1.0 | NaN | NaN | NaN | 1.0 | 3.0 | NaN | NaN | 3.0 |
2.700000e+01 | NaN | 2.0 | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN | 4.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
5.800000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
6.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | 2.0 | 4.0 | NaN | NaN | 1.0 | NaN | NaN |
6.862964e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
7.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN |
7.461682e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
8.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | 1.0 | NaN | NaN |
8.034500e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
8.300000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
9.000000e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
9.476880e+06 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
1.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | 2.0 | NaN | NaN |
1.100000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
1.116390e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
1.156204e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
1.200000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
1.300000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
1.500000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN | 1.0 | NaN | NaN |
1.900000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
2.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | 1.0 | NaN | NaN | NaN | NaN | NaN |
2.100000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
2.140000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
2.400000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
2.500000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
2.857143e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
3.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2.0 | NaN | NaN | 2.0 | NaN | NaN |
3.283965e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
6.000000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
8.200000e+07 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
5.480000e+08 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN |
1.000000e+09 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN |
535 rows × 13 columns
We transform these missing value codes into numpy NaN
values
def mistonan(data, collist, nancode):
"""Replaces columns' missing value code with numpy NaN.
Parameters:
`data`: dataframe
`nanvalue` : the code of the missing value in the columns
"""
colstonan = []
for col in collist:
if col in data.columns:
colstonan.append(col)
else:
print("'{}' is not among the dataframe's columns.".format(col))
data[colstonan] = data[colstonan].apply(lambda x: x.replace(nancode, np.NaN))
for key in miscodes.keys():
mistonan(temp, miscodes[key], float(key))
We also replace "Unknown" values with numpy NaN
s whenever it occurs except in the gname
target attribute (which we do separately, when needed).
temp.drop(columns='gname').replace(to_replace='Unknown', value=np.NaN, inplace=True)
def missing_ratio(data):
"""
Lists missing values ratios for each column in a dataset.
Takes `data`, dataset.
Returns the `mrat` dataframe, which lists the columns and their corresponding missing value ratios in descending order.
"""
mrat = data.isna().mean()[data.isna().any()== True].sort_values()
return mrat
misrat = missing_ratio(temp)
From among the total 135 attributes 104 contains missing values. 96 of them has a 95% missing value rate:
misrat[misrat > 0.05].count()
96
misrat
specificity 0.000023 imonth 0.000141 vicinity 0.000258 guncertain1 0.002254 city 0.002419 ishostkid 0.002771 INT_MISC 0.002912 target1 0.003710 iday 0.005588 natlty1_txt 0.008524 natlty1 0.008524 latitude 0.026627 longitude 0.026627 targsubtype1_txt 0.055344 targsubtype1 0.055344 nkill 0.058397 doubtterr 0.081337 provstate 0.085329 nwound 0.090659 property 0.114751 weapsubtype1 0.115737 weapsubtype1_txt 0.115737 corp1 0.248615 weapdetail 0.350920 nkillus 0.378604 nwoundus 0.380201 summary 0.388231 nkillter 0.391002 claimed 0.396544 nwoundte 0.403776 ... compclaim 0.991711 ransomamt 0.993097 targtype3_txt 0.993989 target3 0.993989 targtype3 0.993989 natlty3_txt 0.994130 natlty3 0.994130 targsubtype3_txt 0.994435 targsubtype3 0.994435 corp3 0.994764 claimmode2 0.996713 claimmode2_txt 0.996713 ransomnote 0.996830 ransomamtus 0.997182 ransompaidus 0.997300 ransompaid 0.997558 attacktype3 0.997910 attacktype3_txt 0.997910 divert 0.998122 guncertain3 0.998544 gname3 0.998544 claim3 0.998568 gsubname2 0.999108 claimmode3 0.999413 claimmode3_txt 0.999413 weaptype4 0.999530 weaptype4_txt 0.999530 weapsubtype4 0.999577 weapsubtype4_txt 0.999577 gsubname3 0.999953 Length: 109, dtype: float64
misrat.hist(bins=20)
<matplotlib.axes._subplots.AxesSubplot at 0x7f4ee3a2f320>
idx_gtd = temp.dtypes[(temp.dtypes == 'float64') |
(temp.dtypes == 'int64')].index
nums = temp.reindex(idx_gtd, axis=1)
#nums.drop(['eventid'], axis=1, inplace=True)
nums.plot(kind='box', subplots=True, layout=(26,5), figsize=(25, 100), fontsize=20)
iyear AxesSubplot(0.125,0.855645;0.133621x0.0243548) imonth AxesSubplot(0.285345,0.855645;0.133621x0.0243548) iday AxesSubplot(0.44569,0.855645;0.133621x0.0243548) extended AxesSubplot(0.606034,0.855645;0.133621x0.0243548) country AxesSubplot(0.766379,0.855645;0.133621x0.0243548) region AxesSubplot(0.125,0.826419;0.133621x0.0243548) latitude AxesSubplot(0.285345,0.826419;0.133621x0.0243548) longitude AxesSubplot(0.44569,0.826419;0.133621x0.0243548) specificity AxesSubplot(0.606034,0.826419;0.133621x0.0243548) vicinity AxesSubplot(0.766379,0.826419;0.133621x0.0243548) crit1 AxesSubplot(0.125,0.797194;0.133621x0.0243548) crit2 AxesSubplot(0.285345,0.797194;0.133621x0.0243548) crit3 AxesSubplot(0.44569,0.797194;0.133621x0.0243548) doubtterr AxesSubplot(0.606034,0.797194;0.133621x0.0243548) alternative AxesSubplot(0.766379,0.797194;0.133621x0.0243548) multiple AxesSubplot(0.125,0.767968;0.133621x0.0243548) success AxesSubplot(0.285345,0.767968;0.133621x0.0243548) suicide AxesSubplot(0.44569,0.767968;0.133621x0.0243548) attacktype1 AxesSubplot(0.606034,0.767968;0.133621x0.0243548) attacktype2 AxesSubplot(0.766379,0.767968;0.133621x0.0243548) attacktype3 AxesSubplot(0.125,0.738742;0.133621x0.0243548) targtype1 AxesSubplot(0.285345,0.738742;0.133621x0.0243548) targsubtype1 AxesSubplot(0.44569,0.738742;0.133621x0.0243548) natlty1 AxesSubplot(0.606034,0.738742;0.133621x0.0243548) targtype2 AxesSubplot(0.766379,0.738742;0.133621x0.0243548) targsubtype2 AxesSubplot(0.125,0.709516;0.133621x0.0243548) natlty2 AxesSubplot(0.285345,0.709516;0.133621x0.0243548) targtype3 AxesSubplot(0.44569,0.709516;0.133621x0.0243548) targsubtype3 AxesSubplot(0.606034,0.709516;0.133621x0.0243548) natlty3 AxesSubplot(0.766379,0.709516;0.133621x0.0243548) ... weapsubtype2 AxesSubplot(0.285345,0.592613;0.133621x0.0243548) weaptype3 AxesSubplot(0.44569,0.592613;0.133621x0.0243548) weapsubtype3 AxesSubplot(0.606034,0.592613;0.133621x0.0243548) weaptype4 AxesSubplot(0.766379,0.592613;0.133621x0.0243548) weapsubtype4 AxesSubplot(0.125,0.563387;0.133621x0.0243548) nkill AxesSubplot(0.285345,0.563387;0.133621x0.0243548) nkillus AxesSubplot(0.44569,0.563387;0.133621x0.0243548) nkillter AxesSubplot(0.606034,0.563387;0.133621x0.0243548) nwound AxesSubplot(0.766379,0.563387;0.133621x0.0243548) nwoundus AxesSubplot(0.125,0.534161;0.133621x0.0243548) nwoundte AxesSubplot(0.285345,0.534161;0.133621x0.0243548) property AxesSubplot(0.44569,0.534161;0.133621x0.0243548) propextent AxesSubplot(0.606034,0.534161;0.133621x0.0243548) propvalue AxesSubplot(0.766379,0.534161;0.133621x0.0243548) ishostkid AxesSubplot(0.125,0.504935;0.133621x0.0243548) nhostkid AxesSubplot(0.285345,0.504935;0.133621x0.0243548) nhostkidus AxesSubplot(0.44569,0.504935;0.133621x0.0243548) nhours AxesSubplot(0.606034,0.504935;0.133621x0.0243548) ndays AxesSubplot(0.766379,0.504935;0.133621x0.0243548) ransom AxesSubplot(0.125,0.47571;0.133621x0.0243548) ransomamt AxesSubplot(0.285345,0.47571;0.133621x0.0243548) ransomamtus AxesSubplot(0.44569,0.47571;0.133621x0.0243548) ransompaid AxesSubplot(0.606034,0.47571;0.133621x0.0243548) ransompaidus AxesSubplot(0.766379,0.47571;0.133621x0.0243548) hostkidoutcome AxesSubplot(0.125,0.446484;0.133621x0.0243548) nreleased AxesSubplot(0.285345,0.446484;0.133621x0.0243548) INT_LOG AxesSubplot(0.44569,0.446484;0.133621x0.0243548) INT_IDEO AxesSubplot(0.606034,0.446484;0.133621x0.0243548) INT_MISC AxesSubplot(0.766379,0.446484;0.133621x0.0243548) INT_ANY AxesSubplot(0.125,0.417258;0.133621x0.0243548) Length: 76, dtype: object
The dataset is imbalanced by many attributes. The visually recognisable examples:
iyear
region
attacktype
seriestargtype
and targsubtype
seriesclaimmode
weapontype
and weaponsubtype
Because this is a highly imbalanced dataset we use the IsolationForest
model to define outliers.
We drop missing values:
nums.dropna(axis=1, inplace=True, thresh=nums.shape[0] * 0.95)
nums.dropna(inplace=True)
clf = IsolationForest(max_samples='auto', random_state=153, contamination=0.05, verbose=True, n_jobs=-1)
clf.fit(nums)
isof = clf.predict(nums)
[Parallel(n_jobs=2)]: Done 2 out of 2 | elapsed: 1.1s remaining: 0.0s [Parallel(n_jobs=2)]: Done 2 out of 2 | elapsed: 1.1s finished
isofdf = pd.Series(isof)
nums['Outlier'] = isof
nums = nums[nums.Outlier != -1]
nums.drop(columns='Outlier', inplace=True)
nums.plot(kind='box', subplots=True, layout=(26,5), figsize=(25, 100), fontsize=20)
iyear AxesSubplot(0.125,0.855645;0.133621x0.0243548) imonth AxesSubplot(0.285345,0.855645;0.133621x0.0243548) iday AxesSubplot(0.44569,0.855645;0.133621x0.0243548) extended AxesSubplot(0.606034,0.855645;0.133621x0.0243548) country AxesSubplot(0.766379,0.855645;0.133621x0.0243548) region AxesSubplot(0.125,0.826419;0.133621x0.0243548) latitude AxesSubplot(0.285345,0.826419;0.133621x0.0243548) longitude AxesSubplot(0.44569,0.826419;0.133621x0.0243548) specificity AxesSubplot(0.606034,0.826419;0.133621x0.0243548) vicinity AxesSubplot(0.766379,0.826419;0.133621x0.0243548) crit1 AxesSubplot(0.125,0.797194;0.133621x0.0243548) crit2 AxesSubplot(0.285345,0.797194;0.133621x0.0243548) crit3 AxesSubplot(0.44569,0.797194;0.133621x0.0243548) multiple AxesSubplot(0.606034,0.797194;0.133621x0.0243548) success AxesSubplot(0.766379,0.797194;0.133621x0.0243548) suicide AxesSubplot(0.125,0.767968;0.133621x0.0243548) attacktype1 AxesSubplot(0.285345,0.767968;0.133621x0.0243548) targtype1 AxesSubplot(0.44569,0.767968;0.133621x0.0243548) natlty1 AxesSubplot(0.606034,0.767968;0.133621x0.0243548) guncertain1 AxesSubplot(0.766379,0.767968;0.133621x0.0243548) individual AxesSubplot(0.125,0.738742;0.133621x0.0243548) weaptype1 AxesSubplot(0.285345,0.738742;0.133621x0.0243548) ishostkid AxesSubplot(0.44569,0.738742;0.133621x0.0243548) INT_MISC AxesSubplot(0.606034,0.738742;0.133621x0.0243548) dtype: object
From among the recognised transformation possibilities, the following transformations are 'universal' (that is, could be used for the whole dataset regardless of the particular training/test split method):
There are a number of configuration possibilites which we also could use but now we leave untouched:
Finally, in the current process we use the following setting:
gname
)resolution
NaTType attributeWe summarized the following configuration possibilities in a function:
def preproc(data,
primonly=True,
period=(1, 5),
onlyknown=True,
nocat=True,
maxna=0.05,
outrat=0.05,
topincrat=1,
hideind=True,
hideuncert=False,
dropspec=True,
dropres=True,
miscodetonan=True):
"""
Cleans and preprocesses dataset.
Parameters:
===========
`primonly`: boolean, True
Includes only general perpetrator group names and only of the primary perpetrator (`gname`).
`period`: tuple, (1, 5)
Defines the included period by setting the start and end dates:
'1': 1970
'2': 1998
'3': April 1 2008
'4': 2012
`5`: 2016
`onlyknown`: boolean, True
Shows only incidents where the perpetrators is identified (even if with doubt).
`nocat`: boolean, True
Excludes all categorical attributes from the dataset with the exception of the perpetrator group name
attributes (`gname`:`gsubname3`).
`maxna`: float, 0.05
The maximum allowed proportion of missing values within an attribute. Drops all rows with missing values and
keeps only columns with missing value ratio below the given threshold.
For instance, a value of '0.05' means that only columns with less than 5% of missing values are kept in the dataset.
`outrat`: float, 0.05
The contamination ratio determining the percent of values classified as outliers and dropped from the dataset.
The function uses the Isolation Forest model for identifying outliers.
Prerequisities:
* No categorical values (`nocat`)
* No missing values (`maxna`)
Example: '0.05' means that 5% of values will be flagged as an outlier and dropped.
`topincrat`: float, 1
Filters perpetrators based on their overall weight of contribution (in terms of number of incidents).
The parameter's value is the ratio of total incidents for which the selected perpetrators are responsible.
Perpetrators are ranked based on the number of incidents in which they are involved and the function calculates
their cumulative contribution. It then makes the selection at or right above the given threshold.
Example: '0.95' means selecting the perpetator groups with the highest incident ratio responsible together
for 95% of the total incidents.
`hideind`: boolean, True
Hides individual perpetrators unaffiliated to groups.
`hideuncert`: boolean, False
Hides uncertain cases
`dropspec`: boolean, True
Drops special attributes.
`dropres`: boolean, True
Drops the `resolution` NaTType attribute.
`miscodetonan`: boolean, True
Transforms the original codes for missing values into numpy NaN.
"""
procd = data.copy(deep=True)
# `dropspec`: Drop special attributes
if dropspec == True:
procd.drop(['eventid', 'addnotes', 'scite1', 'scite2', 'scite3', 'dbsource'], axis=1, inplace=True)
# `miscodetonan`: Turn built-in missing codes into numpy NaN
if miscodetonan == True:
# The `miscodes` dictionary defines the attribute-missing value code pairs:
miscodes = {"0": ['imonth', 'iday'],
"-9": ['claim2', 'claimed', 'compclaim', 'doubtterr', 'INT_ANY', 'INT_IDEO', 'INT_LOG', 'INT_MISC',
'ishostkid', 'ndays', 'nhostkid', 'nhostkidus', 'nhours', 'nperpcap', 'nperps', 'nreleased',
'property', 'ransom', 'ransomamt', 'ransomamtus', 'ransompaid', 'ransompaidus', 'vicinity'],
"-99": ['ransompaid', 'nperpcap', 'compclaim', 'nreleased', 'nperps', 'nhostkidus', 'ransomamtus',
'ransomamt', 'nhours', 'ndays', 'propvalue', 'ransompaidus', 'nhostkid']}
def mistonan(data, collist, nancode):
"""Replaces columns' missing value code with numpy NaN.
Parameters:
`data`: dataframe
`nanvalue` : the code of the missing value in the columns
"""
colstonan = []
for col in collist:
if col in data.columns:
colstonan.append(col)
else:
print("'{}' is not among the dataframe's columns.".format(col))
data[colstonan] = data[colstonan].apply(lambda x: x.replace(nancode, np.NaN))
for key in miscodes.keys():
mistonan(procd, miscodes[key], float(key))
# Replaces "Unknown" values whenever it occurs except in the `gname` target attribute.
# The function controls it by the `onlyknown` parameter.
procd.drop(columns='gname').replace(to_replace='Unknown', value=np.NaN, inplace=True)
# Replacing missing values
## Replace unknown, '0' days and months with a random value
### Months
#tm = procd.imonth[procd.imonth == 0]
#tm = tm.apply(lambda x: np.random.randint(1, 13))
#procd.imonth[procd.imonth == 0] = tm
### Days
#td = procd.iday[procd.iday == 0]
#td = td.apply(lambda x: np.random. randint(1, 29))
#procd.iday[procd.iday == 0] = td
# `period`: Filter the dataset for the choosen time period
dates = [1970, 1997, 2008, 2012, 2016]
predmin = dates[period[0]-1]
predmax = dates[period[1]-1]
if predmin == 2008:
procd = procd[((procd.iyear > predmin ) & (procd.iyear < predmax + 1)) |
((procd.iyear == predmin) & (procd.imonth >= 3))]
elif predmax == 2008:
procd = procd[((procd.iyear >= predmin ) & (procd.iyear < predmax)) |
((procd.iyear == predmax) & (procd.imonth < 3))]
else:
procd = procd[(procd.iyear >= predmin) & (procd.iyear < predmax + 1)]
# `onlyknown`: Show only known perpetrators
if onlyknown == True:
procd = procd[procd.gname != 'Unknown']
# `hideind`: Hide unaffiliated individuals
if hideind == True:
procd = procd[procd.individual != 1]
# `primonly`: Include only the primary perpetrator groups and only their main names.
if primonly == True:
procd.drop(columns=['gsubname','gname2','gsubname2','gname3','gsubname3'], axis=1, inplace=True)
# `topincrat`: Set the threshold for the top frequent perpetrators to show.
tempname = procd.gname
idx_main_groups = tempname.value_counts()[tempname.value_counts(normalize=True).cumsum() <= topincrat].index
procd = procd[procd.gname.isin(idx_main_groups)]
# `hideuncert`: Hide uncertain cases
if hideuncert == True:
procd = procd[(procd.guncertain1 != 1) |
(procd.guncertain2 != 1) |
(procd.guncertain3 != 1)]
# `nocat`: Dropping polynomial attributes (except `gname`)
if nocat == True:
idx_nonobj = procd.dtypes[(procd.dtypes.index.isin(['gname',
'gsubname',
'gname2',
'gsubname2',
'gname3',
'gsubname3'])) |
(procd.dtypes != 'object')].index
procd = procd.reindex(idx_nonobj, axis=1)
# `dropres`: Drop the `resolution` attribute
if dropres == True:
procd.drop(columns='resolution', inplace=True)
#print(procd)
# `maxna`: Drop missing values and columns with missing values above the threshold.
procd.dropna(axis=1, inplace=True, thresh=procd.shape[0] * (1 - procd.drop(['eventid', 'addnotes', 'scite1', 'scite2', 'scite3', 'dbsource'], axis=1, inplace=True)))
procd.dropna(inplace=True)
# `outrat`: Drop outliers
clf = IsolationForest(max_samples='auto', random_state=2425, contamination=outrat, verbose=True, n_jobs=-1)
clf.fit(procd.drop(columns='gname'))
isof = clf.predict(procd.drop(columns='gname'))
isofdf = pd.Series(isof)
procd['Outlier'] = isof
procd = procd[procd.Outlier != -1]
procd.drop(columns='Outlier', inplace=True)
# Drop correlated values
#procd = dropcors(procd, corthr)
print(procd.info(verbose=True))
return procd
Finally we are going to train the model on the following way:
resolution
moddat = gtd.copy(deep=True)
moddat = preproc(moddat,
primonly=True,
period=(1, 5),
onlyknown=True,
nocat=True,
maxna=0.05,
outrat=0.05,
topincrat=1,
hideind=True,
hideuncert=False,
dropspec=True,
dropres=True,
miscodetonan=True)
[Parallel(n_jobs=2)]: Done 2 out of 2 | elapsed: 0.6s remaining: 0.0s [Parallel(n_jobs=2)]: Done 2 out of 2 | elapsed: 0.6s finished
<class 'pandas.core.frame.DataFrame'> Int64Index: 19012 entries, 111973 to 69569 Data columns (total 27 columns): iyear 19012 non-null int64 imonth 19012 non-null float64 iday 19012 non-null float64 extended 19012 non-null int64 country 19012 non-null int64 region 19012 non-null int64 latitude 19012 non-null float64 longitude 19012 non-null float64 specificity 19012 non-null float64 vicinity 19012 non-null float64 crit1 19012 non-null int64 crit2 19012 non-null int64 crit3 19012 non-null int64 doubtterr 19012 non-null float64 multiple 19012 non-null int64 success 19012 non-null int64 suicide 19012 non-null int64 attacktype1 19012 non-null int64 targtype1 19012 non-null int64 targsubtype1 19012 non-null float64 natlty1 19012 non-null float64 gname 19012 non-null object guncertain1 19012 non-null float64 individual 19012 non-null int64 weaptype1 19012 non-null int64 ishostkid 19012 non-null float64 INT_MISC 19012 non-null float64 dtypes: float64(12), int64(14), object(1) memory usage: 4.1+ MB None
We could not automate the selection of correlated attributes, therefore, we do it them semi-manually:
def cors(data, threshold=0.5, sort=False):
"""Lists correlation pairs and their correlation values above a correlation threshold.
`data`: DataFrame
`threshold`: The correlation value above which it shows the correlation pairs.
`paired`: True
Organizes the correlation pairs according to attributes.
If False, it shows the correlation pairs values in the descending order.
"""
corrs = data.corr()
cri_hi = abs(corrs < 1) & abs(corrs >= threshold)
corr_hi = corrs[cri_hi].stack().reset_index()
corr_hi.columns = ['first', 'second', 'corr']
if sort == True:
output = corr_hi.sort_values(by='corr', ascending=False)
else:
output = corrs[cri_hi].stack()
return output
corpair = cors(moddat, 0.7, sort=True)
corpair
first | second | corr | |
---|---|---|---|
0 | targtype1 | targsubtype1 | 0.988035 |
1 | targsubtype1 | targtype1 | 0.988035 |
moddat.drop(columns=['extended', 'targsubtype1', 'INT_LOG', 'INT_IDEO'], inplace=True, errors='ignore')
X = moddat.drop(['gname'], axis=1).dropna(axis=1)
X.dropna(axis=1, inplace=True)
print(X.shape)
(19012, 24)
Normalizing the dataset:
scaler = Normalizer().fit(X)
X = scaler.transform(X)
y = moddat.gname
y.dropna(inplace=True)
y.fillna("NaN", inplace=True)
y.shape
(19012,)
validation_size = 0.2
seed = 17
X_train, X_validation, y_train, y_validation = train_test_split(X, y, test_size=validation_size, random_state=seed)
print(X_train.shape)
print(X_validation.shape)
print(y_train.shape)
print(y_validation.shape)
(15209, 24) (3803, 24) (15209,) (3803,)
results = []
names = []
kfold = KFold(n_splits=10, random_state=seed)
Finally I decided to focus only on Decision Tree and K-NN classifiers for learning and performance reasons:
models = {"Decisiong Tree Classifier": DecisionTreeClassifier(),
"K-Neighbors Classifier": KNeighborsClassifier()}
# "Linear Discriminant Analysis": LinearDiscriminantAnalysis()
# "Logistic Regression": LogisticRegression()
def predict_groups(models, X_train, y_train):
for model in models:
#print("\n{}:\n\n{}\n".format(model, models[model]))
model_score = cross_val_score(models[model], X_train, y_train, cv=kfold, scoring='accuracy')
print("\n{}:\n\tAccuracy: {} ({})".format(model, model_score.mean(), model_score.std()))
model_score = cross_val_score(models[model], X_train, y_train, cv=kfold, scoring='f1_micro')
print("\tF1 micro: {} ({})".format(model_score.mean(), model_score.std()))
#model_score = cross_val_score(models[model], X_train, y_train, cv=kfold, scoring='f1_weighted')
#print("\tF1 weighted: {} ({})".format(model_score.mean(), model_score.std()))
#model_score = cross_val_score(models[model], X_train, y_train, cv=kfold, scoring='roc_auc')
#print("\tF1 ROC: {} ({})".format(model_score.mean(), model_score.std()))
#crosval = cross_validate(model, X, y, scoring=['accuracy', 'precision_micro', 'recall_micro', 'f1_micro'])
predict_groups(models, X_train, y_train)
Decisiong Tree Classifier: Accuracy: 0.6842010104155853 (0.011646355162064125) F1 micro: 0.6832146873594243 (0.011765139215253092) K-Neighbors Classifier: Accuracy: 0.5987244368317244 (0.011534675657358849) F1 micro: 0.5987244368317244 (0.011534675657358849)
The two models produce prediction accuracies of around 68% and 60% respectively with minimal standard deviations. The F1 scores are almost the same (which is normal for multiclass classification problems).
While this is not necessarily bad, it is tested only on a selected dataset and therefore should be developed further.