Game Sales Analysis Final Project: Finding the Ideal Genre
Introduction
This project utilizes video game sales data from Kaggle to solve a potential business decision made by a fictional game studio. View the slide deck covering the same topics and a video walkthrough from the link in the sidebar.
Background
A fictional video game studio has been getting by for a few years producing low-budget fighting games. Recently, they had the opportunity for a new investor to get involved in their product and have a much higher budget for their next title. However, they also have a much higher sales target than in the past.
Problem
This studio has generally tended towards making fighting games. However, they now need to hit a sales target of 1.5 million copies to continue running, so they have decided to investigate if making a game of a different genre would help them reach this goal. Although this is the highest priority, there is a second goal of getting their game into the top 5 best-sellers of its genre in its release year. This would give them a solid chance at awards and critical acclaim that may help them get funding for future titles.
Exploratory Analysis
Before conducting the analysis of our specific problem, we must better understand the data we are working with. The game sales dataset has eleven different variables, and each entry represents a single release of a game that sold at least ten thousand copies worldwide. The attributes of these entries include the game’s name, its genre, the publisher that released it, the platform it was released on, five different sales columns representing the total copies sold in North America, Europe, Japan, other regions, and the whole world, the year of the game’s release, and the overal global sales ranking of a game in the dataset.
This dataset does not comprehensively cover all of the video game sales data that would be desired to make an informed decision regarding genre of a new game being produced today, as games released over the last eight years are not included and recent video game consoles are missing from the platforms as well. However, an updated version of this dataset could be analyzed in the same way as the limited dataset in this article, so the existing data is being used as a proof-of-concept of exploratory and business analysis.
genre_groups <- game_sales |>group_by(genre) |>summarize(count =n())genre_groups |>ggplot() +geom_col(mapping =aes(x = genre, y = count, fill = genre =="Fighting")) +scale_fill_brewer(palette ="Accent") +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1)) +labs(x ="Genre", y ="Count", title ="Count of Genres")
There are twelve genres included in our dataset, one of them being Fighting–the genre the development studio was most interested in producing. By far the most common games in this dataset are Action games, followed by Sports games, and the least popular are Puzzle and Strategy, although there may be some overlap between these categories. There is a Misc category as a catch-all, so there are no games missing a genre in the dataset.
paste("Number of unique publishers in the dataset:",total_publishers)
[1] "Number of unique publishers in the dataset: 579"
Code
top_publishers <- game_sales |>group_by(publisher) |>summarize(count =n()) |>slice_max(count, n =10)top_publishers |>ggplot() +geom_col(mapping =aes(x = publisher, y = count)) +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1)) +labs(x ="Publisher", y ="Count", title ="Count of Top 10 Publishers")
Unlike genre, there are some entries in the dataset that are missing information about who published them. Whether these are cases where actual ownership over the game is unclear due to changing publishers or the source the data was pulled from was simply missing the publisher by mistake is unclear. With over 500 publishers, we cannot visualize how common each one is, so we will instead view the top 10. The most common publisher in the entire dataset is Electronic Arts, followed by Activision, Namco Bandai Games, and Ubisoft.
Code
total_game_count <- game_sales |>summarize(count =n()) |>pluck("count")top_publisher_game_count <- game_sales |>filter(publisher %in% top_publishers$publisher) |>summarize(count =n()) |>pluck("count")paste("Total number of games published by top 10 publishers:",top_publisher_game_count)
[1] "Total number of games published by top 10 publishers: 8164"
Code
paste("Percentage of games in dataset published by top 10 publishers:",round(100*top_publisher_game_count/total_game_count, 2),"%")
[1] "Percentage of games in dataset published by top 10 publishers: 49.19 %"
These top 10 publishers have published nearly 50% of the total games in the dataset, even though there are over 500 other publishers included. Further analysis could give an idea of how successful these games by big-name publishers tend to be compared to other publishers that might be ‘one-hit wonders’.
paste("Number of unique platforms in the dataset:",total_platforms)
[1] "Number of unique platforms in the dataset: 31"
Code
top_platforms <- game_sales |>group_by(platform) |>summarize(count =n()) |>slice_max(count, n =10)top_platforms |>ggplot() +geom_col(mapping =aes(x = platform, y = count)) +theme_minimal() +labs(x ="Platform", y ="Count", title ="Count of Top 10 Platforms")
Every entry in the dataset has a platform associated with it, of which there are 31 total in the dataset. The top two platforms, the DS and the PS2 are extremely close in number of games, and they both have over a 500 game lead compared to the next most popular platforms.
Code
top_platform_game_count <- game_sales |>filter(platform %in% top_platforms$platform) |>summarize(count =n()) |>pluck("count")paste("Total number of games released on top 10 platforms:",top_platform_game_count)
[1] "Total number of games released on top 10 platforms: 13258"
Code
paste("Percentage of games in dataset released on top 10 platforms:",round(100*top_platform_game_count/total_game_count, 2),"%")
[1] "Percentage of games in dataset released on top 10 platforms: 79.88 %"
These top 10 platforms make up a very large portion of the games in the dataset–nearly 80%. However, unlike game publishing companies that persist over time, many platforms grow out-of-date as new consoles are released. Rather than focus too much on the individual platform, later analysis could categorize platforms by the company that produced them and anaylyze the success of games released on them.
game_sales |>ggplot() +geom_histogram(mapping =aes(x = year), color ='white') +theme_minimal() +labs(x ="Release Year", y ="Count")
There are quite a few games missing release year in the dataset, which starts as early as 1980. It continues up to 2017 with a peak around 2010, then strangely skips two years and has a very small amount of data again in 2020. Time series modeling will not be relevant for this analysis, but these discrepancies with the data are good to keep in mind.
paste("Range of North America sales:",na_range[1],"-",na_range[2])
[1] "Range of North America sales: 0 - 41.49"
Code
paste("Range of Europe sales:",eu_range[1],"-",eu_range[2])
[1] "Range of Europe sales: 0 - 29.02"
Code
paste("Range of Japan sales:",jp_range[1],"-",jp_range[2])
[1] "Range of Japan sales: 0 - 10.22"
Code
paste("Range of Other sales:",other_range[1],"-",other_range[2])
[1] "Range of Other sales: 0 - 10.57"
There are five different sales columns in the dataset: three specific regions, one catch-all column for copies sold in other regions, and a global sales column that sums the total copies sold across all regions, all of which are in millions. The minimum required sales to be in this dataset is 10 thousand, so the minimum global sales value is .01, but it is common for a game that is successful in one region to remain unpopular or not be released in another, so all of the more specific sales columns go all the way down to 0. Naturally the highest-reaching column is the sum global sales, but of the separate regions, North America has the highest sales, followed by Europe, the ‘Other’ region, and Japan.
Code
game_sales |>ggplot() +geom_point(mapping =aes(x = year, y = global_sales)) +theme_minimal() +labs(x ='Release Year', y ='Global Copies Sold (Millions)')
Looking at the number of copies sold globally throughout the dataset, we can see that the data is extremely heavily 0-weighted, or 0.1-weighted when it comes to the global sales column, as expected with count data. However, there are a handful of extremely high-selling games that are included in the dataset as well.
Business Analysis
Meeting Sales Target
Designing Metric
Now that we understand what variables are available in our dataset, we can utilize them to create a useful metric for the primary business goal. This is done by adding a new column of boolean values, noting if a game met the target set by the studio or not.
Graphing Genres and Meeting Target Sales Percentage
By graphing the percentage of games meeting the target of 1.5 million copies sold, we see that a fighting game may not be the best choice. Role-playing games seem to be a slight improvement, and platformer and shooter games appear to be about 5% more likely to meet such a sales target. It is very possible that there is a confounding variable linking the genre and global sales values, such as the average price of different genres of games, but this is not present in our data. Thus, we will only investigate the significance of the connection between genre and meeting target sales directly.
Code
genre_target_ratios |>mutate(meets_target_percent = meets_target_ratio *100) |>ggplot() +geom_col(mapping =aes(x = genre, y = meets_target_percent, fill = genre =="Fighting")) +theme_minimal() +labs (x ="Game Genre", y ="Percentage of Games Meeting Target Sales", title ="Game Genre vs. Percentage of Games Meeting Target Sales (1.5 Million Copies)", fill ="Is a Fighting Game?") +scale_fill_brewer(palette ="Accent") +theme(axis.text.x =element_text(angle =45, hjust =1))
ANOVA Testing
First, we will conduct an ANOVA test to see if it is possible the true population percentage of games meeting target sales is the same across all genres. We get a high F value and a very small P-value, meaning that this data would be extremely unlikely to be collected if this null hypothesis was true. Therefore, we can assume that genre has an impact on the likelihood of a game meeting target sales.
Code
aov_result <-aov(meets_target ~ genre, data = game_sales)summary(aov_result)
Df Sum Sq Mean Sq F value Pr(>F)
genre 11 13.5 1.2287 17.63 <2e-16 ***
Residuals 16586 1155.9 0.0697
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Fisher’s Exact Hypothesis Testing
Knowing that it is highly unlikely all genres have an equal chance of meeting target sales is useful, but we need to investigate the specific genres that appear to be more successful than the preferred genre, fighting games. We can create three contingency tables, showing the number of ‘successes’ (games that met target sales) and ‘failures’ (games that did not meet target sales) of fighting games and one of three other genres: platformers, shooters, and role-playing games. With the data in this format, we can conduct Fisher’s Exact Test on each table to get an idea of how unlikely it is that games of the two genres under investigation have an equal probability of meeting target sales. If we get a low p-value on a test (for example, less than .05), that is an indicator that there is a difference in the probability of hitting the sales target for the two categories.
Fisher's Exact Test for Count Data
data: select(fighting_platform_table, met_target, missed_target)
p-value = 0.0003234
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
0.4100787 0.7794299
sample estimates:
odds ratio
0.5670237
Fisher's Exact Test for Count Data
data: select(fighting_shooter_table, met_target, missed_target)
p-value = 0.002345
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
0.4693230 0.8622751
sample estimates:
odds ratio
0.6386536
Fisher's Exact Test for Count Data
data: select(fighting_roleplaying_table, met_target, missed_target)
p-value = 0.7597
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
0.6893464 1.2914795
sample estimates:
odds ratio
0.9465713
These results show that it would be very unlikely to find data more extreme than our sample if Fighting and Platform games had the same average global sales value, and the same is true for Fighting and Shooter games. Thus, we can assume that the probability of meeting our sales target is different for these categories. On the other hand, the Fighting and Role-Playing hypothesis test provided a large p-value, so we cannot make the same assumption about these two games. We would expect Role-Playing games to be as likely to be successful as Fighting games.
Now that we have assumed Platform games are more likely to meet the sales target that Fighting games, and likewise with Shooter games and Fighter games, it would be good to know if one of Platform or Shooter games was noticeably better than the other, as this would drive our decision forward. We can follow the same process of creating a contingency table and using Fisher’s Exact Test with Platform and Shooter games.
Fisher's Exact Test for Count Data
data: select(platform_shooter_table, met_target, missed_target)
p-value = 0.3666
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
0.8683859 1.4583802
sample estimates:
odds ratio
1.12641
The result from this test is insignificant, and thus we cannot conclude that one of Platform and Shooter games has a higher probability of meeting the sales target than the other. Since these two genres both are better at meeting our first goal than Fighting games, we can now investigate our second priority and see if one of the two best options is better there.
Finding Fifth-Best Benchmark
Designing Secondary Metric
The second priority of the team is getting their game into the top 5 best-selling games of its genre for its release year. To make the chances of this more likely, they want to pick a genre with a lower bar to reach to get into the top 5 games of its kind of the year. We can measure this by finding the average number of copies sold of yearly fifth-best-sellers. Since we are only focusing on Platform and Shooter games, we want to find which genre has a lower value on this metric, as that indicates it may be easier for the team to get the game into the top 5.
To calculate our metric, we want to filter our games to one genre (either platformers or shooters), group it by the year, find the fifth best game by global sales of each year, then get the mean of these values. However, in some years there might not have been 5 games sold in our data, so in these cases we will take the lowest ranking game.
[1] "Global sales mean of fifth-best platformers: 1.222"
Code
fifth_best_shooters_mean <- fifth_best_shooters |>pluck("global_sales") |>mean()paste("Global sales mean of fifth-best shooters: ", round(fifth_best_shooters_mean, 3))
[1] "Global sales mean of fifth-best shooters: 1.621"
It appears that the average global sales made by a fifth-best-selling game is higher for a shooter than a platformer, which would imply that a platformer would be easier to get into the top 5 for and thus a better choice. However, the resulting metric for our data is not necessarily the true population value. To get an idea of a range the true metrics may fall into, we can bootstrap our data and use it to help create confidence intervals.
Secondary Metric Bootstrapping
Bootstrapping is a method of simulating random sampling from the population by treating our existing sample as the population and taking samples from it with replacement. To do this, we must assume that our game sales data is representative of the true population of video games. This is not the most accurate assumption, since the data is intended to be comprehensive above a certain point, rather than a random sample of video games. However, this method can still potentially help us estimate what the distribution of random samples of video games could look like.
ggplot() +geom_histogram(mapping =aes(x = platform_values), color ='white') +geom_vline(xintercept = fifth_best_platforms_mean, color ='orange') +theme_minimal() +labs(x ="Bootstrapped Platform Global Sales Values", y ="Count")
Code
platform_se <-sd(platform_values)
Code
ggplot() +geom_histogram(mapping =aes(x = shooter_values), color ='white') +geom_vline(xintercept = fifth_best_shooters_mean, color ='orange') +theme_minimal() +labs(x ="Bootstrapped Shooter Global Sales Values", y ="Count")
Code
shooter_se <-sd(shooter_values)
Here we can see the distributions of our bootstrapped samples of platformers and shooters, which would be similar to the true distribution of video game samples. In both cases, the metric calculated from our entire dataset (marked by an orange line) is on the lower end of the distribution. Since our random samples are done with replacement, some of these very high values may occur when primarily high-selling games are repeatedly selected in one sample, so they may be more extreme than we would actually expect. However, the standard error can still be used in calculating our confidence intervals for our metric.
Secondary Metric Confidence Intervals
To get an idea of where the fifth-best-sales-mean metric truly falls, we can create confidence intervals around the means calculated from our sample. For both platformers and shooters, we will create a 90% confidence interval, meaning that, if we developed this confidence interval for 100 different samples of our population–assuming our sample is representative of the population–we would expect the true population metric to fall within it 90 times. This does not guarantee our population metric will fall in the interval, but it is generally a reasonable assumption to make. We will assume that our metric follows a normal distribution to determine how many standard errors (calculated from the bootstrapped samples) away from the mean we should use.
It turns out that the confidence intervals for platformers and shooters have a large amount of overlap. Thus, we cannot make any assumptions about one having a lower fifth best-selling mean statistic than the other. The platformer confidence interval does extend lower than the shooter one, so it may be a better choice for getting our game into the top 5 best-sellers, but we cannot be confident in this assumption.
Conclusions
Although fighting games were the genre preferred by the team, we determined that both platformer and shooter games are probably more likely to reach the necessary sales target of 1.5 million copies sold by using hypotheiss testing. However, there was not a significant difference between platformer and shooter games when it comes to reaching the sales target, and the potential ranges of fifth-best-selling game benchmarks for these two genres overlapped greatly. Platformers may have a lower number of copies necessary to sell in order to reach the top 5 best-sellers of the year, but since we cannot be confident in this, the choice between platformers and shooters can be left up to the development team.
Next Steps
The natural next step for this project would be to gather similar data for games each year up to the present, so the analysis can better reflect current trends in video game sales. Other kinds of data could also potentially be gathered to see if there is a ‘tie-breaker’ that could be used for two genres with similar rates of meeting the sales target and similar fifth-best-seller averages, as seen with Platform and Shooter games above. Information on the development side of video games, such as how long Platform and Shooter games typically take to produce and how much they cost to make, could help determine which is a better choice than the other. Finally, another piece of analysis that could be completed is determining which video game platform would be best for the game to release on by using similar hypothesis testing techniques to those that were used to determine the genre.