Some statistics from a widget industry
24 Dec 2016An unnamed source sent me some fun datasets for an industrially-produced widget sold in Producistan for P-dollars (denoted $). The widget manufacturers seem to compete in a monopolistically competitive industry. There may be some vertical and horizontal integration, but I can’t see it in the data I have.
This post, like the previous one, is just a fun exercise in holiday programming. I found a neat R package, GGally
, while writing this.
The data
I’ll look at two datasets here: one a spreadsheet with data aggregated to the brand level where each row is a separate brand observation, and another spreadsheet with data broken down to a products offered by different brands. The data come from Producistan’s popular online shopping portal, and include the number of reviews per firm and per product as well as the average review ranking (usual 1-5 scale).
I have a third dataset where I linked the two by brand. I haven’t done anything with the linked dataset, but I like knowing it’s an option.
My source would prefer the data not be public, so I’ve loaded the spreadsheets from a pre-saved workspace and anonymized the firms and products. I’m not really interested in the industry, and my source doesn’t really care about the code and pictures being public.
Plots and regressions and stuff
Let’s look at the aggregated data first.
Aggregated data
I really like the scatterplot matrix produced by ggpairs()
. The diagonal is density plots for the variables, the upper triangular has correlation coefficients between the row and column variables, and the lower triangular has pairwise scatterplots with the row variable on the y-axis and the column variable on the x-axis. The scatterplots would be more useful with outliers trimmed, but I think there’s some utility to keeping them in. Let’s move through the diagonal and lower triangular column by column.
It looks like there are a lot of firms with 0-50 SKUs offered, and very few with more than that. I think there may be only one with more than 300 offered. Most firms have a decent price spread, but firms with a lot of SKUs tend to have prices below $20,000. Most of these SKUs have on the order of tens or hundreds of reviews, but a handful of firms with lots of SKUs have thousands of reviews. The phrase “long tail” comes to mind here.
I’m not sure how much we can glean from the correlation numbers. They’re just raw correlations, not controlling for anything or accounting for outliers, so I won’t spend any more time on them.
The dispersion variable is interesting. It’s the difference between the maximum and minimum observed prices for the item. My source tells me that a number of the listings they scraped for the data include fraudulent listings with prices that are way too high/low. I’m not sure what the purpose of these listings is, but it looks like products with very few reviews seem to have higher dispersions (though the correlation coefficient isn’t very high).
I think one of the weaknesses of the ggpairs()
plots (and maybe scatterplot matrices in general) is that the y-axis scales aren’t always easy to figure out. The density plots, for example, are on a different scale from the scatterplots, but it’s hard to show that in a pretty way.
Now let’s run some simple linear regressions:
The standard errors aren’t robust, so I’m ignoring the t-stat/p-values here. I wouldn’t take these regressions very seriously - they’re just giving fancier correlations than the raw coefficients in the scatterplot matrix. Caveats issued, let’s read the tea leaves look at the parameter estimates.
From the first model, it looks like there is a positive correlation with the dispersion and the number of SKUs, and a positive correlation with the average price. There’s maybe a small negative correlation between the dispersion and the total number of reviews.
From the second model, it looks like a firm’s average price has a positive correlation with the number of SKUs the firm offers, and a small negative correlation with the number of reviews. These support a story where firms with more SKUs tend to target higher-income markets more aggressively, and where firms with higher prices tend to have fewer sales, but the standard errors are so large that it’s hard to buy the story just from the data.
Ok, let’s look at the disaggregated data now.
Disaggregated data
The prices have some commas and ranges in them, so they need to be cleaned before we can work with them. Getting rid of the commas is an easy application of gsub()
. The ranges are a little trickier. There are dashes and number pairs in the fields. Getting rid of them and picking a single number would be fairly easy application of str_split_fixed()
from the stringr
library, but I’d like to take an average. That needs a few more lines.
In the end, I couldn’t think of an elegant one-liner to get the averages I wanted in the one minute I spent thinking about the problem. My lazy “non-R” thought was to write a for loop, but copy-pasting and editing the values ended up being just as easy. If I get a bigger dataset with more ranges, it would be worth it to spend a little more time writing something that scales better.
A lot of the products don’t have reviews or ratings.
Starting from the central density: most products have ratings around 4. Moving to the left, it looks like products with ratings around 4 tend to have the most reviews, as we might expect if having a rating is correlated with the number of reviews. Moving down, it looks like products with reviews and ratings tend to be lower-priced. One story for this is that higher-priced products sell fewer units.
Let’s look at the average price by brand now.
I dropped the brands with too few products for error bars which exclude zero. This also makes the plot slightly more legible - the full plot has over 150 brands, and is just not very easy to read. It looks kinda like something we’d get out of a Hotelling model of monopolistic competition, with maybe heterogeneous consumers/firms. Most firms seem to stay in the $20,000 or less price range, with a few gunning for the higher-end segments.
Let’s wrap up with some more linear regressions:
The Firm.id
variable should be entered as a factor in m2.wmic
rather than a numeric, but this makes the output a little cleaner and we’re not taking these regressions very seriously anyway.
It looks like higher-rated products tend to be higher-priced, controlling for the number of reviews and the identity of the firm. It also looks like the number of reviews is negatively correlated with the price. These results are consistent with a story where price is positively correlated with quality and negatively correlated with sales, and sales are positively correlated with the number of reviews (the story for the sign of the reviews coefficient is just a fancier way to say “demand slopes downward”).
Conclusion
Widgets in Producistan look like a monopolistically competitive industry. Price might be positively correlated with quality, and demand slopes downward. Firms with more products might go for the higher-WTP consumers more aggressively on average, while making fewer sales. Storytime aside, ggpairs()
is a cool function.