Gleason Score / ISUP Grade Group in Prostate Cancer RWE
A prostate-cancer histopathology grading measure that combines primary and secondary Gleason patterns into a score and maps modern prostate adenocarcinoma grades into ISUP Grade Groups 1-5 for risk stratification, cohort definition, baseline adjustment, and registry/EHR endpoint interpretation.
In plain language
Gleason Score is the prostate pathologist's way of describing how aggressive the cancer looks under the microscope. Grade Group turns that score into a clearer 1 to 5 scale, where 1 is least aggressive and 5 is most aggressive. In real-world data, the hard part is knowing whether the value came from the diagnostic biopsy, the prostatectomy specimen, a registry grade field, a free-text EHR report, or a claims-linked registry.
Gleason Score and ISUP Grade Group are core prostate-cancer severity variables. A pathologist assigns the dominant histologic pattern and the next most prevalent or highest-grade pattern, sums them into a Gleason score, and reports an associated Grade Group. Modern reporting usually starts at Gleason 3+3=6, which maps to Grade Group 1, even though the historical score range began lower. Grade Group was introduced to make prognosis more interpretable than a 6-10 scale where "6" sounds mid-range but is the lowest routinely assigned cancer grade.
In RWE, this is not just a pathology label. It is a time-stamped, specimen-specific severity measure. The same patient can have a biopsy Gleason 4+3=7, radical-prostatectomy Gleason 3+4=7, tertiary pattern 5, post-treatment grade, and a registry Grade Pathological field that follows AJCC/NAACCR grade rules rather than simply copying the prostatectomy Gleason SSDI. Analyses must state which construct is being used: diagnosis-time clinical Grade Group, surgical-specimen Gleason, highest grade in the AJCC pathological time frame, or post-neoadjuvant grade. Those are not interchangeable.
Pros, cons, and trade-offs
Gleason/Grade Group is clinically meaningful, widely captured in cancer registries, and central to prostate risk grouping, staging, treatment selection, active-surveillance eligibility, and external-control comparability. The trade-off is operational complexity. Registry data may expose both grade fields and Gleason SSDIs; EHRs may contain free-text pathology reports, core-level biopsy details, and copied summary values; claims-only data do not contain observed Gleason grade. Collapsing to Grade Group 1-5 improves interpretability, but it loses raw pattern information such as 3+4 versus 4+3 only if the mapping is not preserved. Tertiary pattern 5 can mark worse prognosis without changing the basic 1-5 Grade Group mapping.
When to use
Use Gleason Score / Grade Group when defining prostate-cancer risk strata, adjusting for baseline disease severity, matching external controls, stratifying treatment patterns, evaluating active surveillance versus definitive therapy, or interpreting registry-linked outcomes. In SEER/NAACCR-style data, prefer official SSDI/Grade fields when they are available and keep clinical, pathological, and post-therapy fields separate. In EHR data, extract the pathology report source, specimen date, procedure type, primary pattern, secondary pattern, score, Grade Group, tertiary pattern, and whether the value came from biopsy, TURP/simple prostatectomy, radical prostatectomy, or post-treatment resection.
When NOT to use - and when it is actively misleading
Do not infer Gleason Score or Grade Group from claims-only treatment patterns, diagnosis codes, radiation/prostatectomy codes, active-surveillance labels, androgen-deprivation therapy, or drug choice. Those are downstream clinical decisions and create confounding-by-indication if treated as observed tumor grade. Do not merge biopsy and prostatectomy values into one "best Gleason" variable without documenting the time frame and selection mechanism; only patients selected for surgery have a radical-prostatectomy specimen. Do not treat a Gleason score of 7 with unknown patterns as equivalent to either Grade Group 2 or 3; 3+4 and 4+3 carry different prognostic meaning. Do not use post-neoadjuvant grade as baseline tumor aggressiveness unless the estimand explicitly concerns post-treatment pathology.
NAACCR/SEER operational capture
For prostate, NAACCR SSDI/Grade capture separates Grade Clinical (#3843), Grade Pathological (#3844), Grade Post Therapy Path (#3845), Gleason Patterns Clinical (#3838), Gleason Patterns Pathological (#3839), Gleason Score Clinical (#3840), Gleason Score Pathological (#3841), and Gleason Tertiary Pattern (#3842). The prostate schema also flags which items are used by SEER, CoC, NPCR, or other registries. The Grade fields code 1-5 as Grade Groups, E for an ambiguous Gleason 7 when patterns are not documented, and 9 for unknown. Gleason Score fields use values such as 06, 07, 08, 09, 10, and special unknown/not-applicable codes. The crucial registry nuance is that grade fields follow AJCC grade time-frame rules, while Gleason Patterns/Score Pathological SSDIs are tied to the prostatectomy specimen and are coded independently from clinical Gleason SSDIs.
Index definitions
Source-backed definitions and variants for the index or checklist family.
| name | definition | source | use | notes |
|---|---|---|---|---|
| Grade Group 1 | Gleason score less than or equal to 6, usually 3+3 in contemporary prostate adenocarcinoma. | ISUP 2014 consensus; NAACCR Grade 17 prostate tables | Low-risk or very-low-risk prostate cancer stratification, active-surveillance eligibility, baseline covariate. | Preserve whether the value is clinical biopsy grade or surgical pathology grade. |
| Grade Group 2 | Gleason score 7 with pattern 3+4. | ISUP 2014 consensus; NAACCR Grade 17 prostate tables | Favorable intermediate-risk stratification when other risk factors support it. | Pattern order matters; do not merge with Grade Group 3 if raw patterns are available. |
| Grade Group 3 | Gleason score 7 with pattern 4+3. | ISUP 2014 consensus; NAACCR Grade 17 prostate tables | Unfavorable intermediate-risk stratification and treatment-selection adjustment. | Same summed score as Grade Group 2, but higher pattern 4 predominance. |
| Grade Group 4 | Gleason score 8. | ISUP 2014 consensus; NAACCR Grade 17 prostate tables | High-risk prostate-cancer definition and severity adjustment. | Common raw patterns include 4+4, 3+5, or 5+3; retain raw pattern fields when possible. |
| Grade Group 5 | Gleason score 9 or 10. | ISUP 2014 consensus; NAACCR Grade 17 prostate tables | Very-high-risk or high-grade disease stratification, external-control matching, and prognosis adjustment. | Preserve whether the value is biopsy-derived, prostatectomy-derived, or post-therapy. |
| Ambiguous Gleason 7 | Gleason score 7 with no documented primary/secondary pattern, coded as E in NAACCR prostate Grade fields. | NAACCR Grade 17 prostate tables | Missingness/ambiguity flag rather than a Grade Group 2 or Grade Group 3 assignment. | Analyze separately or impute only under a pre-specified sensitivity analysis; never silently assign to 3+4 or 4+3. |
Worked example
Scenario
A linked SEER-style registry/EHR study is building baseline prostate-cancer severity for men diagnosed in 2024. One patient has a diagnostic biopsy with Gleason 5+4=9 and later radical prostatectomy with Gleason 3+4=7 and tertiary pattern 5. The protocol needs both a diagnosis-time baseline Grade Group and a surgical-specimen variable without mixing registry grade rules and Gleason SSDIs.
Dataset
Source pathology events and registry fields for one patient.
| event_date | source | specimen | reported_text | primary_pattern | secondary_pattern | gleason_score | grade_group | tertiary_pattern |
|---|---|---|---|---|---|---|---|---|
| 2024-02-17 | pathology report | needle biopsy | Gleason 5+4=9, Grade Group 5 | 5 | 4 | 9 | 5 | |
| 2024-03-17 | pathology report | radical prostatectomy | Gleason 3+4=7, Grade Group 2, tertiary pattern 5 | 3 | 4 | 7 | 2 | 5 |
Steps
Store clinical biopsy Gleason Patterns Clinical = 54, Gleason Score Clinical = 09, and Grade Clinical = 5.
Store prostatectomy Gleason Patterns Pathological = 34, Gleason Score Pathological = 07, and Gleason Tertiary Pattern = 5.
For the NAACCR/AJCC Grade Pathological field, use the registry rule that the pathological time frame may include the highest grade from the clinical workup; here Grade Pathological is 5 even though the prostatectomy Gleason SSDI-derived group is 2.
For baseline confounding control at diagnosis, use clinical Grade Group 5. For a surgical-pathology subgroup analysis, use the prostatectomy-derived Gleason group 2 with tertiary pattern 5 explicitly flagged.
Result
The patient has baseline clinical Grade Group 5 and a separate prostatectomy Gleason-derived Grade Group 2 with tertiary pattern 5. Combining these into a single "pathologic Gleason 7" variable would hide the high-grade biopsy and misclassify baseline severity.
Runnable example
python implementation
Normalize prostate Gleason/Grade Group fields from registry or EHR-derived rows. Inputs: grade : person_id, source_timeframe, grade_code, gleason_patterns, gleason_score, tertiary_pattern grade_code may be NAACCR Grade Clinical/Pathological codes 1-5, E, or...
import pandas as pd
UNKNOWN = {"", "9", "X7", "X8", "X9", "XX", "NA", "NONE", "NAN"}
def _clean(value):
if pd.isna(value):
return ""
return str(value).strip().upper().replace("+", "").replace(" ", "")
def derive_grade_group(grade_code=None, gleason_patterns=None, gleason_score=None):
code = _clean(grade_code)
if code in {"1", "2", "3", "4", "5"}:
return int(code), "grade_code"
if code == "E":
return pd.NA, "ambiguous_gleason_7"
patterns = _clean(gleason_patterns)
score_text = _clean(gleason_score)
if score_text in UNKNOWN:
return pd.NA, "missing"
try:
score = int(score_text)
except ValueError:
return pd.NA, "invalid_score"
if score <= 6:
return 1, "score"
if score == 7:
if patterns == "34":
return 2, "patterns"
if patterns == "43":
return 3, "patterns"
return pd.NA, "ambiguous_gleason_7"
if score == 8:
return 4, "score"
if score in {9, 10}:
return 5, "score"
return pd.NA, "out_of_range"
def normalize_gleason_grade_group(grade):
rows = []
for row in grade.to_dict("records"):
gg, basis = derive_grade_group(
row.get("grade_code"),
row.get("gleason_patterns"),
row.get("gleason_score"),
)
rows.append({
**row,
"grade_group_normalized": gg,
"grade_group_basis": basis,
"has_tertiary_pattern_5": _clean(row.get("tertiary_pattern")) == "5",
"is_ambiguous_gleason7": basis == "ambiguous_gleason_7",
})
return pd.DataFrame(rows)r implementation
data.table version for deriving normalized Grade Group while preserving ambiguous Gleason 7 and tertiary pattern flags.
library(data.table)
clean_grade_value <- function(x) {
x <- toupper(trimws(as.character(x)))
x <- gsub("\\+", "", x)
x <- gsub(" ", "", x)
fifelse(is.na(x) | x %in% c("", "NA", "NAN"), "", x)
}
derive_grade_group_dt <- function(grade) {
setDT(grade)
g <- copy(grade)
g[, grade_code_clean := clean_grade_value(grade_code)]
g[, patterns_clean := clean_grade_value(gleason_patterns)]
g[, score_clean := clean_grade_value(gleason_score)]
g[, `:=`(
grade_group_normalized = NA_integer_,
grade_group_basis = "missing"
)]
g[grade_code_clean %in% as.character(1:5),
`:=`(grade_group_normalized = as.integer(grade_code_clean),
grade_group_basis = "grade_code")]
g[grade_code_clean == "E",
`:=`(grade_group_normalized = NA_integer_,
grade_group_basis = "ambiguous_gleason_7")]
g[, derive_flag := !grade_code_clean %in% c(as.character(1:5), "E")]
g[derive_flag == TRUE, score_num := suppressWarnings(as.integer(score_clean))]
g[derive_flag == TRUE & !is.na(score_num) & score_num <= 6,
`:=`(grade_group_normalized = 1L, grade_group_basis = "score")]
g[derive_flag == TRUE & score_num == 7L & patterns_clean == "34",
`:=`(grade_group_normalized = 2L, grade_group_basis = "patterns")]
g[derive_flag == TRUE & score_num == 7L & patterns_clean == "43",
`:=`(grade_group_normalized = 3L, grade_group_basis = "patterns")]
g[derive_flag == TRUE & score_num == 7L & !patterns_clean %in% c("34", "43"),
`:=`(grade_group_normalized = NA_integer_,
grade_group_basis = "ambiguous_gleason_7")]
g[derive_flag == TRUE & score_num == 8L,
`:=`(grade_group_normalized = 4L, grade_group_basis = "score")]
g[derive_flag == TRUE & score_num %in% c(9L, 10L),
`:=`(grade_group_normalized = 5L, grade_group_basis = "score")]
g[, has_tertiary_pattern_5 := clean_grade_value(tertiary_pattern) == "5"]
g[, is_ambiguous_gleason7 := grade_group_basis == "ambiguous_gleason_7"]
g[]
}