🎨 Transform Figma mockups into production-ready Dash apps. Join us November 10. Save your spot.

Chris Parmer

Chris Parmer

October 24, 2026

Bubble Charts - A Complete Guide

TL;DR

  • A bubble chart is a scatter plot whose markers are sized by a third number, so each bubble shows three values: x, y and area. Color can add a fourth.
  • Bubble charts are also known as bubble plots, and are similar to scatter plots and proportional symbol maps.
  • Use one when the size of each item changes how you read the relationship, such as countries by population, companies by market value or power plants by capacity.
  • Size bubbles by area, not diameter, or a value 6 times bigger looks 36 times bigger. px.scatter(size=...) does this for you, and with go.Scatter you set sizemode="area".
  • Any coding agent can make one with Plotly in a minute. In Python it's px.scatter(df, x=..., y=..., size=...).

What is a bubble chart?

Bubble charts look like this:

Bubble chart of 195 countries in 2024, life expectancy against GDP per person on a log axis, bubbles sized by population and colored by region. China (78.0 years) and the US (78.9) are at nearly the same height, while Nigeria is lowest at 54.6 years; a size legend shows 10M, 100M, 500M and 1.4B

Data: World Bank, World Development Indicators, 2024.

How do you read a bubble chart? In the example above, notice these things:

  • Each bubble is one country, placed left to right by its GDP per person and bottom to top by its life expectancy, so the cloud rising toward the top right shows that richer countries tend to live longer.
  • A bubble's area is the country's population, which is why India and China dominate the middle of the chart while more than half of the countries are small dots, and the size legend in the bottom right gives the scale.
  • The income axis is on a log scale, so each step along it multiplies income instead of adding to it, and the poorest countries on the left are spread out enough to read instead of piled against the axis.
  • Color shows the region, so the blue Sub-Saharan African countries stand out as a group in the lower left, with Nigeria the lowest bubble in the chart.
  • China and the United States sit at almost the same height, but the US bubble is far to the right with about three times the income per person, and the log axis makes that gap look smaller than it is.

It's often used for charts like these:

Bubble chart of 382 S&P 500 companies outside financials and real estate, revenue on a log x axis, net margin on y, bubble area by market cap and color by sector. Nvidia sits at 56% margin with the largest bubble, Walmart at 3% margin with $713 billion of revenue

Companies by size, profitability and value. Nvidia kept 56% of its $216 billion of revenue as profit and Walmart 3% of $713 billion, so Nvidia was worth 5.1 times as much at the end of 2025. Data: SEC EDGAR, Yahoo Finance.

Bubble chart of the 379 drugs with $100 million or more of Medicare Part D spending in 2024, patients on a log x axis, spending per claim on a log y axis, bubble area by spending. The eight GLP-1 drugs in blue cluster near $1,200 per claim with up to 2 million patients; Eliquis is the largest bubble at $20.8 billion

Drugs by patients, price and total spending. Eight GLP-1 drugs cost Medicare Part D $27.7 billion in 2024, 9.6% of its $289 billion drug bill, and they sit together at $1,100 to $1,500 per claim. Data: CMS Medicare Part D Spending by Drug, 2024 (before rebates).

Bubble chart of 195 countries in 2024, low-carbon share of electricity against generation per person on a log axis, bubbles sized by total generation and colored by the largest low-carbon source. France is at 95% mostly from nuclear, China is the largest bubble at 38%, and 68 mostly fossil countries are gray

Countries by how they make electricity. France makes 95% of its electricity without fossil fuels, mostly from nuclear, while the generation-weighted figure across 195 countries is 41%. Data: Our World in Data, from Ember and the Energy Institute.

A short history

Circles sized by quantity started on maps as proportional symbols, and in 1971 the cartographer James Flannery found that readers underestimate the larger of two circles (Flannery 1971, doi:10.3138/J647-1776-745H-3667). The bubble chart became famous in 2006, when Hans Rosling showed his animated Gapminder chart of income and life expectancy in the TED talk The best stats you've ever seen. Here is that idea redrawn with Plotly on World Bank data for 2006 (not Gapminder's own data):

A redraw of Rosling's Gapminder chart with Plotly: 194 countries in 2006, life expectancy against GDP per person on a log axis, bubbles sized by population and colored by region, with the year 2006 in large gray type in the corner. China and India are the two largest bubbles and Nigeria sits lowest of the labeled countries

Google bought Rosling's software, Trendalyzer, in 2007, and "motion charts" spread from there. Example 1 below is a static version of the same chart for 1990, 2007 and 2024.

Alternatives to a bubble chart

Here's the same table of the 15 most populous countries drawn six ways. Some are better for the question, some worse, some just different:

The 15 most populous countries in 2024 drawn six ways: a bubble chart of GDP per person, life expectancy and population; a scatter plot with population as color; a sorted bar chart of population from India at 1,451M to DR Congo at 109M; a bubble map; packed bubbles; and a treemap of population
  • Scatter plot with population as color (worse for size, better for overlap). Every point is the same size, so nothing hides anything else, but people can't rank countries by shade. Use it when the items are similar in size or there are too many to size.
  • Sorted bar chart (better for precise comparisons). People judge length on a shared baseline more accurately than area, as Cleveland and McGill (1984) measured, so use bars when one number matters most. You lose the other two numbers.
  • Bubble map (different). The same bubbles at each country's location, which answers where instead of how two numbers relate. Example 6 below is one.
  • Packed bubbles (worse). Circles packed together without axes show only size, and less precisely than the bar chart next to it. We drew these with Plotly as circle shapes, after placing them with a short packing function.
  • Treemap (works too, for sizes only). Shows parts of a whole and fits hundreds of items. See our treemap guide.

How to make a bubble chart

With AI

ChatGPT, Claude Code, GitHub Copilot, Cursor and other AI tools are excellent at writing Plotly code. Name Plotly in the prompt, say what x, y, size and color mean, and list the checks you want:

Make a plotly bubble chart from plants.csv (columns: plant, fuel, capacity_mw, age_years, capacity_factor).
- x = age_years, y = capacity_factor (%), bubble area = capacity_mw, color = fuel.
- go.Scatter with sizemode="area" and sizeref = 2 * max(capacity_mw) / 55**2; sizemin=2.5.
- Sort by capacity descending so small plants draw on top; opacity 0.55, white 0.6px outline.
- Put the capacity-weighted average for each fuel in its legend entry.
- Add a size legend with reference bubbles for 100, 1,000 and 4,000 MW.
- A title that states the takeaway with a real number.
- Check: print the pixel diameter of the largest and smallest bubble.
- Save it as bubbles.html and as a PNG at 2x scale.

With Plotly Studio

Plotly Studio is a desktop app where you point an AI at a dataset and it builds charts and a Dash app around your questions. Ask for a bubble chart the same way:

Show a bubble chart of countries with GDP per person on a log x axis, life expectancy on y, bubble area by population and color by region. Label the ten most populous countries, add a size legend, and add a year slider from 1990 to 2024.

Make a bubble chart in Python or JavaScript

This is the code AI tools write under the hood. In Python, Plotly Express sizes bubbles by area and sets the scale from size_max, the diameter in pixels of the largest bubble:

import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.scatter(df, x="gdpPercap", y="lifeExp", size="pop", color="continent",
hover_name="country", log_x=True, size_max=60)
fig.show()

With go.Scatter, set the sizing yourself, because its default sizemode is diameter:

import plotly.graph_objects as go
max_px = 60
fig = go.Figure(go.Scatter(
x=df.gdpPercap, y=df.lifeExp, mode="markers", text=df.country,
marker=dict(size=df["pop"], sizemode="area", sizeref=2 * df["pop"].max() / max_px**2,
sizemin=3, opacity=0.8, line=dict(color="white", width=1))))
fig.update_xaxes(type="log")

This helper adds a size legend to a Plotly bubble chart as reference bubbles on an inset axis whose x range is in pixels:

def add_size_legend(fig, values, labels, sizeref, title="", x=(0.78, 0.99), y=(0.05, 0.17), gap=26):
diam = [(v / sizeref) ** 0.5 for v in values] # area mode: diameter = sqrt(value / sizeref)
xs, pos = [], 0
for d in diam:
xs.append(pos + d / 2)
pos += d + gap
fig.add_trace(go.Scatter(x=xs, y=[0] * len(values), xaxis="x2", yaxis="y2", text=labels,
mode="markers+text", textposition="bottom center", hoverinfo="skip", showlegend=False,
marker=dict(size=values, sizemode="area", sizeref=sizeref, color="rgba(0,0,0,0)",
line=dict(color="gray", width=1))))
fig.update_layout(xaxis2=dict(domain=x, range=[-gap, pos], visible=False, anchor="y2"),
yaxis2=dict(domain=y, range=[-1, 1], visible=False, anchor="x2"))
fig.add_annotation(text=title, xref="x2 domain", yref="y2 domain", x=0.5, y=1.3, showarrow=False)
return fig
add_size_legend(fig, [1e7, 1e8, 1e9], ["10M", "100M", "1B"], 2 * df["pop"].max() / max_px**2, "Population")

In JavaScript, plotly.js takes the same marker options on a scatter trace:

<div id="chart"></div>
<script src="https://cdn.plot.ly/plotly-4.1.1.min.js"></script>
<script>
const pop = [1408975000, 1450936000, 340004000, 232679000];
Plotly.newPlot("chart", [{
type: "scatter",
mode: "markers+text",
x: [23842, 9416, 75698, 7994],
y: [78.0, 72.2, 78.9, 54.6],
text: ["China", "India", "US", "Nigeria"],
textposition: "top center",
marker: {size: pop, sizemode: "area", sizeref: 2 * Math.max(...pop) / 60 ** 2,
opacity: 0.8, line: {color: "white", width: 1}}
}], {xaxis: {type: "log", title: {text: "GDP per person"}},
yaxis: {title: {text: "Life expectancy (years)"}}});
</script>

Share it

Every Plotly chart has a Share chart button (the cloud icon in its toolbar) that uploads it to Plotly Cloud and gives you a link. Only you can see it until you change its visibility.

For a live app, put the chart in a Dash app and publish it with the Plotly Cloud CLI. Coding agents can run these commands for you:

pip install "dash[cloud]"
plotly user login
plotly app publish

If your data has to stay in your own cloud or data center, publish to Dash Enterprise instead.

Dos and don'ts

Both charts below show the same 195 countries in the same region colors. The differences are the mistakes that most often make a bubble chart misleading.

Don't size bubbles by diameter, put income on a linear axis, draw the biggest bubbles last over the small ones, or leave raw column names as axis titles. Sized by diameter, China's 6.1 times more people than Nigeria look 37 times bigger, and on the linear axis 77% of the countries pile into the left third of the chart:

A bubble chart of 195 countries' income, life expectancy and population in the region colors, with plotly defaults that mislead: bubbles sized by diameter, so China and India are huge and most countries are specks, a linear income axis that crowds most countries to the left, and axis titles gdp_per_capita and life_expectancy

Do size by area with sizemode="area", use a log axis for income, draw big bubbles first with thin white outlines, label the countries the story needs and add a size legend:

The same bubble chart done well: bubbles sized by area on a log income axis with dollar ticks, small countries drawn on top of large ones with white outlines, labels on China, India, the US, Nigeria and nine other countries, and a size legend for 10M, 100M, 500M and 1.4B people

Use color for one other thing

Position and area already show three numbers, so color is free for one more. Here are four options on the same data.

By category. Region is a good default, because it groups countries that sit together on the chart.

The health and wealth bubble chart colored by World Bank region

Highlight one story. Sub-Saharan Africa's 46 countries are the only color, and everything else is gray.

The health and wealth bubble chart with the 46 Sub-Saharan African countries in blue and every other country in light gray, with Nigeria, Ethiopia and South Africa labeled

A fourth number. Here it's the years of life expectancy gained since 2000, for the 189 countries with data for both years, on a scale capped at 0 and 20.

The health and wealth bubble chart for 189 countries shaded from cream to purple by years of life expectancy gained between 2000 and 2024, with a colorbar from 0 to 20 years

Hollow bubbles. Colored outlines with no fill keep every bubble visible where they overlap.

The health and wealth bubble chart drawn as hollow circles with outlines in each region's color

Checklist

Do

  • Size by area, with sizeref = 2 * max(size) / max_px**2 and a largest bubble of 40 to 90 pixels.
  • Draw big bubbles first, with a thin white outline and 0.6 to 0.85 opacity.
  • Use a log axis when a variable spans more than about 20 times.
  • Add a size legend and label the bubbles the story needs.
  • Write the takeaway in the title and say what size and color mean in the subtitle.

Don't

  • Size by diameter, because a value 6 times bigger then looks 36 times bigger.
  • Size by rates or averages, because area reads as a total.
  • Rely on an animation in a static report; use small multiples or connected bubbles.
  • Let bubbles at the edge get cut off; widen the axis range or set cliponaxis=False.

Options to customize

These are the go.Scatter (and px.scatter) options that matter most for bubble charts. The full list is in the Plotly bubble chart documentation.

  1. size, size_max (px only): the column for bubble size, and the diameter in pixels of the largest bubble (default 20; 40 to 90 is usually better).
  2. marker.size: the bubble size, one value per point.
  3. marker.sizemode: "diameter" (the default) or "area". Use "area" for quantities.
  4. marker.sizeref: the scale. In area mode the diameter is sqrt(value / sizeref), so set it to 2 * max(size) / max_px**2.
  5. marker.sizemin: a minimum diameter in pixels, so tiny values stay visible (2 to 4 px).
  6. marker.opacity, marker.line: transparency and outline, 0.55 to 0.85 with a white line when bubbles overlap.
  7. marker.color, colorscale, cmin, cmax, colorbar: a category or a fourth number as color.
  8. mode="markers+text", textposition: labels on the bubbles; a list sets one position per point, for up to about 50 bubbles.
  9. layout.annotations (showarrow, ax, ay): labels on leader lines for crowded clusters. On a log axis, pass log10(x).
  10. xaxis.type="log": a log axis for skewed variables such as income or revenue.
  11. legend.itemsizing="constant": keeps legend markers one size instead of copying a random bubble.
  12. animation_frame, animation_group (px only): frames and identity for a Gapminder-style animation.
  13. facet_col, facet_row (px only): small multiples that share one size scale.
  14. go.Scattergl: the WebGL renderer, with the same marker options, for more than about 10,000 bubbles.
  15. go.Scattergeo, go.Scattermap: bubbles on a map.
  16. cliponaxis: set to False to draw bubbles past the edge of the plot area.

More bubble chart examples

Ten more bubble charts built from real public data, each with its source, the technique that makes it work and a prompt to rebuild it.

1. Thirty-four years of health and wealth, as small multiples

Three bubble charts side by side for 1990, 2007 and 2024 with the same axes and size scale. The population-weighted world average life expectancy, drawn as a dotted line, rises from 65.1 to 69.8 to 73.5 years

On a page, the Gapminder animation becomes three frames side by side. The population-weighted world average rose from 65.1 years in 1990 to 73.5 in 2024, and China and India moved up and to the right. Data: World Bank, World Development Indicators.

Tip: px.scatter(facet_col="year") uses one size scale for every panel, so a bubble's size means the same thing in each year.

Get the data from the World Bank World Development Indicators (life expectancy, GDP per capita
PPP and population by country and year) and shape it into a table
(columns: country, region, year, gdp_per_capita, life_expectancy, population).
Build plotly bubble small multiples for 1990, 2007 and 2024 with px.scatter(facet_col="year"),
log x, the same y range in each panel, area = population, color = region. Draw the
population-weighted average life expectancy as a dotted line with its value in each panel.

2. What gets reported for eight big drugs

Bubble matrix of eight drugs by 16 adverse reactions, bubble area = share of each drug's FDA adverse event reports from 2020 to 2025. Each drug's largest share is dark blue and labeled: semaglutide nausea 15%, dupilumab pruritus 12%, pembrolizumab malignant neoplasm progression 10%, adalimumab pain 10%, lenalidomide diarrhoea 10%

With categories on both axes, a bubble chart becomes a matrix. Nausea is in 15.1% of semaglutide's 58,221 reports, the largest share in the grid, and each drug has its own signature. Reports are not proven side effects. Data: FDA FAERS via openFDA, 2020-2025.

Tip: a light gray ring behind every cell, so an empty cell reads as "close to zero" instead of missing.

Get the data from the FDA Adverse Event Reporting System through the openFDA drug event API
(total reports and the top 100 reaction terms per drug, received 2020-2025) and shape it into a
table (columns: drug, brand, total_reports, reaction, reports, share).
Build a plotly bubble matrix: x = drug (sorted by reports), y = reaction (each drug's top 3,
dropping off-label use and other administrative terms), area = share. Draw each drug's largest
cell darker with its percentage, and a gray ring behind every cell.

Another way to see it: a heatmap of the same cells prints every share, but cells under 1% and missing cells look alike.

Heatmap of the same eight drugs by 16 reactions with the share printed in each cell, from 15.1% for semaglutide nausea down to blank cells

3. Government debt and deficits

Bubble chart of the 76 economies with GDP over $100 billion, government debt against budget balance with deficits to the right, bubbles sized by GDP. The shaded quadrant over both EU thresholds (debt over 60%, deficit over 3%) holds 23 economies in red, including the US, China, India, France, the UK and Brazil

Twenty-three of the 76 largest economies, 62% of world GDP, have debt over 60% of GDP and a deficit over 3%, the EU's two fiscal thresholds. Japan's debt is the highest of the 76 at 205% of GDP, with a deficit of 0.7%. Data: IMF World Economic Outlook, October 2026.

Tip: reference lines and a shaded quadrant with add_vline, add_hline and add_shape(type="rect", layer="below") turn the chart into a rule check.

Get the data from the IMF World Economic Outlook (DataMapper API: GGXWDG_NGDP, GGXCNL_NGDP and
NGDPD for 2025) and shape it into a table (columns: country, debt_pct_gdp, balance_pct_gdp, gdp_bn).
Build a plotly bubble chart for economies with GDP of $100B+: x = balance (reversed, deficits right),
y = debt, area = GDP. Dotted lines at 60% debt and -3% balance, the quadrant over both shaded,
those countries in red and the rest gray. Add a size legend.

4. US metros: people moved, prices rose everywhere

Bubble chart of the 98 largest US metro areas, population change 2020-2025 against home value change since January 2020, bubbles sized by population and colored by the 2020 typical home value. Austin grew 15% with values up 27%; Syracuse shrank with values up 79%; the correlation is 0.01

Population growth didn't predict price growth: the correlation across the 98 largest metros is 0.01. Austin grew 15% and its home values rose 27%, while Syracuse lost people and rose 79%. Data: US Census Bureau population estimates, Zillow Home Value Index.

Tip: a continuous colorscale on the log of the 2020 price adds a fourth variable, and shows that cheaper metros rose more.

Get the data from the US Census Bureau's Vintage 2025 metro population estimates
(cbsa-est2025-alldata.csv) and the Zillow Home Value Index by metro (ZHVI, all homes, middle tier)
and shape it into a table (columns: metro, population_2025, pop_change_pct,
zhvi_2020_01, zhvi_latest, home_value_change_pct) for the 100 largest metros.
Build a plotly bubble chart: x = pop_change_pct, y = home_value_change_pct, area = population,
color = log10(zhvi_2020_01) with a colorbar in dollars. Median lines on both axes; label the extremes.

5. How hard US power plants ran in 2025

Bubble chart of 2,386 US power plants of 100 MW or more, age against 2025 capacity factor, bubbles sized by capacity and colored by fuel. Nuclear plants form a purple band near 90% at 35 to 55 years old; solar and wind cluster young and low; gas spreads across all heights

US nuclear plants are 42 years old on average and ran at 88% of capacity in 2025, about twice the rate of coal (45%). Gas plants spread from almost idle peakers to baseload. Data: EIA-860 and EIA-923, 2025.

Tip: with 2,386 overlapping bubbles, 0.55 opacity, a thin white outline and drawing the most numerous fuel first keep every cloud visible.

Get the data from EIA-860 2025 (generators and plant locations) and EIA-923 2025 (net generation
by plant) and shape it into a table (columns: plant_name, state, fuel, capacity_mw, age_years,
capacity_factor), one row per plant, battery and pumped storage left out.
Build a plotly bubble chart for plants of 100 MW+ that ran all of 2025: x = age_years,
y = capacity_factor (%), area = capacity_mw, one trace per fuel. Opacity 0.55, white outlines,
big bubbles first. Put each fuel's capacity-weighted average in its legend entry.

Another way to see it: box plots with every plant as a dot show each fuel's spread directly: 47 of the 54 nuclear plants ran above 80%.

Box plots of 2025 capacity factor by fuel with one dot per plant and a black tick at the capacity-weighted average: nuclear 88%, coal 45%, gas 37%, hydro 35%, wind 34%, solar 25%, oil and other 13%

6. Every US power plant on a map

Bubble map of 13,832 US power plants at the end of 2025, sized by capacity and colored by main fuel: gas along the Gulf Coast and East, wind across the Plains, solar in California, Texas and the Southeast, hydro in the Pacific Northwest

Natural gas is 44% of the 1,311 GW of US plant capacity, but solar has the most plants (7,269), mostly small. The map shows the geography the numbers hide: wind on the Plains, hydro on the Columbia River. Data: EIA-860, 2025.

Tip: one go.Scattergeo trace sorted big to small, plus empty traces (lon=[None]) per fuel for the legend.

Get the data from EIA-860 2025 (operable generators and plant latitude and longitude) and shape
it into a table (columns: plant_name, state, fuel, capacity_mw, latitude, longitude).
Build a plotly bubble map with go.Scattergeo (scope "usa", albers usa projection): area = capacity_mw
(sizemode area, largest 40 px), color = fuel, opacity 0.6, thin white outline, sorted big to small.
Legend entries with each fuel's share of capacity and number of plants.

7. The paths of the biggest emitters

Connected bubble chart of the seven biggest CO2 emitters from 1990 to 2024, CO2 per person against GDP per person on a log axis, with bubbles every ten years sized by total emissions. The US path falls from 20.3 to 14.2 tonnes per person while income rises; China's rises from 2.2 to 8.7 tonnes

US emissions per person fell 30% since 1990 as GDP per person rose 71%, while China's rose 4.0 times. A connected bubble chart shows a whole path at once, which an animation can only show one frame at a time. Data: Global Carbon Budget via Our World in Data; World Bank.

Tip: a thin line through every year, bubbles only every ten years, and the country name at the end of the path instead of a legend.

Get the data from Our World in Data's CO2 dataset (github.com/owid/co2-data, Global Carbon Budget)
and World Bank GDP per capita PPP and shape it into a table (columns: country, year, co2_mt,
co2_per_capita, gdp_per_capita) for the seven biggest emitters, 1990-2024.
Build a plotly connected bubble chart: one line per country through all years, bubbles at 1990,
2000, 2010, 2020 and 2024 sized by co2_mt, x = gdp_per_capita (log), y = co2_per_capita.
Outline the 2024 bubble and write the country name next to it.

8. Every magnitude 6+ earthquake since 2000

Bubble timeline of 3,874 magnitude 6+ earthquakes from 2000 to 2025, date on x and depth on a reversed y axis, bubbles sized by energy released. Four giant bubbles (Sumatra 2004, Maule 2010, Tohoku 2011 and Kamchatka 2025) dominate a band of small shallow quakes

Sized by energy instead of magnitude, four earthquakes released 49% of the energy of all 3,874 since 2000. Each magnitude step is about 32 times more energy, which a magnitude-sized bubble would hide. Data: USGS ANSS Comprehensive Earthquake Catalog.

Tip: size by the physical quantity (energy) rather than a log-scale index (magnitude), and set sizemin so small events still show.

Get the data from the USGS Comprehensive Earthquake Catalog (FDSN event web service,
magnitude 6+, 2000-2025) and shape it into a table (columns: time, latitude, longitude,
depth_km, mag, place, energy_joules = 10**(1.5*mag + 4.8)).
Build a plotly bubble timeline: x = time, y = depth (reversed), area = energy_joules
(largest bubble 110 px, sizemin 2.5), one color at 0.5 opacity. Label the four largest on leader lines.

Another way to see it: a bar chart of energy by year shows that 2004, 2010, 2011 and 2025 hold 54% of the total, but loses the individual quakes and their depth.

Bar chart of the share of earthquake energy released each year from 2000 to 2025, with 2004 and 2011 at 19% each and 2010 and 2025 at 8%

9. NFL quarterbacks in 2025

Bubble chart of 38 NFL quarterbacks with 200+ dropbacks in the 2025 regular season, completion percentage over expected against expected points added per dropback, bubbles sized by dropbacks, every bubble labeled. Drake Maye is alone in the top right at +0.32 EPA and +10.8 CPOE

Drake Maye led the 38 starters in both expected points added per dropback (+0.32) and completion percentage over expected (+10.8). Bubble area adds the volume behind each rate. Data: nflverse play-by-play, nflfastR models, 2025 regular season.

Tip: with 38 bubbles every one can carry its name, using mode="markers+text" and a per-point textposition list to fix collisions.

Get the data from the nflverse 2025 play-by-play file (nflverse-data releases, nflfastR EPA and CPOE)
and shape it into a table (columns: name, team, dropbacks, epa_per_dropback, cpoe) for regular-season
quarterbacks with 200+ dropbacks.
Build a plotly bubble chart: x = cpoe, y = epa_per_dropback, area = dropbacks, names on every
bubble, league-average lines and a quadrant caption in each corner. Highlight the leader.

10. Who runs Phase 3 trials, and how many stop early

Bubble chart of the 30 drug companies with the most Phase 3 trials started 2015-2024, number of trials against share stopped early, bubbles sized by participants and shaded by share of trials in cancer. Biogen is highest at 40% stopped; Novartis has the most trials (260); the industry average is 14%

Biogen stopped 40% of the 47 Phase 3 trials it started from 2015 to 2024, the highest rate among the 30 busiest industry sponsors, against 14% for all 9,367 industry trials. Trials stop for many reasons, including business decisions. Data: ClinicalTrials.gov.

Tip: a single-hue continuous color for a share (cancer trials) adds a fourth variable without competing with the size.

Get the data from the ClinicalTrials.gov API v2 (interventional Phase 3 trials with an industry lead
sponsor, started 2015-2024) and shape it into a table per company (columns: company, trials,
median_enrollment, total_enrollment, share_oncology, share_stopped).
Build a plotly bubble chart for the 30 biggest sponsors: x = trials, y = share_stopped (%),
area = total_enrollment, color = share_oncology on a light-to-dark blue scale. A dotted line at the
industry-wide rate; labels on every bubble, on leader lines where they collide.

FAQ

bluesly
x
instagram
youtube
medium
fcaebook

Product

© 2026
Plotly. All rights reserved.
Cookie Preferences
aicpa/soc2