Introduction To eSports and Counter-Strike
eSports is a synonym for competitive gaming. Competitive gaming has always been an important part of video game culture but is just now gaining recognition as a legitimate professional mainstream sport.
The earliest recorded official video game competition took place in 1972 at Stanford University featuring the highly acclaimed multiplayer arcade game, Spacewar. The event gained the attention of the video game industry despite being small and limited only to Stanford students. Video game hardware and software companies saw income potential and started hosting competitions of their own.
Fast forwarding to today, eSports generated $696 million in revenue in 2017 alone, and its yearly revenue is expected to grow to $1.4 billion in 2020. Revenue is generated through tournament audience admission, game developer sponsorships, tournament entry fees, and advertising contracts. In 2017, there were 191 million frequent viewers and 194 million occasional/one-time viewers. Maximum concurrent viewership has exceeded 2.3 million. Competitive gaming is particularly popular in North America, China, Sweden, and South Korea. Tournaments are viewable via Twitch, ESPN, and other online streaming platforms.
This tutorial focuses on analyzing statistics of Counter-Strike Global Offensive, a popular competitive first-person shooter video game. We will perform data analysis on important first-person shooter statistics such as match wins/losses, kill/death ratios, and kill differentials of various players. These statistics can be used to help construct teams with a high likelihood of winning with a relatively low operation cost. Statistical based team optimization has been utilized by sports team management professionals for quite some time. In 2002, the Oakland Athletics made the first noteworthy use of these techniques. Increased public interest in the subject was generated through the
2003 release of the book Moneyball and its
2011 film adaption.
All data will scraped and parsed from
HLTV, one the gaming community’s most regarded media groups.
Preparation
This tutorial utilizes Jupyter, an interactive computing notebook environment for Python. Jupyter provides a rich GUI with input and output history, making it ideal for data science related tasks. Jupyter is most easily accessed through a portable cross-platform computing environment called Docker. Docker includes all tools needed to perform the tasks described in this tutorial including a data analysis toolkit called Pandas, an HTML parser called Beautiful Soup, and a machine learning library called SKLearn.
Instructions for installing Docker can be found here.
Once installed, Docker will create a shortcut to an application called Anaconda on your desktop. Launch Anaconda and open Jupyter through the application manager. Click File > New Notebook > Python 3 to create a new workspace.
Import all necessary libraries into your newly created workbook
import math
import re as re
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from bs4 import BeautifulSoup as bs
from sklearn import svm
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import learning_curve
Data Scraping and Sanitization
We will scrape team information from an HTML table, put it into a Pandas data frame, and sanitize it. A data frame is a Pandas construct that holds structured information for easy querying and analysis of information.
Python has built-in networking functionality that allows us to send HTTP requests and retrieve raw HTML data back in the form of a string. An HTML parser called Beautiful Soup will take the raw HTML data and convert the table on the given page to a format readable by Pandas. The data then gets passed into the Pandas read_html function and a new data frame is generated. Data frame previews in this tutorial will be limited to 15 rows for the sake of brevity.
Everything works properly except for the extraction of country names. HLTV's table presents country data in the form of a flag image instead of plain text. Fortunately,
regex allows us to pull the text of an HTML image tag’s alternate property which contains plaintext country names.
We will perform a preliminary sanitation by removing Tyloo since they play exclusively with low performing teams. This anomaly causes massive data skews and would ruin the integrity of some of our data analyses.
# Get the data
r = requests.get("https://www.hltv.org/stats/teams")
# Create a pandas with pulled data
root = bs(r.content, "html.parser")
root.prettify()
table = (str)(root.find("table"))
teams = pd.read_html(table, header=0)[0]
# Pull the country data out of the table and put into our dataframe
table = table.strip("\\n")
countries = (re.findall('title="(.+)"/>', table))
teams['Country'] = pd.Series(countries)
# Clean up columns
teams.columns = ['Team', 'Maps', 'K/D Differential', 'K/D', 'Rating', 'Country']
teams = teams[['Team', 'Country', 'Maps', 'K/D Differential', 'K/D', 'Rating']]
# Delete Tyloo from dataframe for reasons mentioned above
teams = teams.drop([0])
# Get a series with all team names
teamlist = teams[teams.columns[0]]
# Display resultant panda
teams
|
Team |
Country |
Maps |
K/D Differential |
K/D |
Rating |
| 1 |
Space Soldiers |
Turkey |
623 |
5117 |
1.10 |
1.06 |
| 2 |
FaZe |
Europe |
451 |
2302 |
1.06 |
1.06 |
| 3 |
NiP |
Sweden |
1232 |
11763 |
1.12 |
1.06 |
| 4 |
Kinguin |
Poland |
551 |
3392 |
1.07 |
1.05 |
| 5 |
Astralis |
Denmark |
449 |
3504 |
1.09 |
1.05 |
| 6 |
Luminosity |
Brazil |
573 |
3828 |
1.08 |
1.05 |
| 7 |
G2 |
France |
657 |
1789 |
1.03 |
1.05 |
| 8 |
Titan |
France |
484 |
1269 |
1.03 |
1.04 |
| 9 |
TSM |
Denmark |
504 |
2863 |
1.07 |
1.04 |
| 10 |
OpTic |
North America |
467 |
2126 |
1.05 |
1.04 |
| 11 |
Liquid |
United States |
705 |
3398 |
1.06 |
1.04 |
| 12 |
Virtus.pro |
Poland |
1077 |
3677 |
1.04 |
1.04 |
| 13 |
HellRaisers |
CIS |
985 |
3329 |
1.04 |
1.04 |
| 14 |
EnVyUs |
France |
828 |
3282 |
1.05 |
1.04 |
| 15 |
fnatic |
Sweden |
1075 |
7897 |
1.09 |
1.04 |
Repeat this process on HLTV’s player list. Players that are not in the list of HLTV's top 10 ranked teams can be discarded since they are not needed for our purposes. We will sort the data frame by team name so players from the same team are grouped together.
# Get the data
r = requests.get("https://www.hltv.org/stats/players")
# Create a pandas with pulled data
root = bs(r.content, "html.parser")
root.prettify()
# Pull the player data out of the table and put into our dataframe
table = (str)(root.find("table"))
players = pd.read_html(table, header=0)[0]
# An array containing a list of top 10 teams (according to HLTV's critically acclaimed ranking system)
topTeams = ['SK', 'FaZe', 'Astralis', 'NiP', 'Cloud9', 'G2', 'North', 'Virtus.pro', 'fnatic', 'Liquid']
# Discard player information for anyone not listed in the top 10 teams
players = players[players['Team'].isin(topTeams)]
players = players.sort_values('Team')
players.columns = ['Player', 'Team', 'Maps', 'K/D Differential', 'K/D', 'Rating']
# Display resultant panda
players
|
Player |
Team |
Maps |
K/D Differential |
K/D |
Rating |
| 129 |
Kjaerbye |
Astralis |
893 |
1686 |
1.11 |
1.07 |
| 19 |
device |
Astralis |
1012 |
3942 |
1.24 |
1.16 |
| 293 |
gla1ve |
Astralis |
855 |
333 |
1.02 |
1.00 |
| 227 |
Xyp9x |
Astralis |
1189 |
1528 |
1.08 |
1.03 |
| 72 |
dupreeh |
Astralis |
1132 |
2756 |
1.14 |
1.09 |
| 116 |
Stewie2K |
Cloud9 |
629 |
943 |
1.08 |
1.07 |
| 177 |
RUSH |
Cloud9 |
682 |
882 |
1.08 |
1.04 |
| 119 |
tarik |
Cloud9 |
759 |
1296 |
1.10 |
1.07 |
| 113 |
Skadoodle |
Cloud9 |
836 |
2310 |
1.18 |
1.08 |
| 104 |
autimatic |
Cloud9 |
658 |
1324 |
1.11 |
1.08 |
| 12 |
NiKo |
FaZe |
740 |
2628 |
1.20 |
1.17 |
| 27 |
GuardiaN |
FaZe |
1060 |
3998 |
1.23 |
1.15 |
| 409 |
karrigan |
FaZe |
1127 |
-891 |
0.96 |
0.95 |
| 50 |
olofmeister |
FaZe |
1015 |
2921 |
1.17 |
1.11 |
| 103 |
rain |
FaZe |
765 |
1227 |
1.09 |
1.08 |
Getting the match data for the top 10 ranked teams is a bit more complicated. We need to store all ten of the team names and URLs into arrays. A for loop allows us to iterate through and parse the HTML data from all URLs, storing it into one unified data frame.
i = 0
masterDF = pd.DataFrame()
# Top teams
topTeamCountries = ['Brazil', 'EU', 'Denmark', 'Sweden', 'North America', 'France', 'Denmark', 'Russia', 'Sweden', 'North America']
# Top team URLS (for scraping match data)
topTeamMatches = ['https://www.hltv.org/stats/teams/matches/6137/SK', 'https://www.hltv.org/stats/teams/matches/6667/FaZe', \
'https://www.hltv.org/stats/teams/matches/6665/Astralis', 'https://www.hltv.org/stats/teams/matches/4411/NiP', \
'https://www.hltv.org/stats/teams/matches/5752/Cloud9', 'https://www.hltv.org/stats/teams/matches/5995/G2', \
'https://www.hltv.org/stats/teams/matches/7533/North?startDate=2017-01-01&endDate=2017-12-31', 'https://www.hltv.org/stats/teams/matches/5378/Virtus.pro', \
'https://www.hltv.org/stats/teams/matches/4991/fnatic', 'https://www.hltv.org/stats/teams/matches/5973/Liquid']
# Go through each of the top 10 teams match data, sanitize/clean it, and throw the match data into one unified master dataframe
for team in topTeamMatches :
# Pull/scrape/parse data
r = requests.get(team)
root = bs(r.content, "html.parser")
root.prettify()
table = (str)(root.find("table"))
SK = pd.read_html(table, header=0)[0]
# Give columns proper names
SK.columns = ['Date', 'Event', 'Opponent', 'Map', 'Rating', 'W/L', 'Outcome']
# Clean win/loss data and turn to numeric format for compatibilty with machine learning classifier
SK.loc[SK.Outcome == 'W', 'Outcome'] = 1
SK.loc[SK.Outcome == 'L', 'Outcome'] = 0
SK.loc[SK.Outcome == 'T', 'Outcome'] = 0
# Misc sanitation (datetime conversion, typecasting)
SK['Outcome'] = SK['Outcome'].astype(str).astype(int)
SK['Event'] = SK['Event'].astype(str)
SK['Date'] = pd.to_datetime(SK['Date'])
SK['Year'] = SK['Date'].dt.year
SK['Team'] = topTeams[i]
masterDF = masterDF.append(SK)
i += 1
# Reset indexes so that they are from 0 to N instead of being random jumbled numbers
masterDF = masterDF.reset_index(drop=True)
# Drop unneeded colums and clean up names of other columns
masterDF = masterDF.drop(['Event', 'Opponent'], axis=1)
masterDF = masterDF.rename(index=str, columns={"Map" : "Opponent", "Rating" : "Map"})
cols = masterDF.columns.tolist()
cols = cols[-1:] + cols[:-1]
masterDF = masterDF[cols]
# Print resultant dataframe of all merged together top 10 team historic match results
masterDF
|
Team |
Date |
Opponent |
Map |
W/L |
Outcome |
Year |
| 0 |
SK |
2018-01-19 |
Space Soldiers |
Mirage |
16 - 13 |
1 |
2018 |
| 1 |
SK |
2017-10-12 |
FaZe |
Train |
19 - 16 |
1 |
2017 |
| 2 |
SK |
2017-10-12 |
FaZe |
Mirage |
16 - 9 |
1 |
2017 |
| 3 |
SK |
2017-10-12 |
FaZe |
Overpass |
16 - 11 |
1 |
2017 |
| 4 |
SK |
2017-10-12 |
FaZe |
Inferno |
13 - 16 |
0 |
2017 |
| 5 |
SK |
2017-09-12 |
Misfits |
Overpass |
16 - 1 |
1 |
2017 |
| 6 |
SK |
2017-09-12 |
Misfits |
Cobblestone |
16 - 6 |
1 |
2017 |
| 7 |
SK |
2017-06-12 |
Misfits |
Cache |
16 - 7 |
1 |
2017 |
| 8 |
SK |
2017-06-12 |
NRG |
Cache |
16 - 9 |
1 |
2017 |
| 9 |
SK |
2017-06-12 |
NiP |
Overpass |
16 - 9 |
1 |
2017 |
| 10 |
SK |
2017-05-12 |
OpTic |
Mirage |
14 - 16 |
0 |
2017 |
| 11 |
SK |
2017-05-12 |
North |
Overpass |
16 - 7 |
1 |
2017 |
| 12 |
SK |
2017-11-25 |
Astralis |
Cache |
19 - 16 |
1 |
2017 |
| 13 |
SK |
2017-11-25 |
Astralis |
Inferno |
16 - 8 |
1 |
2017 |
| 14 |
SK |
2017-11-25 |
Astralis |
Mirage |
11 - 16 |
0 |
2017 |
Visualization
Visualization is an invaluable tool in the world of data science. It helps people understand data trends more efficiently than sifting through raw numbers. Pandas and pyplot make graphical data representation very easy.
# Compute standardized Kill/Death ratio for every country using groupby clauses
g = teams.groupby("Country")
kd = g.mean()['K/D']
avg = kd.mean()
std = kd.std()
normalized_kd = []
for c in kd:
normalized_kd.append((c-avg)/std)
ts = teams['Country'].sort_values().unique()
temp = pd.DataFrame({'teams': ts, 'kd': normalized_kd})
print("Average kill/death ratio of teams given: ", avg)
# Create scatter plot of team win ratio (by country)
ax = temp.plot(kind='bar',x='teams',y='kd', title='Scatter Plot of Team Win Ratio Color Coded (By Country)', width = 1)
ax.set_xlabel('Country')
ax.set_ylabel('Standardized Kill/Death Ratio')
plt.show()
Average kill/death ratio of teams given: 1.045059523809524
This plot shows us that Russia has currently been struggling while countries such as Turkey and Sweden have been prospering. The average kill/death ratio for the teams analyzed above exceeds 1 since we are only processing data for the upper echelon of teams from their countries. More precisely, countries without teams in the upper 30% of kill/death ratios are not represented.
Next is a visual representation of the top 10 team win rates.
# Get Outcome groups for Team, Year
chanz = masterDF.groupby(['Team','Year'])['Outcome'].mean()
cha = chanz.to_frame()
cha['o'] = chanz.index.get_level_values('Team')
cha['f'] = chanz.index.get_level_values('Year')
cha = cha.reset_index(drop=True)
cha = cha.rename(columns={'Outcome': 'Outcome', 'o': 'Team', 'f': 'Year'})
# Group by team and get unique
ts = cha.groupby('Team')
names = ts['Team'].unique()
flat_names = [item for sublist in names for item in sublist]
# Format plot
plt.figure(figsize=(20,10))
plt.xlim(2013,2017)
plt.ylim(.4, .82)
plt.title("Winrate per year of top 10 teams")
plt.ylabel('Winrate')
plt.xlabel('Year')
# Plot for each team
for k,v in ts:
plt.plot(v['Year'], v['Outcome'],marker='o')
plt.legend(flat_names, ncol=5, prop={'size': 20})
# Show aformentioned plot
plt.show()
# Print out significant data about NiP
wrs = ts.get_group('NiP')['Outcome'].tolist()
years = ['2012','2013','2014','2015','2016','2017']
for i in range(0,6):
print("NiP winrate in", years[i], ": ", wrs[i])
NiP win rate in 2012: 0.9843750000000000
NiP win rate in 2013: 0.8053435114503816
NiP win rate in 2014: 0.6026785714285714
NiP win rate in 2015: 0.6028368794326241
NiP win rate in 2016: 0.6206896551724138
NiP win rate in 2017: 0.5228426395939086
The most significant conclusion that can be made at first glance is that NiP's performance dropped dramatically after their 2012 legendary record of 87 wins to 0 losses. NiP has been in decline ever since. It is also clear that SK's performance during 2015 and every subsequent year has been phenomenal and continues to improve. The significant shifts in performance of these two teams are best explained by major team roster changes.
A player’s kill/death differential is calculated by subtracting kills from deaths. It documents the quantity of kills and deaths whereas the kill/death ratio only provides a success rate. As in other sports, Counter-Strike teams often include star players who consistently make high contributions to their team. Having a graphical representation of a team’s mean kill/death differential and the team’s top player’s kill/death differential makes for quick comparisons.
Means and maxes of the kill/death differential for each team can be obtained by using Pandas group by clauses. Mean and maximum kill/death differentials are printed above the graph for clarity.
means = []
maxs = []
teams = players['Team'].sort_values().unique()
stuff = players.groupby('Team')
# Get the means and maxes from the groups and format the columns
means = stuff.mean()
maxs = stuff.max()
means.columns = ['Maps', 'K/D Diff', 'K/D', 'Rating']
maxs.columns = ['Player', 'Maps', 'K/D Diff', 'K/D', 'Rating']
# Print the data I got
print("Means of K/D Difference per team\n", means['K/D Diff'])
print("\nMaxs of K/D Difference per team\n", maxs['K/D Diff'])
# Plot the bar graph with means as blue and maxs as green
ax = plt.subplot(111)
ax.bar([.8,1.8,2.8,3.8,4.8,5.8,6.8,7.8,8.8,9.8], means['K/D Diff'], width=0.3,color='b',align='center')
ax.bar([1.2,2.2,3.2,4.2,5.2,6.2,7.2,8.2,9.2,10.2], maxs['K/D Diff'],width=0.3,color='g',align='center')
plt.xticks([1,2,3,4,5,6,7,8,9,10], teams, rotation='vertical')
# Graph formatting
ax.set_ylim([0, 5000])
ax.legend(['Mean', 'Max'])
plt.title("Average K/D Differential vs max K/D Differential per team")
plt.ylabel('K/D Differential')
plt.xlabel('Team')
plt.show()
me = means['K/D Diff'].tolist()
ma = maxs['K/D Diff'].tolist()
# Print the differences in the star player vs mean players
print("How much better each teams best player did versus average of players on the team")
for i in range(len(me)):
c = (me[i]/ma[i])*100
print(teams[i], ": %.2f" %c, "%")
Means of K/D Difference per team
Astralis 2049.0
Cloud9 1351.0
FaZe 1976.6
G2 2202.4
Liquid 830.6
NiP 2667.4
North 700.2
SK 1910.6
Virtus.pro 1165.8
fnatic 1592.6
Maximums of K/D Difference per team
Astralis 3942
Cloud9 2310
FaZe 3998
G2 4871
Liquid 1533
NiP 5009
North 2616
SK 3711
Virtus.pro 2191
fnatic 2987
How much better the star player(s) did versus average of players on the team
Astralis 51.98%
Cloud9 58.48%
FaZe 49.44%
G2 45.21%
Liquid 54.18%
NiP 53.25%
North 26.77%
SK 51.48%
Virtus.pro 53.21%
fnatic 53.32%
Star players are undoubtedly the most skilled players on their team but sometimes not by a large margin. Teams intentionally give their star players the best opportunities which causes them to increasingly surpass the measurable performance of their teammates. It is easy to recognize that North is a team in which 3 to 4 players perform well, and no real star player exists.
Machine Learning
Machine learning enables us to predict future match performance. We will analyze high performing teams in clusters grouped by country. The linear support vector learning classifier is very powerful and will lend itself well to this task. Since SVMs only accept numerical data input, match dates need to be converted into
ordinal format before being passed into SKLearn.
SKLearn will handle all the heavy lifting. We will give SKLearn the training data, perform a 10-point cross validation, and plot the learning curve of our classifier below. The cross mean and standard error we generate demonstrates how effectively the machine learning classifier is operating.
# Convert datetime data to ordinal format so it can be used in our machine learning classisfier
masterDF['Date'] = masterDF['Date'].apply(lambda x: x.toordinal())
# A function that plots a learning curve of a given machine learning classifier (provided by SKLearn documentation)
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
plt.figure()
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel("Training examples")
plt.ylabel("Score")
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o-', color="r",
label="Training score")
plt.legend(loc="best")
return plt
# Lump groups of countries together for machine learning purposes
europe = masterDF[(masterDF.Team == 'Astralis') | (masterDF.Team == 'North') | (masterDF.Team == 'G2') | (masterDF.Team == 'FaZe')]
america = masterDF[(masterDF.Team == 'C9') | (masterDF.Team == 'Liquid') | (masterDF.Team == 'SK')]
sweden = masterDF[(masterDF.Team == 'NiP') | (masterDF.Team == 'fnatic')]
# Create SKlearn machine learning classifier
eursvc = svm.SVC()
amersvc = svm.SVC()
swedsvc = svm.SVC()
# Give the machine learning classifiers the training data
eursvc.fit(europe.Date.values.reshape((len(europe),1)), europe.Outcome)
amersvc.fit(america.Date.values.reshape((len(america),1)), america.Outcome)
swedsvc.fit(sweden.Date.values.reshape((len(sweden),1)), sweden.Outcome)
# Perform a cross validation analysis on each of the three country group classifiers
europesvm = cross_val_score(eursvc, europe.Date.values.reshape((len(europe),1)), europe.Outcome, cv=10)
americasvm = cross_val_score(amersvc, america.Date.values.reshape((len(america),1)), america.Outcome, cv=10)
swedensvm = cross_val_score(swedsvc, sweden.Date.values.reshape((len(sweden),1)), sweden.Outcome, cv=10)
# Perform a 10 point cross validation on Europe group
print ("Linear SVM 10 Point Cross Validation On Europe Data")
print(europesvm)
print ("\n")
# Perform a 10 point cross validation on America group
print ("Linear SVM 10 Point Cross Validation On America Data")
print(americasvm)
print ("\n")
# Perform a 10 point cross validation on Sweden group
print ("Linear SVM 10 Point Cross Validation On Sweden Data")
print(swedensvm)
print("\n")
# Cross mean and standard error stats for each country under SVM classifier
print ("Linear SVM Cross Mean and Standard Error For Europe Data")
print("Accuracy: %0.2f (+/- %0.2f)" % (europesvm.mean(), europesvm.std() * 2))
print("\n")
print ("Linear SVM Cross Mean and Standard Error For America Data")
print("Accuracy: %0.2f (+/- %0.2f)" % (americasvm.mean(), americasvm.std() * 2))
print("\n")
print ("Linear SVM Cross Mean and Standard Error For Sweden Data")
print("Accuracy: %0.2f (+/- %0.2f)" % (swedensvm.mean(), swedensvm.std() * 2))
# Plot the learning curves for each of the three country groups
plot_learning_curve(eursvc, "Europe", europe.Date.values.reshape((len(europe),1)), europe.Outcome, ylim=(0.7, 1.01), cv=10, n_jobs=4)
plot_learning_curve(amersvc, "America", america.Date.values.reshape((len(america),1)), america.Outcome, ylim=(0.7, 1.01), cv=10, n_jobs=4)
plot_learning_curve(swedsvc, "Sweden", sweden.Date.values.reshape((len(sweden),1)), sweden.Outcome, ylim=(0.7, 1.01), cv=10, n_jobs=4)
# Show the learning curves
plt.show()
Linear SVM 10 Point Cross Validation On European Data
[0.49714286, 0.52, 0.42857143, 0.48, 0.44, 0.40804598,
0.45977011, 0.49132948, 0.40462428, 0.54913295]
Linear SVM 10 Point Cross Validation On American Data
[0.46308725, 0.34459459, 0.27027027, 0.27027027, 0.30612245, 0.4829932
0.49659864, 0.50340136, 0.5170068, 0.49659864]
Linear SVM 10 Point Cross Validation On Swedish Data
[0.42672414, 0.51948052, 0.41125541, 0.23809524, 0.59307359, 0.58008658
0.53913043, 0.52608696, 0.53913043, 0.53043478]
Linear SVM Cross Mean and Standard Error For Europe Data
Accuracy: 0.47 (+/- 0.09)
Linear SVM Cross Mean and Standard Error For America Data
Accuracy: 0.42 (+/- 0.20)
Linear SVM Cross Mean and Standard Error For Sweden Data
Accuracy: 0.49 (+/- 0.20)
Since machine learning works best when provided with large quantities of data, we will cluster together all-star teams from Europe, America, and Sweden. Europe's top teams are Astralis, North, G2, and FaZe. America's top teams are C9, Liquid, and SK. Sweden's top teams are NiP and fnatic.
Our classifiers can predict win/loss ratios for the next 250 games that each country cluster plays. Unsurprisingly, Sweden is projected to continue to dominate the sport.
# Initialize win count variables for each predicition
ewincount = 0
awincount = 0
swincount = 0
# Calculate the each country group amount of wins for the next 250 games
for x in range(735621, 735871):
if eursvc.predict([[x]]) == [1] :
ewincount += 1
for x in range(735621, 735871):
if amersvc.predict([[x]]) == [1] :
awincount += 1
print (awincount)
for x in range(735621, 735871):
if swedsvc.predict([[x]]) == [1] :
swincount += 1
# Output Europe group predicition results
print("Predicted Outcome of Next 250 Games By Best European Teams (Astralis, North, G2, and FaZe)")
plt.pie([ewincount, 250-ewincount], explode=(0, 0), labels=['Wins', 'Losses'], colors=['limegreen', 'red'],
autopct='%1.1f%%', shadow=False, startangle=140)
plt.axis('equal')
plt.show()
# Output America group predicition results
print("Predicted Outcome of Next 250 Games By Best American Teams (C9, Liquid, and SK)")
plt.pie([awincount, 250-awincount], explode=(0, 0), labels=['Wins', 'Losses'], colors=['limegreen', 'red'],
autopct='%1.1f%%', shadow=False, startangle=140)
plt.axis('equal')
plt.show()
# Output Sweden group predicition results
print("Predicted Outcome of Next 250 Games By Best Swedish Teams (NiP and fnatic)")
plt.pie([swincount, 250-swincount], explode=(0, 0), labels=['Wins', 'Losses'], colors=['limegreen', 'red'],
autopct='%1.1f%%', shadow=False, startangle=140)
plt.axis('equal')
plt.show()
Predicted Outcome of Next 250 Games By Best European Teams (Astralis, North, G2, and FaZe)
Predicted Outcome of Next 250 Games By Best American Teams (C9, Liquid, and SK)
Predicted Outcome of Next 250 Games By Best Swedish Teams (NiP and fnatic)
That's it! I hope you enjoyed following my introductory tutorial on data scraping, data sanitization, data visualization, and machine learning. A pre-made Jupyter notebook version of this tutorial can be found here. Happy scraping!
Created by Jeffrey Rosen


测试demo：
vnode
array
event
computed
watch
lifecircle
component
directive
ref
slots
scopedSlot
transition
demo


--- layout: page title: About header-img: "img/about-bg.png" ---
Bold and ambitious, I usually dive headfirst into even the most challenging situations. Uncomplicated and direct in my approach, I often get frustrated by exhaustive details and unnecessary nuances. I lead with blind optimism, barreling through life with an electric "joie de vivre" that perfectly complements my distinctive impulsivity.


| None |
|
This command returns the X location of the mouse on the screen. This position is always from the range 0 to GraphicsWidth( ) - 1. You can use this command in combination with DrawImage to make a custom mouse pointer, or to control something on the screen directly with the mouse.
See also: MouseY, MouseZ. |
|
Graphics 640,480
SetBuffer BackBuffer() Repeat Cls Text 320,0,"Click to reset mouse",True Text 0,0,"Mouse X:"+MouseX() Text 0,10,"Mouse Y:"+MouseY() If MouseDown(1) Or MouseDown(2) Then MoveMouse 320,240 Text MouseX(),MouseY(),"X",True,True Flip Until KeyHit(1) End |


The MarkInChI could not be converted into a list of InChI. Only a small subset of the MarkInChI syntax is currently included in this program


| filehandle = the variable returned by the Readfile, WriteFile or OpenFile when the file was opened. The value returned is the offset from the start of the file. ( 0 = Start of the file ) |
|
This command allows the position in a file to be changed. This allows random access to data within files and can be used with files opened by ReadFile, WriteFile and OpenFile. Note, the offset is the number of bytes from the start of the file, where the first byte is at offset 0. It is important to take account of the size of the data elements in your file.
For instance Integers are 4 bytes long so the first integer in the file is at offset 0 and the second at offset 4. If you write Custom Data types out then you must work out haw many bytes each takes so that you can move about the file correctly. Seeking beyond the end of a file does not generate an error but the data is not read or written to the file, and may course unknown side effects. By using FilePos and SeekFile the position within the file that is being read or written can be determined and also changed. This allows a file to be read and updated without having to make a new copy of the file or working through the whole file sequentially. This could be useful if you have created a database file and you want to find and update just a few records within it. It is also possible to create an index file that contains pointers to where each record starts in a data file. To calculate an offset you need to know how long each data element is; Offset = Wanted_Element * size_of_element - size_of_element For example a file of integers which are 4 bytes long is calculated by: The 7th integer is at offset 7 * 4 - 4 i.e. 24 Note, extreme care needs to be exercised when updating files that contain strings since these are not fixed in length. |
|
; Changing part of a file using OpenFile, SeekFile, FilePos
; Open/create a file to Write fileout = WriteFile("mydata.dat") ; Write the information to the file WriteInt( fileout, 100 ) WriteInt( fileout, 200 ) WriteInt( fileout, 300 ) WriteInt( fileout, 400 ) WriteInt( fileout, 500 ) ; Close the file CloseFile( fileout ) DisplayFile( "The file as originally written", mydata.dat" ) Print "Data read in random order" ; Open the file to read just the 4th and 2nd elements from file = OpenFile("mydata.dat") ; read and print the 4th integer ie 4*4-4 = 12 byte from the start of the file SeekFile( file, 12 ) ; Move to the found location Number = ReadInt( file ) Print Number ; read and print the 2th integer ie 2*4-4 = 4 bytes from the start of the file SeekFile( file, 4 ) ; Move to the found location Number = ReadInt( file ) Print Number CloseFile( file ) Waitkey() End ; End of program ; **** Function Definitions follow **** ; Read the file and print it Function DisplayFile( Tittle$, Filename$ ) Print tittle$ file = ReadFile( Filename$ ) While Not Eof( file ) Number = ReadInt( file ) Print Number Wend CloseFile( file ) End Function |