// Use this to read a CSV from a URL > which were stored through the targets pipeline
ahrq_data = {
const response = await fetch("https://files.grant-witness.us/ahrq_overview_breakdown.csv");
const text = await response.text();
return d3.csvParse(text, d3.autoType);
}Grantmaking over Time for AHRQ
The following interactive funding curves are adapted from Jeremy Berg. These visualizations demonstrate differences in the cumulative number of awards and cumulative amount obligated across AHRQ awards from fiscal year (FY) 2021 to 2026.
For more discussions about these funding curves, read the related articles published by Nature and Science.
For more information about how we created these graphs, see our Methods section.
Overview
Click or move your cursor along the graph to compare data across FY 2021 to 2026.
The tooltip will provide information about a given day compared to previous fiscal years. The black dotted line represents today’s date, while the bolded orange line demonstrates the trends for FY 2025 and the bolded red line demonstrates the current trend for FY 2026, with the remaining lines demonstrating trends from FY 2021-2024. The FY runs from October 1st of the previous year through September 30th of the current year.
// Define a helper to normalize today's date to the year 2016/2017
todayNormalized = {
const now = new Date();
const month = now.getMonth(); // 0 = Jan, 9 = Oct
const year = month >= 9 ? 2016 : 2017;
// Create the date and force the specific year
const d = new Date(now);
d.setFullYear(year);
return d;
}usdFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
// For the "Overview" graphs
// 1. Cumulative 2021 - 2024 Values
ahrq_cum_award_avg = {
// Filter by years
const filtered = ahrq_data.filter(d =>
[2021, 2022, 2023, 2024].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
// Summarize (Mean) and Round
const mean = latestEntries.reduce((sum, d) => sum + d.Cum_Award, 0) / latestEntries.length;
return Math.round(mean);
}
ahrq_cum_cost_avg = {
// 1. Filter by years
const filtered = ahrq_data.filter(d =>
[2021, 2022, 2023, 2024].includes(d.Original_Year)
);
// 2. Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
// 3. Summarize (Mean) and Round
const mean = latestEntries.reduce((sum, d) => sum + d.Cum_Cost, 0) / latestEntries.length;
return Math.round(mean);
}
ahrq_formatted_cum_cost_avg = usdFormatter.format(ahrq_cum_cost_avg / 1e6) + "M"
// 2. Cumulative 2025 values
ahrq_cum_award_2025 = {
// Filter by years
const filtered = ahrq_data.filter(d =>
[2025].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
return latestEntries[0].Cum_Award;
}
ahrq_cum_cost_2025 = {
// Filter by years
const filtered = ahrq_data.filter(d =>
[2025].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
return latestEntries[0].Cum_Cost;
}
ahrq_formatted_cum_cost_2025 = usdFormatter.format(ahrq_cum_cost_2025 / 1e6) + "M"Plot.plot({
marginLeft: 50,
width: 928,
// Add labels to the axes here
x: {
label: "Award Date",
tickFormat: "%b", // Shows 'Jan', 'Feb', etc. Use "%B" for full names
},
y: {
label: "Number of Awards",
grid: true // Optional: makes it easier to read values
},
// specify colors here for outstanding lines
color: {
domain: [2021, 2022, 2023, 2024, 2025, 2026], // The specific values in your data
range: ["#6605a6", "#0b72b3", "#2A9D8F", "#e96ab6", "#F4A261", "#E63946"] // The specific colors you want (e.g., Red and Blue)
},
marks: [
// Filter for years that ARE NOT 2025 or 2026 (as an example)
Plot.lineY(ahrq_data.filter(d => ![2015, 2016, 2017, 2018, 2019, 2020].includes(d.Original_Year)), {
x: d => new Date(d.NoA_date),
y: "Cum_Award",
stroke: "Original_Year", // This will now use the color scale
strokeWidth: 3 // Optional: make them pop a bit more
}),
Plot.ruleX(
ahrq_data,
Plot.pointerX(Plot.binX({}, { x: "NoA_date", thresholds: 1000, insetTop: 20 }))
),
// The Current Day line
Plot.ruleX([todayNormalized], {
stroke: "black",
strokeDasharray: "4", // 4px dash, 4px gap
strokeOpacity: 1 // Makes it look a bit more subtle
}),
Plot.text([todayNormalized], {
x: d => d,
text: d => "Today",
textAnchor: "start",
fontSize: 20,
dx: 10,
dy: -50,
fill: "black"
}),
//Tooltip code
Plot.tip(
ahrq_data,
Plot.pointerX(
Plot.binX(
{title: (v) => v},
{
x: "NoA_date",
thresholds: 1000,
render(index, scales, values, dimensions, context) {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const ahrq_data = values.title[i];
g.attr(
"transform",
`translate(${Math.min(
values.x1[i],
dimensions.width - dimensions.marginRight - 200
)}, 20)`
).append(() =>
Plot.plot({
marginTop: 20,
height: 150,
width: 150,
axis: null,
y: {domain: scales.scales.color.domain},
marks: [
Plot.frame({ fill: "white", stroke: "currentColor" }),
Plot.dot(ahrq_data, {
y: "Original_Year",
fill: (d) => scales.color(d.Original_Year),
r: 5,
frameAnchor: "left",
symbol: "square2",
dx: 6
}),
Plot.text(ahrq_data, {
y: "Original_Year",
text: (d) => `FY ${d.Original_Year} :`,
fontSize: 16,
frameAnchor: "left",
dx: 13
}),
Plot.text(ahrq_data, {
y: "Original_Year",
text: "Cum_Award",
fontSize: 16,
frameAnchor: "right",
dx: -8
}),
Plot.text(ahrq_data, {
frameAnchor: "top-left",
dy: -20,
text: (d) => {
// 1. Convert the string "2026-03-30" into a JS Date object
const dateObj = new Date(d.NoA_date);
// 2. Format it to "Month Day" (e.g., "March 30")
return dateObj.toLocaleString("en-US", {
month: "long",
day: "numeric"
});
},
fontSize: 16,
fontWeight: "bold"
})
]
})
);
}
return g.node();
}
}
)
)
),
Plot.ruleY([0])
]
})Plot.plot({
marginLeft: 50,
width: 928,
// Add labels to the axes here
x: {
label: "Award Date",
tickFormat: "%b", // Shows 'Jan', 'Feb', etc. Use "%B" for full names
},
y: {
label: "Fraction of Awards",
grid: true // Optional: makes it easier to read values
},
// specify colors here for outstanding lines
color: {
domain: [2021, 2022, 2023, 2024, 2025, 2026], // The specific values in your data
range: ["#6605a6", "#0b72b3", "#2A9D8F", "#e96ab6", "#F4A261", "#E63946"] // The specific colors you want (e.g., Red and Blue)
},
marks: [
// Filter for years that ARE NOT 2025 or 2026 (as an example)
Plot.lineY(ahrq_data.filter(d => ![2015, 2016, 2017, 2018, 2019, 2020].includes(d.Original_Year)), {
x: d => new Date(d.NoA_date),
y: "Cum_Award_Norm",
stroke: "Original_Year", // This will now use the color scale
strokeWidth: 3 // Optional: make them pop a bit more
}),
Plot.ruleX(
ahrq_data,
Plot.pointerX(Plot.binX({}, { x: "NoA_date", thresholds: 1000, insetTop: 20 }))
),
// The Current Day line
Plot.ruleX([todayNormalized], {
stroke: "black",
strokeDasharray: "4", // 4px dash, 4px gap
strokeOpacity: 1 // Makes it look a bit more subtle
}),
Plot.text([todayNormalized], {
x: d => d,
text: d => "Today",
textAnchor: "start",
fontSize: 20,
dx: 10,
dy: -50,
fill: "black"
}),
//Tooltip code
Plot.tip(
ahrq_data,
Plot.pointerX(
Plot.binX(
{title: (v) => v},
{
x: "NoA_date",
thresholds: 1000,
render(index, scales, values, dimensions, context) {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const ahrq_data = values.title[i];
g.attr(
"transform",
`translate(${Math.min(
values.x1[i],
dimensions.width - dimensions.marginRight - 200
)}, 20)`
).append(() =>
Plot.plot({
marginTop: 20,
height: 150,
width: 150,
axis: null,
y: {domain: scales.scales.color.domain},
marks: [
Plot.frame({ fill: "white", stroke: "currentColor" }),
Plot.dot(ahrq_data, {
y: "Original_Year",
fill: (d) => scales.color(d.Original_Year),
r: 5,
frameAnchor: "left",
symbol: "square2",
dx: 6
}),
Plot.text(ahrq_data, {
y: "Original_Year",
text: (d) => `FY ${d.Original_Year} :`,
fontSize: 16,
frameAnchor: "left",
dx: 13
}),
Plot.text(ahrq_data, {
y: "Original_Year",
text: "Cum_Award_Norm",
fontSize: 16,
frameAnchor: "right",
dx: -8
}),
Plot.text([ahrq_data[0]], {
frameAnchor: "top-left",
dy: -20,
text: (d) => {
// 1. Convert the string "2026-03-30" into a JS Date object
const dateObj = new Date(d.NoA_date);
// 2. Format it to "Month Day" (e.g., "March 30")
return dateObj.toLocaleString("en-US", {
month: "long",
day: "numeric"
});
},
fontSize: 16,
fontWeight: "bold"
})
]
})
);
}
return g.node();
}
}
)
)
),
Plot.ruleY([0])
]
})Plot.plot({
marginLeft: 50,
width: 928,
// Add labels to the axes here
x: {
label: "Award Date",
tickFormat: "%b", // Shows 'Jan', 'Feb', etc. Use "%B" for full names
},
y: {
label: "Obligations",
tickFormat: d => `$${(d / 1e9).toFixed(1)}B`,
grid: true // Optional: makes it easier to read values
},
// specify colors here for outstanding lines
color: {
domain: [2021, 2022, 2023, 2024, 2025, 2026], // The specific values in your data
range: ["#6605a6", "#0b72b3", "#2A9D8F", "#e96ab6", "#F4A261", "#E63946"] // The specific colors you want (e.g., Red and Blue)
},
marks: [
// Filter for years that ARE NOT 2025 or 2026 (as an example)
Plot.lineY(ahrq_data.filter(d => ![2015, 2016, 2017, 2018, 2019, 2020].includes(d.Original_Year)), {
x: d => new Date(d.NoA_date),
y: "Cum_Cost",
stroke: "Original_Year", // This will now use the color scale
strokeWidth: 3 // Optional: make them pop a bit more
}),
Plot.ruleX(
ahrq_data,
Plot.pointerX(Plot.binX({}, { x: "NoA_date", thresholds: 1000, insetTop: 20 }))
),
// The Current Day line
Plot.ruleX([todayNormalized], {
stroke: "black",
strokeDasharray: "4", // 4px dash, 4px gap
strokeOpacity: 1 // Makes it look a bit more subtle
}),
Plot.text([todayNormalized], {
x: d => d,
text: d => "Today",
textAnchor: "start",
fontSize: 20,
dx: 10,
dy: -50,
fill: "black"
}),
//Tooltip code
Plot.tip(
ahrq_data,
Plot.pointerX(
Plot.binX(
{title: (v) => v},
{
x: "NoA_date",
thresholds: 1000,
render(index, scales, values, dimensions, context) {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const ahrq_data = values.title[i];
g.attr(
"transform",
`translate(${Math.min(
values.x1[i],
dimensions.width - dimensions.marginRight - 200
)}, 20)`
).append(() =>
Plot.plot({
marginTop: 20,
height: 150,
width: 150,
axis: null,
y: {domain: scales.scales.color.domain},
marks: [
Plot.frame({ fill: "white", stroke: "currentColor" }),
Plot.dot(ahrq_data, {
y: "Original_Year",
fill: (d) => scales.color(d.Original_Year),
r: 5,
frameAnchor: "left",
symbol: "square2",
dx: 6
}),
Plot.text(ahrq_data, {
y: "Original_Year",
text: (d) => `FY ${d.Original_Year} :`,
fontSize: 16,
frameAnchor: "left",
dx: 13
}),
Plot.text(ahrq_data, {
y: "Original_Year",
text: d => {
if (d.Cum_Cost >= 1000000000) {
// Format as Billions if 1,000,000,000 or greater
return `$${(d.Cum_Cost / 1000000000).toFixed(1)}B`;
} else {
// Format as Millions otherwise
return `$${(d.Cum_Cost / 1000000).toFixed(1)}M`;
}
},
fontSize: 16,
frameAnchor: "right",
dx: -8
}),
Plot.text(ahrq_data, {
frameAnchor: "top-left",
dy: -20,
text: (d) => {
// 1. Convert the string "2026-03-30" into a JS Date object
const dateObj = new Date(d.NoA_date);
// 2. Format it to "Month Day" (e.g., "March 30")
return dateObj.toLocaleString("en-US", {
month: "long",
day: "numeric"
});
},
fontSize: 16,
fontWeight: "bold"
})
]
})
);
}
return g.node();
}
}
)
)
),
Plot.ruleY([0])
]
})From these graphs, we can see that there was a significant lag in FY 2025 funding, which occurred during the spring and into the summer (March through August). This is consistent with reported disruptions to grant review and funding processes that occurred in 2025.
- Specifically, the total number of awards made in FY 2025 is .
- This total number is less than the average number of awards across FY 2021 - 2024, which was .
- Similarly, the total funding obligated across these awards was less in FY 2025, which was .
- This is compared to the average amount obligated across FY 2021 through 2024, which was .
By Award Type
The following graphs visualize funding across FY based on award types, comparing new and competitive renewal awards (Type 1, 2, 4, 9) with non-competitive renewal awards (Type 5, 6, 7, 8). More information about the different types of NIH grants can be found here.
- New applications are the first request for funding for a project that has not previously received that specific grant.
- Competitive Renewals are requests for continued funding after the original project period ends; these applications go through full peer review and must compete again for funding.
- In contrast, Non-Competitive Renewals provide continued funding for the next budget period within an already approved multi-year project and generally do not undergo full peer review, assuming satisfactory progress and compliance with administrative requirements.
ahrq_data_newcomp = {
const response = await fetch("https://files.grant-witness.us/ahrq_new_comp_breakdown.csv");
const text = await response.text();
return d3.csvParse(text, d3.autoType);
}ahrq_data_noncomp = {
const response = await fetch("https://files.grant-witness.us/ahrq_noncomp_breakdown.csv");
const text = await response.text();
return d3.csvParse(text, d3.autoType);
}ahrq_newcomp_award_avg = {
// Filter by years
const filtered = ahrq_data_newcomp.filter(d =>
[2021, 2022, 2023, 2024].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
// Summarize (Mean) and Round
const mean = latestEntries.reduce((sum, d) => sum + d.Cum_Award, 0) / latestEntries.length;
return Math.round(mean);
}
// 2. Cumulative 2025 values
ahrq_newcomp_award_2025 = {
// Filter by years
const filtered = ahrq_data_newcomp.filter(d =>
[2025].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
return latestEntries[0].Cum_Award;
}ahrq_noncomp_award_avg = {
// Filter by years
const filtered = ahrq_data_noncomp.filter(d =>
[2021, 2022, 2023, 2024].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
// Summarize (Mean) and Round
const mean = latestEntries.reduce((sum, d) => sum + d.Cum_Award, 0) / latestEntries.length;
return Math.round(mean);
}
// 2. Cumulative 2025 values
ahrq_noncomp_award_2025 = {
// Filter by years
const filtered = ahrq_data_noncomp.filter(d =>
[2025].includes(d.Original_Year)
);
// Group & Slice Max (Get the latest entry for each year)
// Reduce the array into a Map to keep only the entry with the max NoA_date per year
const latestEntries = Array.from(
filtered.reduce((acc, curr) => {
const existing = acc.get(curr.Original_Year);
if (!existing || new Date(curr.NoA_date) > new Date(existing.NoA_date)) {
acc.set(curr.Original_Year, curr);
}
return acc;
}, new Map()).values()
);
return latestEntries[0].Cum_Award;
}Plot.plot({
marginLeft: 50,
width: 928,
// Add labels to the axes here
x: {
label: "Award Date",
tickFormat: "%b", // Shows 'Jan', 'Feb', etc. Use "%B" for full names
},
y: {
label: "Number of Awards",
grid: true // Optional: makes it easier to read values
},
// specify colors here for outstanding lines
color: {
domain: [2021, 2022, 2023, 2024, 2025, 2026], // The specific values in your data
range: ["#6605a6", "#0b72b3", "#2A9D8F", "#e96ab6", "#F4A261", "#E63946"] // The specific colors you want (e.g., Red and Blue)
},
marks: [
// Filter for years that ARE NOT 2025 or 2026 (as an example)
Plot.lineY(ahrq_data_newcomp.filter(d => ![2015, 2016, 2017, 2018, 2019, 2020].includes(d.Original_Year)), {
x: d => new Date(d.NoA_date),
y: "Cum_Award",
stroke: "Original_Year", // This will now use the color scale
strokeWidth: 3 // Optional: make them pop a bit more
}),
Plot.ruleX(
ahrq_data_newcomp,
Plot.pointerX(Plot.binX({}, { x: "NoA_date", thresholds: 1000, insetTop: 20 }))
),
// The Current Day line
Plot.ruleX([todayNormalized], {
stroke: "black",
strokeDasharray: "4", // 4px dash, 4px gap
strokeOpacity: 1 // Makes it look a bit more subtle
}),
Plot.text([todayNormalized], {
x: d => d,
text: d => "Today",
textAnchor: "start",
fontSize: 20,
dx: 10,
dy: -50,
fill: "black"
}),
//Tooltip code
Plot.tip(
ahrq_data_newcomp,
Plot.pointerX(
Plot.binX(
{title: (v) => v},
{
x: "NoA_date",
thresholds: 1000,
render(index, scales, values, dimensions, context) {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const ahrq_data_newcomp = values.title[i];
g.attr(
"transform",
`translate(${Math.min(
values.x1[i],
dimensions.width - dimensions.marginRight - 200
)}, 20)`
).append(() =>
Plot.plot({
marginTop: 20,
height: 150,
width: 150,
axis: null,
y: {domain: scales.scales.color.domain},
marks: [
Plot.frame({ fill: "white", stroke: "currentColor" }),
Plot.dot(ahrq_data_newcomp, {
y: "Original_Year",
fill: (d) => scales.color(d.Original_Year),
r: 5,
frameAnchor: "left",
symbol: "square2",
dx: 6
}),
Plot.text(ahrq_data_newcomp, {
y: "Original_Year",
text: (d) => `FY ${d.Original_Year} :`,
fontSize: 16,
frameAnchor: "left",
dx: 13
}),
Plot.text(ahrq_data_newcomp, {
y: "Original_Year",
text: "Cum_Award",
fontSize: 16,
frameAnchor: "right",
dx: -8
}),
Plot.text(ahrq_data_newcomp, {
frameAnchor: "top-left",
dy: -20,
text: (d) => {
// 1. Convert the string "2026-03-30" into a JS Date object
const dateObj = new Date(d.NoA_date);
// 2. Format it to "Month Day" (e.g., "March 30")
return dateObj.toLocaleString("en-US", {
month: "long",
day: "numeric"
});
},
fontSize: 16,
fontWeight: "bold"
})
]
})
);
}
return g.node();
}
}
)
)
),
Plot.ruleY([0])
]
})Plot.plot({
marginLeft: 50,
width: 928,
// Add labels to the axes here
x: {
label: "Award Date",
tickFormat: "%b", // Shows 'Jan', 'Feb', etc. Use "%B" for full names
},
y: {
label: "Number of Awards",
grid: true // Optional: makes it easier to read values
},
// specify colors here for outstanding lines
color: {
domain: [2021, 2022, 2023, 2024, 2025, 2026], // The specific values in your data
range: ["#6605a6", "#0b72b3", "#2A9D8F", "#e96ab6", "#F4A261", "#E63946"] // The specific colors you want (e.g., Red and Blue)
},
marks: [
// Filter for years that ARE NOT 2025 or 2026 (as an example)
Plot.lineY(ahrq_data_noncomp.filter(d => ![2015, 2016, 2017, 2018, 2019, 2020].includes(d.Original_Year)), {
x: d => new Date(d.NoA_date),
y: "Cum_Award",
stroke: "Original_Year", // This will now use the color scale
strokeWidth: 3 // Optional: make them pop a bit more
}),
Plot.ruleX(
ahrq_data_noncomp,
Plot.pointerX(Plot.binX({}, { x: "NoA_date", thresholds: 1000, insetTop: 20 }))
),
// The Current Day line
Plot.ruleX([todayNormalized], {
stroke: "black",
strokeDasharray: "4", // 4px dash, 4px gap
strokeOpacity: 1 // Makes it look a bit more subtle
}),
Plot.text([todayNormalized], {
x: d => d,
text: d => "Today",
textAnchor: "start",
fontSize: 20,
dx: 10,
dy: -50,
fill: "black"
}),
//Tooltip code
Plot.tip(
ahrq_data_noncomp,
Plot.pointerX(
Plot.binX(
{title: (v) => v},
{
x: "NoA_date",
thresholds: 1000,
render(index, scales, values, dimensions, context) {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const ahrq_data_noncomp = values.title[i];
g.attr(
"transform",
`translate(${Math.min(
values.x1[i],
dimensions.width - dimensions.marginRight - 200
)}, 20)`
).append(() =>
Plot.plot({
marginTop: 20,
height: 150,
width: 150,
axis: null,
y: {domain: scales.scales.color.domain},
marks: [
Plot.frame({ fill: "white", stroke: "currentColor" }),
Plot.dot(ahrq_data_noncomp, {
y: "Original_Year",
fill: (d) => scales.color(d.Original_Year),
r: 5,
frameAnchor: "left",
symbol: "square2",
dx: 6
}),
Plot.text(ahrq_data_noncomp, {
y: "Original_Year",
text: (d) => `FY ${d.Original_Year} :`,
fontSize: 16,
frameAnchor: "left",
dx: 13
}),
Plot.text(ahrq_data_noncomp, {
y: "Original_Year",
text: "Cum_Award",
fontSize: 16,
frameAnchor: "right",
dx: -8
}),
Plot.text(ahrq_data_noncomp, {
frameAnchor: "top-left",
dy: -20,
text: (d) => {
// 1. Convert the string "2026-03-30" into a JS Date object
const dateObj = new Date(d.NoA_date);
// 2. Format it to "Month Day" (e.g., "March 30")
return dateObj.toLocaleString("en-US", {
month: "long",
day: "numeric"
});
},
fontSize: 16,
fontWeight: "bold"
})
]
})
);
}
return g.node();
}
}
)
)
),
Plot.ruleY([0])
]
})- In FY 2025, the total number of New and Competitively Renewed awards (n = ) was lower than previous years.
- The average annual total in the previous four FYs was awards.
- Similar trends are observed for Non-Competitive Renewals, where by the end of FY 2025, awards had been made.
- This is compared with an average year-end total of awards across FY 2021–2024.