Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

**Changes in version 5.11 (not released yet)**

- Updated the Yost/ACS algorithm to extend the second-to-last range of the "year for lookup" by one year and to implement the A,B,C,D codes that are found in other census tract lookups.
- Added 2023 calculated field for Rural Urban Continuum algorithm.
- Updated the lookup for item #194 in the PRCDA algorithm to use 2024 PRCDA counties
- Removed the 2017 calculated field from the PRCDA algorithm.
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/imsweb/algorithms/StateCountyInputDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public boolean hasUnknownStateOrCounty() {
return isUnknownStateOrCounty(_addressAtDxState, _countyAtDxAnalysis);
}

public static boolean isInValidCounty(String county) {
public static boolean isInvalidCounty(String county) {
return !(isValidCounty(county) || isUnknownCounty(county));
}

Expand All @@ -66,7 +66,7 @@ public static boolean isInvalidState(String state) {
}

public static boolean isInvalidStateOrCounty(String state, String county) {
return isInvalidState(state) || isInValidCounty(county);
return isInvalidState(state) || isInvalidCounty(county);
}

public static boolean isUnknownCounty(String county) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void applyRecodes() {
_censusTract2020 = _censusTract2020 == null ? "" : _censusTract2020.trim();
}

private String getCensusTractVariable(CensusTract censusTract) {
protected String getCensusTractVariable(CensusTract censusTract) {
if (CensusTract.CENSUS_2000.equals(censusTract))
return _censusTract2000;
if (CensusTract.CENSUS_2010.equals(censusTract))
Expand All @@ -53,7 +53,7 @@ public static boolean isInvalidCensusTract(String censusTract) {
}

public static boolean isInvalidStateCountyOrCensusTract(String state, String county, String censusTract) {
return isInvalidState(state) || isInValidCounty(county) || isInvalidCensusTract(censusTract);
return isInvalidState(state) || isInvalidCounty(county) || isInvalidCensusTract(censusTract);
}

public static boolean isUnknownCensusTract(String censusTract) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,77 @@
package com.imsweb.algorithms.yostacspoverty;

public class YostAcsPovertyInputDto {
import com.imsweb.algorithms.StateCountyTractInputDto;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

private String _addressAtDxState;
import static com.imsweb.algorithms.yostacspoverty.YostAcsPovertyUtils.YOST_ACS_CENSUS_TRACT_PIVOT_YEAR;

private String _countyAtDxAnalysis;
public class YostAcsPovertyInputDto extends StateCountyTractInputDto {

private String _censusTract2010;

private String _censusTract2020;
private static final String MISSING_YEAR = "";

private String _dateOfDiagnosis;
private String _yearOfDiagnosis;

public String getAddressAtDxState() {
return _addressAtDxState;
@Override
public void applyRecodes() {
super.applyRecodes();
_dateOfDiagnosis = _dateOfDiagnosis == null ? "" : _dateOfDiagnosis.trim();
_yearOfDiagnosis = _yearOfDiagnosis == null ? "" : _yearOfDiagnosis.trim();
}

public void setAddressAtDxState(String addressAtDxState) {
_addressAtDxState = addressAtDxState;
public int computeYearOfDiagnosis() {
if (isValidYear(_yearOfDiagnosis)) {
return Integer.parseInt(_yearOfDiagnosis);
}
return -1;
}

public String getCountyAtDxAnalysis() {
return _countyAtDxAnalysis;
public String getDateOfDiagnosis() {
return _dateOfDiagnosis;
}

public void setCountyAtDxAnalysis(String countyAtDxAnalysis) {
_countyAtDxAnalysis = countyAtDxAnalysis;
public void setDateOfDiagnosis(String dateOfDiagnosis) {
_dateOfDiagnosis = dateOfDiagnosis;
_yearOfDiagnosis = dateOfDiagnosis == null ? null : StringUtils.rightPad(dateOfDiagnosis, 4).substring(0, 4).trim();
}

public String getCensusTract2010() {
return _censusTract2010;
public boolean hasInvalidStateCountyCensusTractOrYear() {
if (isInvalidState(getAddressAtDxState()) || isInvalidCounty(getCountyAtDxAnalysis()) || isInvalidYear(_yearOfDiagnosis))
return true;
// if year is missing we don't know which census tract is the right one to use, so just check both
if (isUnknownYear(_yearOfDiagnosis))
return isInvalidCensusTract(getCensusTract2010()) || isInvalidCensusTract(getCensusTract2020());
// year isn't missing, so check the census tract we'll end up using for the lookup
int dxYear = Integer.parseInt(_yearOfDiagnosis);
CensusTract censusTract = dxYear <= YOST_ACS_CENSUS_TRACT_PIVOT_YEAR ? CensusTract.CENSUS_2010 : CensusTract.CENSUS_2020;
return isInvalidCensusTract(getCensusTractVariable(censusTract));
}

public void setCensusTract2010(String censusTract2010) {
_censusTract2010 = censusTract2010;
public boolean hasUnknownStateCountyCensusTractOrYear() {
if (isUnknownState(getAddressAtDxState()) || isUnknownCounty(getCountyAtDxAnalysis()) || isUnknownYear(_yearOfDiagnosis))
return true;
// if year is invalid we don't know which census tract is the right one to use, so just check both
if (isInvalidYear(_yearOfDiagnosis))
return isUnknownCensusTract(getCensusTract2010()) || isUnknownCensusTract(getCensusTract2020());
// year isn't invalid, so check the census tract we'll end up using for the lookup
int dxYear = Integer.parseInt(_yearOfDiagnosis);
CensusTract censusTract = dxYear <= YOST_ACS_CENSUS_TRACT_PIVOT_YEAR ? CensusTract.CENSUS_2010 : CensusTract.CENSUS_2020;
return isUnknownCensusTract(getCensusTractVariable(censusTract));
}

public String getCensusTract2020() {
return _censusTract2020;
public static boolean isInvalidYear(String year) {
return !(isValidYear(year) || isUnknownYear(year));
}

public void setCensusTract2020(String censusTract2020) {
_censusTract2020 = censusTract2020;
public static boolean isUnknownYear(String year) {
return MISSING_YEAR.equals(year);
}

public String getDateOfDiagnosis() {
return _dateOfDiagnosis;
// this is private because it's meant to be more of a helper function
// I want users of this class to use the "Invalid" and "Unknown" functions
private static boolean isValidYear(String year) {
return year != null && year.length() == 4 && NumberUtils.isDigits(year);
}

public void setDateOfDiagnosis(String dateOfDiagnosis) {
_dateOfDiagnosis = dateOfDiagnosis;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ public String getYostQuintileUS() {
return _yostQuintileUS;
}

public void setAllFields(String value) {
_yostQuintileUS = value;
_yostQuintileState = value;
_acsPctPovAllRaces = value;
_acsPctPovWhite = value;
_acsPctPovBlack = value;
_acsPctPovAIAN = value;
_acsPctPovAsianNHOPI = value;
_acsPctPovWhiteNonHisp = value;
_acsPctPovHispanic = value;
}

public void setYostQuintileUS(String yostQuintileUS) {
_yostQuintileUS = yostQuintileUS;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package com.imsweb.algorithms.yostacspoverty;

import java.time.LocalDate;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import com.imsweb.algorithms.internal.CensusData;
import com.imsweb.algorithms.internal.CountryData;
import com.imsweb.algorithms.internal.CountyData;
import com.imsweb.algorithms.internal.StateData;
import com.imsweb.algorithms.internal.YearData;

import java.time.LocalDate;

/**
* This class can be used to calculate ACS Linkage variables.
* Created on Oct 13, 2017 by howew
Expand All @@ -19,41 +16,73 @@
public final class YostAcsPovertyUtils {

public static final String ALG_NAME = "NAACCR Yost Quintile & Area-Based Social Measures Linkage Program";
public static final String ALG_VERSION = "released in September 2025";
public static final String ALG_VERSION = "August 13, 2026";

public static final int YOST_ACS_CENSUS_TRACT_PIVOT_YEAR = 2017;

//Unknown values for each code
public static final String YOST_ACS_UNK_A = "A";
public static final String YOST_ACS_UNK_B = "B";
public static final String YOST_ACS_UNK_C = "C";
public static final String YOST_ACS_UNK_D = "D";

private YostAcsPovertyUtils() {
// no instances of this class allowed!
}

/**
* Helper method to compute the lookup year.
* The algorithm uses data from 2006 to 2024, but it uses 5-years estimate data points, meaning it only has data points from 2008 to 2022.
* Different DX years use different data points:
* - 2006-2007 use the 2008 data point from 2010 census
* - 2008-2017 use the provided year data point for 2010 census (so 2008 uses the 2008 data point, 2009 uses the 2009 data point, etc...)
* - 2018-2022 use the provided year data point for 2020 census (so 2018 uses the 2018 data point, etc...)
* - 2023+ use the 2022 data point from 2020 census
* @param dxYear the year of diagnosis from the input dto
* @return the dx year to use for the lookup
*/
private static int computeYearForLookup(int dxYear) {
int yearForLookup = dxYear;
if (dxYear == 2006 || dxYear == 2007)
yearForLookup = 2008;
else if (dxYear >= 2023)
yearForLookup = 2022;
return yearForLookup;
}

/**
* Calculates the ACS data for the provided ACS data input dto
* <br/><br/>
* The returned dto will contain yost indices and acs poverty percentages across a number of time periods
* or one of the unknown values listed below:
* <ul>
* <li>A = State, county, tract, or year of dx are invalid</li>
* <li>B = State, tract, and year of dx are valid, but county was not reported</li>
* <li>C = State + county + tract + year combination was not found</li>
* <li>D = State, county, tract, or year of dx are blank or unknown/li>
* </ul>
* <br/><br/>
* @param input a <code>ACSLinkageInputDto</code> input object
* @return the computed ACS data
*/
public static YostAcsPovertyOutputDto computeYostAcsPovertyData(YostAcsPovertyInputDto input) {
YostAcsPovertyOutputDto result = new YostAcsPovertyOutputDto();

String state = input.getAddressAtDxState();
String county = input.getCountyAtDxAnalysis();
String census2010 = input.getCensusTract2010();
String census2020 = input.getCensusTract2020();
String year = input.getDateOfDiagnosis() == null ? null : StringUtils.rightPad(input.getDateOfDiagnosis(), 4).substring(0, 4).trim();

if (state != null && county != null && (census2010 != null || census2020 != null) && NumberUtils.isDigits(year)) {
int dxYear = Integer.parseInt(year);

// The algorithm uses data from 2006 to 2023, but it uses 5-years estimate data points, meaning it only has data points from 2008 to 2021.
// Different DX years use different data points:
// - 2006-2007 use the 2008 data point from 2010 census
// - 2008-2015 use the provided year data point for 2010 census (so 2008 uses the 2008 data point, 2009 uses the 2009 data point, etc...)
// - 2016-2017 use the 2018 data point from 2020 census
// - 2018-2021 use the provided year data point for 2020 census (so 2018 uses the 2018 data point, etc...)
// - 2022+ use the 2021 data point from 2020 census
input.applyRecodes();

if (input.hasInvalidStateCountyCensusTractOrYear())
result.setAllFields(YOST_ACS_UNK_A);
else if (input.hasUnknownStateCountyCensusTractOrYear())
result.setAllFields(YOST_ACS_UNK_D);
else if (input.countyIsNotReported())
result.setAllFields(YOST_ACS_UNK_B);
else {
int dxYear = input.computeYearOfDiagnosis();
if (dxYear >= 2006 && dxYear <= LocalDate.now().getYear()) {
String state = input.getAddressAtDxState();
String county = input.getCountyAtDxAnalysis();
String census2010 = input.getCensusTract2010();
String census2020 = input.getCensusTract2020();

if (!CountryData.getInstance().isYearBasedTractDataInitialized(state))
CountryData.getInstance().initializeYearBasedTractData(state);
Expand All @@ -62,17 +91,9 @@ public static YostAcsPovertyOutputDto computeYostAcsPovertyData(YostAcsPovertyIn
if (stateData != null) {
CountyData countyData = stateData.getCountyData(county);
if (countyData != null) {
CensusData censusData = countyData.getCensusData(dxYear <= 2015 ? census2010 : census2020);
CensusData censusData = countyData.getCensusData(dxYear <= YOST_ACS_CENSUS_TRACT_PIVOT_YEAR ? census2010 : census2020);
if (censusData != null) {

int yearForLookup = dxYear;
if (dxYear == 2006 || dxYear == 2007)
yearForLookup = 2008;
else if (dxYear == 2016 || dxYear == 2017)
yearForLookup = 2018;
else if (dxYear >= 2022)
yearForLookup = 2021;

int yearForLookup = computeYearForLookup(dxYear);
YearData yearData = censusData.getYearData(String.valueOf(yearForLookup));
if (yearData != null) {
result.setYostQuintileUS(yearData.getYostQuintileUS());
Expand All @@ -90,7 +111,25 @@ else if (dxYear >= 2022)
}
}
}
if (result.getYostQuintileUS() == null)
result.setYostQuintileUS(YOST_ACS_UNK_C);
if (result.getYostQuintileState() == null)
result.setYostQuintileState(YOST_ACS_UNK_C);
if (result.getAcsPctPovAllRaces() == null)
result.setAcsPctPovAllRaces(YOST_ACS_UNK_C);
if (result.getAcsPctPovWhite() == null)
result.setAcsPctPovWhite(YOST_ACS_UNK_C);
if (result.getAcsPctPovBlack() == null)
result.setAcsPctPovBlack(YOST_ACS_UNK_C);
if (result.getAcsPctPovAsianNHOPI() == null)
result.setAcsPctPovAsianNHOPI(YOST_ACS_UNK_C);
if (result.getAcsPctPovAIAN() == null)
result.setAcsPctPovAIAN(YOST_ACS_UNK_C);
if (result.getAcsPctPovWhiteNonHisp() == null)
result.setAcsPctPovWhiteNonHisp(YOST_ACS_UNK_C);
if (result.getAcsPctPovHispanic() == null)
result.setAcsPctPovHispanic(YOST_ACS_UNK_C);

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,27 @@ public void testComputePovertyIndicator() {
rec.put("censusTract2010", "020600");
rec.put("dateOfDiagnosisYear", "2015");
Assert.assertEquals("3", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
// test 2016 (census2020)
rec.put("censusTract2020", "020600");
// test 2016 (census2010) - note: this may fail if 2017 data is added to the SEER big data file (2010 geographies)
rec.put("censusTract2010", "020600");
rec.put("dateOfDiagnosisYear", "2016");
Assert.assertEquals("4", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
// test 2017 (census2020)
rec.put("censusTract2020", "020600");
Assert.assertEquals("9", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
// test 2017 (census2010) - note: this may fail if 2017 data is added to the SEER big data file (2010 geographies)
rec.put("censusTract2010", "020600");
rec.put("dateOfDiagnosisYear", "2017");
Assert.assertEquals("9", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
// test 2018 (census2020)
rec.put("censusTract2010", "");
rec.put("censusTract2020", "020600");
rec.put("dateOfDiagnosisYear", "2018");
Assert.assertEquals("4", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
// test 2021 (census2020)
rec.put("censusTract2020", "020600");
rec.put("dateOfDiagnosisYear", "2021");
Assert.assertEquals("2", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
// test 2025 (census2020)
// test 2025 (census2020) - note: this may fail once 2022 data is added to the SEER big data file (2020 geographies)
rec.put("censusTract2020", "020600");
rec.put("dateOfDiagnosisYear", "2025");
Assert.assertEquals("2", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
Assert.assertEquals("9", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
//test unknown year
rec.put("dateOfDiagnosisYear", "9999");
Assert.assertEquals("9", computePovertyIndicator(rec).getCensusTractPovertyIndicator());
Expand Down
Loading
Loading