Week 7 - Valentine’s Day Consumer Data
ns year-2024.week-7.analysis
(:require
(:as ht]
[aerial.hanami.templates :as charred]
[charred.api :as hanami]
[scicloj.noj.v1.vis.hanami :as tc])) [tablecloth.api
This week’s data is from Valentine’s day surveys about how much people spend and on what. The data originally comes from a kaggle dataset and has been collated into three CSVs by the R tidy tuesdays team, so we’ll just use it directly.
There are three datasets – one on historical spending, one breaking down spending on gifts by age, one breaking it down by gender.
def historical-spending
("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-02-13/historical_spending.csv")) (tc/dataset
def gifts-gender
("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-02-13/gifts_gender.csv")) (tc/dataset
def gifts-age
("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-02-13/gifts_age.csv")) (tc/dataset
Historical spending
The first and easiest thing to plot is just spending over time. This isn’t linear data (i.e. there’s no inferred value missing between the years), so we’ll just make a bar chart.
-> historical-spending
(
(hanami/plot ht/bar-chart:X "Year"
{:XTYPE "ordinal"
:XAXIS {:labelAngle -45 :title "Year"}
:Y "PerPerson"
:YAXIS {:format "$.0f" :title "Spending per person"}}))
We can see spending is increasing over time. It might also be interesting to see how the proportion of people celebrating Valentine’s day is changing over time:
-> historical-spending
(
(hanami/plot ht/bar-chart:X "Year"
{:XTYPE "ordinal"
:XAXIS {:labelAngle -45 :title "Year"}
:Y "PercentCelebrating"}))
From this we can clearly see it’s decreasing. If we change the scale of the y axis though it makes the decrease seem less pronounced. We can also format the y axis values – to do this we’ll have to update the column to support the new formatting.
-> historical-spending
("PercentCelebrating"
(tc/update-columns {partial map #(-> % (/ 100) double))})
(
(hanami/plot ht/bar-chart:X "Year"
{:XTYPE "ordinal"
:XAXIS {:labelAngle -45 :title "Year"}
:Y "PercentCelebrating"
:YSCALE {:domain [0 1]}
:YAXIS {:format "1%" :title "Percent celebrating"}}))
We could compute the percentage change in spending year over year per category, to see how the spending habits by category have changed over the period we have data for. First we’ll get the first and last years that we have data for, then we’ll compute the percentage change for each category.
We can see there are 13 rows, so we’ll just make extra sure they’re sorted by year and then manually pick out the first and last ones. Then we’ll want to pivot the data to lengthen it so we have one column for the categories and one for the amounts.
This is where I ran into the first problem with the data. Sorting by year should work here, but instead we get an error about not being able to find the “Year” column.
(tc/row-count historical-spending)
13
-> historical-spending
("Year")
(tc/order-by 0 12])) (tc/select-rows [
The “Year” column header looks right:
-> historical-spending
(
tc/column-namesfirst)
"Year"
But if we inspect the first character of it, we can see it’s misleading:
-> historical-spending
(
tc/column-namesfirst
first)
\
(That should just be “Y”)
We can find out what actual character this is by dropping into Java:
-> historical-spending
(
tc/column-namesfirst
first
;; first convert the character to its integer value
int
(Integer/toHexString))
"feff"
So this first character is actually the unicode character “U+FEFF”, the dreaded “zero-width no-break space”, also known as the byte order mark. For historical and other reasons, sometimes files start with this nefarious character.
There are lots of ways to fix this, one is to just strip the character from the CSV first and then create our dataset. We’ll have to create it a little more manually since we’ll be passing the data directly the dataset constructor instead of reading it from a file:
def historical-spending
(let [data (-> "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-02-13/historical_spending.csv"
(slurp
subs 1)
(
charred/read-csv)]rest data) {:column-names (first data)}))) (tc/dataset (
Now we should be able to pivot our data to re-organize it and make our grouped bar chart:
-> historical-spending
("Year")
(tc/order-by 0 12])
(tc/select-rows [complement #{"Year" "PercentCelebrating" "PerPerson"})
(tc/pivot->longer (:target-columns "Category"
{:value-column-name "Amount"}))
:_unnamed [14 5]:
Year | PercentCelebrating | PerPerson | Category | Amount |
---|---|---|---|---|
2010 | 60 | 103.00 | Candy | 8.60 |
2022 | 53 | 175.41 | Candy | 15.90 |
2010 | 60 | 103.00 | Flowers | 12.33 |
2022 | 53 | 175.41 | Flowers | 16.71 |
2010 | 60 | 103.00 | Jewelry | 21.52 |
2022 | 53 | 175.41 | Jewelry | 45.75 |
2010 | 60 | 103.00 | GreetingCards | 5.91 |
2022 | 53 | 175.41 | GreetingCards | 7.47 |
2010 | 60 | 103.00 | EveningOut | 23.76 |
2022 | 53 | 175.41 | EveningOut | 31.35 |
2010 | 60 | 103.00 | Clothing | 10.93 |
2022 | 53 | 175.41 | Clothing | 21.46 |
2010 | 60 | 103.00 | GiftCards | 8.42 |
2022 | 53 | 175.41 | GiftCards | 17.22 |
Great. Now we can get back to computing the percent changes between the years. There are lots of ways to do this. I think the easiest is to pivot our data to tidy it up, then fold the rows by Category to group the pairs of values to work with.
-> historical-spending
("Year")
(tc/order-by 12 0])
(tc/select-rows [complement #{"Year" "PercentCelebrating" "PerPerson"})
(tc/pivot->longer (:target-columns "Category"
{:value-column-name "Amount"})
"Amount"] :float64)
(tc/convert-types ["Category")
(tc/fold-by "Increase" ["Amount"] (fn [[new old]]
(tc/map-columns -> new (- old) (/ old))))) (
_unnamed [7 6]:
Category | Year | PercentCelebrating | PerPerson | Amount | Increase |
---|---|---|---|---|---|
Candy | [2022 2010] | [53 60] | [175.41 103.00] | [15.9 8.6] | 0.84883721 |
Flowers | [2022 2010] | [53 60] | [175.41 103.00] | [16.71 12.33] | 0.35523114 |
Jewelry | [2022 2010] | [53 60] | [175.41 103.00] | [45.75 21.52] | 1.12592937 |
GreetingCards | [2022 2010] | [53 60] | [175.41 103.00] | [7.47 5.91] | 0.26395939 |
EveningOut | [2022 2010] | [53 60] | [175.41 103.00] | [31.35 23.76] | 0.31944444 |
Clothing | [2022 2010] | [53 60] | [175.41 103.00] | [21.46 10.93] | 0.96340348 |
GiftCards | [2022 2010] | [53 60] | [175.41 103.00] | [17.22 8.42] | 1.04513064 |
Now we can plot the percentage increase by category. I’ll sort by the increase to make the chart look nicer.
-> historical-spending
("Year")
(tc/order-by 12 0])
(tc/select-rows [complement #{"Year" "PercentCelebrating" "PerPerson"})
(tc/pivot->longer (:target-columns "Category"
{:value-column-name "Amount"})
"Amount"] :float64)
(tc/convert-types ["Category")
(tc/fold-by "Increase" ["Amount"] (fn [[new old]]
(tc/map-columns -> new (- old) (/ old))))
(
(hanami/plot ht/bar-chart:X "Category"
{:XTYPE :nominal
:XSORT "y"
:Y "Increase"}))
Spending differences between men and women
We also have data about spending differences between men and women. We can compare those side by side if we rearrange the data a little bit first. Our gifts-by-gender dataset has a separate column per category. We’ll want to rearrange the data so that the category is a single variable with each value in its own row.
This dataset has the same issue as the last one, so we’ll re-fetch it and strip the first character.
def gifts-gender
(let [data (-> "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-02-13/gifts_gender.csv"
(slurp
subs 1)
(
charred/read-csv)]rest data) {:column-names (first data)}))) (tc/dataset (
Now we should be able to pivot our data to re-organize it and make our grouped bar chart:
-> gifts-gender
(complement #{"Gender" "SpendingCelebrating"})
(tc/pivot->longer (:target-columns "Category"
{:value-column-name "Percentage"})
(hanami/plot ht/bar-chart:X "Category"
{:XTYPE :nominal
:Y "Percentage"
:COLOR "Gender"})
assoc-in [:encoding :xOffset] {:field "Gender"})) (
Here we can see pretty clearly that men are significantly more likely to spend on flowers and jewelry than women, but the other categories are more evenly matched.
We also have data about spending by age, so we can explore a bit how spending habits change with age. We’ll start again with fixing the dataset and pivoting the data to make a column for categories and a column for the percentages.
def gifts-age
(let [data (-> "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-02-13/gifts_age.csv"
(slurp
subs 1)
(
charred/read-csv)]rest data) {:column-names (first data)}))) (tc/dataset (
I think the most informative way to plot this would be to see the categories grouped together, comparing the age ranges side by side within a category.. if that makes any sense. Anyway like this:
-> gifts-age
(complement #{"Age" "SpendingCelebrating"})
(tc/pivot->longer (:target-columns "Category"
{:value-column-name "Percentage"})
(hanami/plot ht/bar-chart:X "Category"
{:XTYPE :nominal
:Y "Percentage"
:COLOR "Age"})
assoc-in [:encoding :xOffset] {:field "Age"})) (
This shows a pretty clear trend that spending in all categories except for greeting cards seems to decrease with age. This is interesting, but seeing it makes me wonder what the main message would be if we reversed the age/category encodings:
-> gifts-age
(complement #{"Age" "SpendingCelebrating"})
(tc/pivot->longer (:target-columns "Category"
{:value-column-name "Percentage"})
(hanami/plot ht/bar-chart:X "Age"
{:XTYPE :nominal
:Y "Percentage"
:COLOR "Category"})
assoc-in [:encoding :xOffset] {:field "Category"})) (
I still think the first one is better, but I suppose it depends what information you’re looking for. From this one we can see that people of all ages are likely to spend on candy and greeting cards, and that gift cards are the least common gift no matter the age.
Well.. there are tons of questions we could ask of this data, but I’ll leave it here for this week. See you next week :)