2 Constructing our data sets

Data is the raw material for the rest of this book. Before we can explore, model, segment, price, score leads, evaluate promotions, or simulate operations, we need a working set of tables that look enough like sports business data to be useful.

This chapter introduces those tables. The data is fictional, but the structure is based on common business problems inside a professional sports organization: customer records, season schedules, ticket inventory, secondary-market sales, demographics, survey responses, and operational observations. The examples use the Nashville Game Hens, an imaginary professional baseball team, but the lessons apply across teams, venues, and other live-event businesses.

The data used throughout the book lives in the FOSBAAS package. Earlier versions of this chapter spent a lot of time showing the original data-generation code. That was useful while the package was being built, but it is less useful for readers now. The package has been reconstructed, and the book should treat it as the stable source of teaching data. When you need a table, call it directly from the package:

library(FOSBAAS)

customer_renewals <- FOSBAAS::customer_renewals
season_data <- FOSBAAS::season_data

In this chapter we will cover:

  • What kinds of data appear in sports business analytics
  • How the book’s example data sets fit together
  • How to inspect the data before using it in later chapters
  • How to generate small alternate examples when that helps explain the workflow

The goal is not to make you memorize every column. The goal is to make the rest of the book easier to follow because you understand what each table represents.

2.1 Working With R Data

The examples in this book use R (R Core Team 2025). R is common in statistics, visualization, teaching, and applied analytics. Python, SQL, spreadsheets, and BI tools are also common in sports organizations. The tool matters less than the habit: keep the work reproducible, inspect the data before modeling it, and connect the analysis to a business decision.

Code is useful because it records what happened. A spreadsheet can be fast, but it is often difficult to audit after several manual changes. A script makes the sequence of decisions visible: load the data, filter rows, join tables, calculate fields, summarize results, and create charts. That does not make code automatically correct. It makes the work easier to check.

Most examples in later chapters follow a simple pattern:

  1. Load a data set from FOSBAAS
  2. Inspect its columns and row counts
  3. Transform it into the shape needed for the analysis
  4. Build a graph, summary, model, or simulation
  5. Interpret the result in business terms

You do not need to install every package used in the entire book from inside this chapter. The render script and the chapter code load packages as needed. If something is missing, install the package named in the error message and rerun the chunk.

2.2 The Example Data Model

A sports organization does not usually work from one perfect table. Data is spread across ticketing systems, CRM tools, survey platforms, finance files, operations systems, and third-party vendors. Even when a vendor provides clean exports, analysis usually requires joining and reshaping multiple sources.

The FOSBAAS data is simplified, but it preserves the main relationships:

  • customer_data gives customers an identifier and a name.
  • demographic_data adds customer-level demographic and geographic attributes.
  • season_data describes games, opponents, dates, promotions, and simulated demand.
  • manifest_data describes seat inventory and price levels.
  • secondary_data connects customers, seats, games, ticket types, and resale prices.
  • customer_renewals describes season-ticket accounts and renewal behavior.
  • perceptual_data and fa_survey_data support survey and segmentation examples.
  • scan_data and wait_times_data support operations examples.

These tables are cleaner than real operational data. That is intentional. The book needs data that is realistic enough to teach useful patterns but stable enough to run on a reader’s machine. Real data would include duplicate customers, missing values, inconsistent names, odd date formats, refunded transactions, seat relocations, package changes, and many other complications. We will still discuss those issues, but the teaching data keeps the focus on the analytical method.

You can see the available data objects exported by the package with:

ls("package:FOSBAAS")

2.3 Customer Renewal Data

The customer_renewals table is used later to demonstrate lead scoring and renewal modeling. Each row represents a season-ticket account for a given season.

Table 2.1: Customer renewal data
variable class first_values
accountID character WD6TDY7C151R, X3SB8ADEML22
corporate character i, c
season numeric 2021, 2021
planType character p, f
ticketUsage numeric 0.728026975947432, 0.992104738159105
tenure numeric 2, 19
spend numeric 4908, 16410
tickets numeric 6, 2
distance numeric 61.6614648674555, 19.5341155295423
renewed character nr, nr

The most important fields are:

  • accountID: account identifier
  • corporate: whether the account is corporate or individual
  • season: season year
  • planType: full or partial plan
  • ticketUsage: share of tickets used
  • tenure: years with the organization
  • spend: ticket spend
  • tickets: number of tickets on the account
  • distance: distance from the ballpark
  • renewed: renewal outcome

This is a customer-retention table. A team might use similar data to understand which accounts are at risk, which service behaviors matter, or which renewal offers should be prioritized. The exact columns vary by organization, but the pattern is common: account attributes, behavior, value, geography, and an outcome.

The package also includes the function used to create synthetic renewal-style data. You do not need to use this function for the rest of the book, but it is helpful for understanding how reproducible simulated data can be generated.

small_renewal_sample <- FOSBAAS::f_create_lead_scoring_data(
  seed = 434,
  num_purchasers = 100,
  season = "2023",
  f_tenure = FOSBAAS::f_calculate_tenure,
  f_spend = FOSBAAS::f_calculate_spend,
  f_use = FOSBAAS::f_calculate_ticket_use,
  f_renewal_assignment = FOSBAAS::f_renewal_assignment,
  f_assign_renewal = FOSBAAS::f_assign_renewal,
  renew = FALSE
)

describe_data(small_renewal_sample, "Small generated renewal sample")
Table 2.2: Small generated renewal sample
variable class first_values
accountID character IQ7G9BPWYJSD, L3TA5OEHCAAF
corporate character i, i
season character 2023, 2023
planType character f, f
ticketUsage numeric 0.726619377103634, 0.880502448994666
tenure numeric 0, 0
spend numeric 8848, 4892
tickets numeric 4, 2
distance numeric 25.237493169165, 4.39980797935277

The important idea is not the specific simulation formula. The important idea is reproducibility. The same seed and arguments produce the same synthetic data, which means the examples in the book can be checked, rerun, and modified.

2.4 Season Data

The season_data table is a simulated schedule and demand table. Professional baseball teams usually play 81 regular-season home games. This package contains three simulated seasons, for 243 rows.

Table 2.3: Season data
variable class first_values
gameNumber numeric 1, 2
team character SF, SF
date Date 2022-03-27, 2022-03-28
dayOfWeek character Sun, Mon
month character Mar, Mar
weekEnd logical FALSE, FALSE
schoolInOut logical FALSE, FALSE
daysSinceLastGame numeric 50, 1
openingDay logical TRUE, FALSE
promotion character none, none
ticketSales numeric 42928, 25759
season numeric 2022, 2022

The table includes game number, opponent, date, day of week, month, weekend flag, school-in-session flag, rest between games, opening day, promotion type, ticket sales, and season.

This table supports forecasting and pricing examples later in the book. The simulated sales values are intentionally patterned. Weekend games, summer games, certain opponents, opening day, and promotions can all affect demand. In real work, you might add more variables:

  • Team record and opponent quality
  • Playoff probability
  • Weather
  • Giveaway type and media support
  • Start time
  • Dynamic price changes
  • Competing local events

The simplified table is still useful because it lets us practice the core workflow: define demand drivers, visualize sales, estimate relationships, and interpret the result.

2.5 Manifest Data

A ticket manifest describes seat inventory. It is the practical upper bound of what can be sold, and it connects a physical seat to a section, row, seat number, and price.

Table 2.4: Manifest data
variable class first_values
seatID numeric 1, 2
section numeric 1, 1
sectionNumber numeric 1, 1
rowNumber numeric 1, 1
seatNumber numeric 1, 2
seasonPrice numeric 190, 190
groupPrice numeric 199.5, 199.5
singlePrice numeric 218.5, 218.5

Manifest data matters because many ticketing questions are really inventory questions. A club may want to know which sections are underpriced, where unsold seats are concentrated, whether group prices are too close to single-game prices, or how season-ticket inventory should be protected.

Real manifests can be more complicated than this example. They may include obstructed views, accessible seating, premium products, holds, kills, suite inventory, variable price codes, and event-specific configuration changes.

2.6 Customer and Demographic Data

The customer_data table is intentionally minimal. It gives us customer identifiers and names.

Table 2.5: Customer data
variable class first_values
custID character XVRB8F124HVE, 14F3TMMBK97A
name character Alice Stone, Jennifer Guerrero

The demographic_data table extends the customer concept with fields often purchased from a third-party vendor or appended from a CRM enrichment process.

Table 2.6: Demographic data
variable class first_values
custID character MBT9G0X70NTI, QTR3JJJ5J6GJ
nameF character Philip, Evelyn
nameL character Riddle, Campos
nameFull character Philip Riddle, Evelyn Campos
gender character m, f
age numeric 55, 30
latitude numeric 36.0491208455478, 37.1248221175549
longitude numeric -86.0860894506936, -86.0744325353366
distance numeric 48.34, 77.09
maritalStatus character m, s
ethnicity character w, w
children character y, n
hhIncome numeric 2866, 552
county character tennessee,wilson, kentucky,edmonson

Demographic and geographic data can help with segmentation, market analysis, direct marketing, and sales prioritization. It should also be used carefully. Vendor-modeled fields can be wrong, stale, incomplete, or inappropriate for certain decisions. Before using demographic data, ask what decision it will support and whether the field is reliable enough for that purpose.

The example includes customer identifiers, names, gender, age, location, distance from the ballpark, marital status, ethnicity, children in household, household income, and county.

2.7 Secondary-Market Data

The secondary_data table represents ticket purchases or resale activity at the seat-game level.

Table 2.7: Secondary market data
variable class first_values
seatID numeric 9010, 20950
custID character N22J8UPWACNO, II3IGIN0PY15
ticketType character se, se
gameID numeric 130, 111
tickets numeric 1, 1
priceKey character 9010_se, 20950_se
price numeric 54, 30
orderedCluster numeric 8, 7
secondayrPrice numeric 60.51, 37.11

This table connects several concepts:

  • seatID links to the manifest.
  • custID links to a customer.
  • ticketType distinguishes ticket categories.
  • gameID links to a game.
  • tickets records quantity.
  • priceKey, price, and secondayrPrice describe price fields used in later examples.

The column name secondayrPrice is misspelled in the package data and is left as-is here so the book matches the current API. In production work, you would usually correct names during a data-cleaning step and keep a documented mapping back to the source system.

Secondary-market data is useful because it reveals demand signals that primary sales alone may miss. It can also be noisy. Prices may reflect fees, listing behavior, timing, seat quality, opponent demand, or brokers’ strategies. Treat it as a signal, not a perfect measure of willingness to pay.

2.8 Survey Data

Survey data appears in two forms in the package.

The first is perceptual_data, an aggregated table used for perceptual mapping. Each row represents a team or property, and each column counts how often respondents selected an attribute.

Table 2.8: Perceptual data
Friendly Exciting Fresh Inovative Fun Old Historic Winners Great Expensive
1930 3080 1955 2128 2861 2757 280 1039 1781 3616
2646 4732 1444 2569 3508 4492 1775 3883 3422 3675
2928 3783 2959 3831 3417 3034 3034 1945 2876 2774

A question behind this table might ask:

How do you feel about the following sports properties? Please check all that apply for each team listed.

The second is fa_survey_data, a respondent-level survey table used for factor analysis and segmentation.

Table 2.9: Factor-analysis survey data
variable class first_values
ReasonForAttending character Special Game, Support the Team
Socialize numeric 2, 4
Tailgate numeric 3, 1
TakeSelfies numeric 4, 4
PostToSocialMedia numeric 2, 0
SeeFriends numeric 2, 3
VisitKidAttractions numeric 1, 1
MeetMascot numeric 3, 2
SnacksForKids numeric 3, 3
KidsRunBases numeric 9, 2
KidsSlide numeric 1, 2
GetDinner numeric 4, 8
EatParkFood numeric 1, 4
SampleFood numeric 2, 2
GetDrinks numeric 4, 5
DrinkBeer numeric 4, 4
BuyGear numeric 5, 0
TourThePark numeric 1, 8
VisitAttractions numeric 0, 2
WatchPregameShow numeric 9, 3
SeeTheChicken numeric 2, 0
UpgradeSeats numeric 2, 1
GetAutographs numeric 2, 1
WatchGame numeric 9, 0
SeePractice numeric 2, 1
MeetPlayers numeric 1, 1

Survey data is different from behavioral data. Ticket purchases tell us what someone did. Survey responses tell us what someone says, remembers, believes, or prefers. Both are useful, and both can mislead. Good research design matters because bad sampling or poorly worded questions can produce confident answers to the wrong problem.

2.9 Pricing Survey Data

Later chapters use pricing research concepts such as the Van Westendorp price sensitivity meter. The package does not need a separate object for every small teaching example, so the following chunk creates a compact pricing survey data set directly in the chapter.

set.seed(715)
vw_data <- data.frame(
  product = "DugoutSeats",
  expected_price = round(rnorm(1000, 100, 10), 0),
  too_expensive = round(rnorm(1000, 130, 20), 0),
  too_cheap = round(rnorm(1000, 60, 15), 0),
  bargain = round(rnorm(1000, 50, 10), 0),
  too_cheap_to_trust = round(rnorm(1000, 160, 20), 0)
)

knitr::kable(
  head(vw_data),
  caption = "Van Westendorp-style survey data",
  align = "c",
  format = "markdown",
  padding = 0
)
Table 2.10: Van Westendorp-style survey data
product expected_price too_expensive too_cheap bargain too_cheap_to_trust
DugoutSeats 100 115 57 39 170
DugoutSeats 113 147 69 52 129
DugoutSeats 102 169 67 51 163
DugoutSeats 103 134 72 54 148
DugoutSeats 99 88 46 37 163
DugoutSeats 83 123 77 59 145

A Van Westendorp-style pricing study asks respondents to identify price points that feel too expensive, too cheap, expensive but still possible, and a bargain. The exact wording matters, and pricing research should be interpreted alongside sales history, inventory constraints, brand positioning, and business goals.

2.10 Operations Data

Operations data helps teams understand how the venue works on event day. In this book, the operations examples focus on ingress and service times.

The scan_data table records simulated ticket scans by time interval.

Table 2.11: Scan data
variable class first_values
observations numeric 1, 2
scans numeric 0, 2
action_time hms, difftime 61200, 61260
date character 4/1/2024, 4/1/2024

The wait_times_data table records simulated service-process times.

Table 2.12: Wait-times data
variable class first_values
transaction numeric 1, 2
orderTimes numeric 39, 0
paymentTimes numeric 28, 56
fulfillTimes numeric 0, 7
totalTime numeric 67, 63

Operational data often behaves differently from customer or sales data. It is time-dependent, process-dependent, and sensitive to bottlenecks. A small staffing change, gate closure, POS outage, weather event, or delayed crowd arrival can change the shape of the data quickly.

The package also exposes helper functions for creating scan curves:

small_scan_sample <- FOSBAAS::f_get_scan_data(
  x_value = 150,
  y_value = 500,
  seed = 42,
  sd_mod = 8
)

describe_data(small_scan_sample, "Small generated scan sample")
Table 2.13: Small generated scan sample
variable class first_values
observations integer 1, 2
scans numeric 0, 8

This kind of simulated data is useful when you want to test an operational idea without exposing real venue data or waiting for a full season of observations.

2.11 Package Data and Reproducibility

The FOSBAAS package gives the book a stable data layer. That matters for three reasons.

First, it keeps the examples reproducible. Readers can run the same code and get the same tables.

Second, it keeps the chapters focused. A chapter on pricing should not need to rebuild a customer table before it can estimate demand.

Third, it makes the code easier to maintain. If the package data changes, the book should update the examples that use the public package objects and functions rather than copying large blocks of generation code into each chapter.

When working with package data, prefer explicit namespace calls:

FOSBAAS::season_data
FOSBAAS::customer_renewals
FOSBAAS::f_get_scan_data

This makes it clear where the object came from and reduces confusion when multiple packages define functions with similar names.

2.12 Key Concepts and Chapter Summary

This chapter introduced the data used throughout the book. The tables are fictional, but they represent common categories of sports business data: customers, demographics, schedules, inventory, transactions, survey responses, pricing research, and operations.

The most important lessons are practical:

  • Sports business data is relational. Useful analysis often requires connecting customers, games, seats, prices, and behavior.
  • Clean teaching data is not the same as clean production data. Real data needs validation, cleaning, and documentation.
  • Reproducible package data lets the book focus on analytical thinking instead of rebuilding source tables in every chapter.
  • Simulated data is useful when it preserves the structure of a real problem and makes assumptions visible.

In Chapter 3, we will begin exploring these data sets with summaries and graphics. That exploration step is essential. Before you model, forecast, segment, or recommend a strategy, you need to understand what the data is actually saying.