diff --git a/docs/counters.md b/docs/counters.md index a141eb9d..a79e44f8 100644 --- a/docs/counters.md +++ b/docs/counters.md @@ -281,7 +281,7 @@ Column referred to in TMCF is missing from CSV header. Found an unknown statType value. StatTypes values either: -- end with one of {`value`, `estimate`, `stderror`, `samplesize`, `growthrate`}, or +- end with one of {`value`, `estimate`, `stderror`, `samplesize`, `growthrate`, `ratio`}, or - start with `percentile`, or - equal any one of {`marginoferror`, `measurementResult`}. diff --git a/pipeline/differ/src/test/resources/current/schema.mcf b/pipeline/differ/src/test/resources/current/schema.mcf index 33bd3747..2724055c 100644 --- a/pipeline/differ/src/test/resources/current/schema.mcf +++ b/pipeline/differ/src/test/resources/current/schema.mcf @@ -1,4 +1,4 @@ -dcid: "InterestRate_TreasuryBill_6Month" +Node: dcid:InterestRate_TreasuryBill_6Month name: "InterestRate_TreasuryBill_6Month" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -6,7 +6,7 @@ populationType: dcs:TreasuryBill maturity: [6 Month] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBill_1Year" +Node: dcid:InterestRate_TreasuryBill_1Year name: "InterestRate_TreasuryBill_1Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -14,7 +14,7 @@ populationType: dcs:TreasuryBill maturity: [1 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_2Year" +Node: dcid:InterestRate_TreasuryNote_2Year name: "InterestRate_TreasuryNote_2Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -22,7 +22,7 @@ populationType: dcs:TreasuryNote maturity: [2 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_3Year" +Node: dcid:InterestRate_TreasuryNote_3Year name: "InterestRate_TreasuryNote_3Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -30,7 +30,7 @@ populationType: dcs:TreasuryNote maturity: [3 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_5Year" +Node: dcid:InterestRate_TreasuryNote_5Year name: "InterestRate_TreasuryNote_5Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -38,7 +38,7 @@ populationType: dcs:TreasuryNote maturity: [5 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_7Year" +Node: dcid:InterestRate_TreasuryNote_7Year name: "InterestRate_TreasuryNote_7Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -46,7 +46,7 @@ populationType: dcs:TreasuryNote maturity: [7 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_10Year" +Node: dcid:InterestRate_TreasuryNote_10Year name: "InterestRate_TreasuryNote_10Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -54,7 +54,7 @@ populationType: dcs:TreasuryNote maturity: [10 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBond_20Year" +Node: dcid:InterestRate_TreasuryBond_20Year name: "InterestRate_TreasuryBond_20Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -62,7 +62,7 @@ populationType: dcs:TreasuryBond maturity: [20 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBond_30Year" +Node: dcid:InterestRate_TreasuryBond_30Year name: "InterestRate_TreasuryBond_30Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate diff --git a/pipeline/differ/src/test/resources/obs-diff.csv b/pipeline/differ/src/test/resources/obs-diff.csv index de3d0c97..d5777b3d 100644 --- a/pipeline/differ/src/test/resources/obs-diff.csv +++ b/pipeline/differ/src/test/resources/obs-diff.csv @@ -1,4 +1,4 @@ -dcid:InterestRate_TreasuryBill_3Month;dcid:country/USA;"2025-01-31";;dcid:ConstantMaturityRate;dcid:Percent;,,4.31,DELETED -dcid:InterestRate_TreasuryBond_20Year;dcid:country/USA;"2025-01-30";;dcid:ConstantMaturityRate;dcid:Percent;,4.85,4.81,MODIFIED -dcid:InterestRate_TreasuryBill_3Month;dcid:country/USA;"2025-01-30";;dcid:ConstantMaturityRate;dcid:Percent;,,4.30,DELETED -dcid:InterestRate_TreasuryNote_10Year;dcid:country/USA;"2025-01-31";;dcid:ConstantMaturityRate;dcid:Percent;,4.58,,ADDED +InterestRate_TreasuryBill_3Month;country/USA;2025-01-31;;ConstantMaturityRate;Percent;,,4.31,DELETED +InterestRate_TreasuryBond_20Year;country/USA;2025-01-30;;ConstantMaturityRate;Percent;,4.85,4.81,MODIFIED +InterestRate_TreasuryBill_3Month;country/USA;2025-01-30;;ConstantMaturityRate;Percent;,,4.30,DELETED +InterestRate_TreasuryNote_10Year;country/USA;2025-01-31;;ConstantMaturityRate;Percent;,4.58,,ADDED diff --git a/pipeline/differ/src/test/resources/previous/schema.mcf b/pipeline/differ/src/test/resources/previous/schema.mcf index 4b30a505..490a6019 100644 --- a/pipeline/differ/src/test/resources/previous/schema.mcf +++ b/pipeline/differ/src/test/resources/previous/schema.mcf @@ -1,4 +1,4 @@ -dcid: "InterestRate_TreasuryBill_1Month" +Node: dcid:InterestRate_TreasuryBill_1Month name: "InterestRate_TreasuryBill_1Month" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -6,7 +6,7 @@ populationType: dcs:TreasuryBill maturity: [1 Month] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBill_3Month" +Node: dcid:InterestRate_TreasuryBill_3Month name: "InterestRate_TreasuryBill_3Month" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -14,7 +14,7 @@ populationType: dcs:TreasuryBill maturity: [3 Month] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBill_6Month" +Node: dcid:InterestRate_TreasuryBill_6Month name: "InterestRate_TreasuryBill_6Month" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -22,7 +22,7 @@ populationType: dcs:TreasuryBill maturity: [6 Month] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBill_1Year" +Node: dcid:InterestRate_TreasuryBill_1Year name: "InterestRate_TreasuryBill_01Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -30,7 +30,7 @@ populationType: dcs:TreasuryBill maturity: [1 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_2Year" +Node: dcid:InterestRate_TreasuryNote_2Year name: "InterestRate_TreasuryNote_02Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -38,7 +38,7 @@ populationType: dcs:TreasuryNote maturity: [2 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryNote_10Year" +Node: dcid:InterestRate_TreasuryNote_10Year name: "InterestRate_TreasuryNote_10Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -46,7 +46,7 @@ populationType: dcs:TreasuryNote maturity: [10 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBond_20Year" +Node: dcid:InterestRate_TreasuryBond_20Year name: "InterestRate_TreasuryBond_20Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate @@ -54,7 +54,7 @@ populationType: dcs:TreasuryBond maturity: [20 Year] statType: dcs:measuredValue -dcid: "InterestRate_TreasuryBond_30Year" +Node: dcid:InterestRate_TreasuryBond_30Year name: "InterestRate_TreasuryBond_30Year" typeOf: dcs:StatisticalVariable measuredProperty: dcs:interestRate diff --git a/pipeline/differ/src/test/resources/schema-diff.csv b/pipeline/differ/src/test/resources/schema-diff.csv index 79425c20..971cec76 100644 --- a/pipeline/differ/src/test/resources/schema-diff.csv +++ b/pipeline/differ/src/test/resources/schema-diff.csv @@ -1,7 +1,7 @@ -"InterestRate_TreasuryBill_1Year",maturity:[1 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryBill_1Year";populationType:dcs:TreasuryBill;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,maturity:[1 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryBill_01Year";populationType:dcs:TreasuryBill;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,MODIFIED -"InterestRate_TreasuryBill_3Month",,maturity:[3 Month];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryBill_3Month";populationType:dcs:TreasuryBill;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,DELETED -"InterestRate_TreasuryNote_3Year",maturity:[3 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryNote_3Year";populationType:dcs:TreasuryNote;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,,ADDED -"InterestRate_TreasuryNote_7Year",maturity:[7 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryNote_7Year";populationType:dcs:TreasuryNote;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,,ADDED -"InterestRate_TreasuryNote_5Year",maturity:[5 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryNote_5Year";populationType:dcs:TreasuryNote;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,,ADDED -"InterestRate_TreasuryBill_1Month",,maturity:[1 Month];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryBill_1Month";populationType:dcs:TreasuryBill;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,DELETED -"InterestRate_TreasuryNote_2Year",maturity:[2 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryNote_2Year";populationType:dcs:TreasuryNote;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,maturity:[2 Year];measuredProperty:dcs:interestRate;name:"InterestRate_TreasuryNote_02Year";populationType:dcs:TreasuryNote;statType:dcs:measuredValue;typeOf:dcs:StatisticalVariable,MODIFIED +dcid:InterestRate_TreasuryBill_1Year,maturity:[1 Year];measuredProperty:interestRate;name:InterestRate_TreasuryBill_1Year;populationType:TreasuryBill;statType:measuredValue;typeOf:StatisticalVariable,maturity:[1 Year];measuredProperty:interestRate;name:InterestRate_TreasuryBill_01Year;populationType:TreasuryBill;statType:measuredValue;typeOf:StatisticalVariable,MODIFIED +dcid:InterestRate_TreasuryBill_3Month,,maturity:[3 Month];measuredProperty:interestRate;name:InterestRate_TreasuryBill_3Month;populationType:TreasuryBill;statType:measuredValue;typeOf:StatisticalVariable,DELETED +dcid:InterestRate_TreasuryNote_3Year,maturity:[3 Year];measuredProperty:interestRate;name:InterestRate_TreasuryNote_3Year;populationType:TreasuryNote;statType:measuredValue;typeOf:StatisticalVariable,,ADDED +dcid:InterestRate_TreasuryNote_7Year,maturity:[7 Year];measuredProperty:interestRate;name:InterestRate_TreasuryNote_7Year;populationType:TreasuryNote;statType:measuredValue;typeOf:StatisticalVariable,,ADDED +dcid:InterestRate_TreasuryNote_5Year,maturity:[5 Year];measuredProperty:interestRate;name:InterestRate_TreasuryNote_5Year;populationType:TreasuryNote;statType:measuredValue;typeOf:StatisticalVariable,,ADDED +dcid:InterestRate_TreasuryBill_1Month,,maturity:[1 Month];measuredProperty:interestRate;name:InterestRate_TreasuryBill_1Month;populationType:TreasuryBill;statType:measuredValue;typeOf:StatisticalVariable,DELETED +dcid:InterestRate_TreasuryNote_2Year,maturity:[2 Year];measuredProperty:interestRate;name:InterestRate_TreasuryNote_2Year;populationType:TreasuryNote;statType:measuredValue;typeOf:StatisticalVariable,maturity:[2 Year];measuredProperty:interestRate;name:InterestRate_TreasuryNote_02Year;populationType:TreasuryNote;statType:measuredValue;typeOf:StatisticalVariable,MODIFIED diff --git a/util/src/main/java/org/datacommons/util/FileGroup.java b/util/src/main/java/org/datacommons/util/FileGroup.java index e1cf7454..487155fd 100644 --- a/util/src/main/java/org/datacommons/util/FileGroup.java +++ b/util/src/main/java/org/datacommons/util/FileGroup.java @@ -66,7 +66,7 @@ public static FileGroup build( int nTsv = 0; for (File file : files) { String lowerPath = file.getPath().toLowerCase(); - if (lowerPath.endsWith(".mcf")) { + if (lowerPath.contains(".mcf")) { mcfFiles.add(file); } else if (lowerPath.endsWith(".tmcf")) { tmcfFiles.add(file); diff --git a/util/src/main/java/org/datacommons/util/GraphUtils.java b/util/src/main/java/org/datacommons/util/GraphUtils.java index 2c4c1c08..a79a31d6 100644 --- a/util/src/main/java/org/datacommons/util/GraphUtils.java +++ b/util/src/main/java/org/datacommons/util/GraphUtils.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -301,28 +302,7 @@ public static McfStatVarObsSeries convertMcfGraphToMcfStatVarObsSeries( * @return An McfGraph proto representing the input MCF string. */ public static McfGraph convertToGraph(String input) { - McfGraph.Builder g = McfGraph.newBuilder(); - g.setType(McfType.INSTANCE_MCF); - String node_id = ""; - McfGraph.PropertyValues.Builder base_node = McfGraph.PropertyValues.newBuilder(); - String[] lines = input.split("\n"); - for (String line : lines) { - line = line.trim(); - if (line.isEmpty() || line.startsWith("//") || line.startsWith("#")) { - continue; - } - int colon = line.indexOf(":"); - String lhs = line.substring(0, colon).trim(); - String rhs = line.substring(colon + 1).trim(); - if (lhs.equals(Property.dcid.name()) || lhs.equals("Node")) { - node_id = rhs; - } - setPropVal(lhs, ValueType.TEXT, rhs, base_node); - } - if (!node_id.isEmpty()) { - g.putNodes(node_id, base_node.build()); - } - return g.build(); + return McfParser.parseInstanceMcfString(input, true, null); } /** @@ -334,8 +314,25 @@ public static McfGraph convertToGraph(String input) { */ public static List readMcfFile(String fileName) throws IOException { List graphList = new ArrayList<>(); - File file = new File(fileName); - Scanner scanner = new Scanner(file); + try (Scanner scanner = new Scanner(new File(fileName), StandardCharsets.UTF_8)) { + scanner.useDelimiter("\n\n"); + while (scanner.hasNext()) { + graphList.add(convertToGraph(scanner.next())); + } + } + return graphList; + } + + /** + * Converts an MCF file text into a list of McfGraph protos. + * + * @param mcfString Contents of MCF file. + * @return A list of McfGraph protos, where each proto represents a node or block from the MCF + * file. + */ + public static List readMcfString(String mcfString) { + List graphList = new ArrayList<>(); + Scanner scanner = new Scanner(mcfString); scanner.useDelimiter("\n\n"); while (scanner.hasNext()) { String token = scanner.next(); diff --git a/util/src/main/java/org/datacommons/util/McfParser.java b/util/src/main/java/org/datacommons/util/McfParser.java index 724fb133..4580adc1 100644 --- a/util/src/main/java/org/datacommons/util/McfParser.java +++ b/util/src/main/java/org/datacommons/util/McfParser.java @@ -22,6 +22,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.datacommons.proto.Debug; import org.datacommons.proto.LogLocation; import org.datacommons.proto.Mcf; @@ -31,6 +33,8 @@ // // NOTE: Expects caller to set location file in LogWrapper. public class McfParser { + private static final Logger logger = LogManager.getLogger(McfParser.class); + static String IN_MEMORY_FILE_NAME = "InMemory"; private McfGraph.Builder graph; @@ -58,7 +62,7 @@ public static McfParser init( // Parse a string with instance nodes in MCF format into the McfGraph proto. public static McfGraph parseInstanceMcfString( - String mcfString, boolean isResolved, LogWrapper logCtx) throws IOException { + String mcfString, boolean isResolved, LogWrapper logCtx) { return parseMcfString(mcfString, Mcf.McfType.INSTANCE_MCF, isResolved, logCtx); } @@ -69,8 +73,7 @@ public static McfGraph parseInstanceMcfFile( } // Parse a string with template nodes in MCF format into the McfGraph proto. - public static McfGraph parseTemplateMcfString(String mcfString, LogWrapper logCtx) - throws IOException { + public static McfGraph parseTemplateMcfString(String mcfString, LogWrapper logCtx) { return parseMcfString(mcfString, Mcf.McfType.TEMPLATE_MCF, false, logCtx); } @@ -80,7 +83,7 @@ public static McfGraph parseTemplateMcfFile(String fileName, LogWrapper logCtx) return parseMcfFile(fileName, Mcf.McfType.TEMPLATE_MCF, false, logCtx); } - public McfGraph parseNextNode() throws IOException { + public McfGraph parseNextNode() { if (finished) return null; while (lines.hasNext()) { String line = lines.next(); @@ -118,12 +121,9 @@ private void parseLine(String line) throws AssertionError { } int colon = line.substring(prefixLen).indexOf(Vocabulary.REFERENCE_DELIMITER); if (colon < 1) { - logCtx.addEntry( - Debug.Log.Level.LEVEL_ERROR, + logError( "MCF_MalformedColonLessLine", - "Malformed line without a colon delimiter :: line: '" + line + "'", - fileName, - lineNum); + "Malformed line without a colon delimiter :: line: '" + line + "'"); return; } @@ -131,37 +131,30 @@ private void parseLine(String line) throws AssertionError { String rhs = line.substring(colon + 1).trim(); if (lhs.equals(Vocabulary.NODE)) { if (rhs.indexOf(',') != -1) { - logCtx.addEntry( - Debug.Log.Level.LEVEL_ERROR, + logError( "MCF_MalformedNodeName", - "Found malformed Node value with a comma; must be a unary value :: node: '" + rhs + "'", - fileName, - lineNum); + "Found malformed Node value with a comma; must be a unary value :: node: '" + + rhs + + "'"); return; } if (rhs.startsWith("\"")) { - logCtx.addEntry( - Debug.Log.Level.LEVEL_ERROR, + logError( "MCF_MalformedNodeName", "Found malformed Node value with quotes; must be a non-quoted value :: node: '" + rhs - + "'", - fileName, - lineNum); + + "'"); return; } if (graph.getType() == Mcf.McfType.TEMPLATE_MCF) { LogCb logCb = getLogCb().setDetail(LogCb.VALUE_KEY, rhs); SchemaTerm term = parseSchemaTerm(rhs, logCb); if (term.type != SchemaTerm.Type.ENTITY) { - logCtx.addEntry( - Debug.Log.Level.LEVEL_ERROR, + logError( "TMCF_MalformedEntity", "Found malformed entity name that is not an entity prefix (E:) :: name: '" + rhs - + "'", - fileName, - lineNum); + + "'"); return; } } else { @@ -174,12 +167,9 @@ private void parseLine(String line) throws AssertionError { addNodeLocation(); } else { if (curEntity.isEmpty()) { - logCtx.addEntry( - Debug.Log.Level.LEVEL_ERROR, + logError( "MCF_UnexpectedProperty", - "Property found without a preceding line with 'Node' :: line: '" + line + "'", - fileName, - lineNum); + "Property found without a preceding line with 'Node' :: line: '" + line + "'"); return; } parseValues(lhs, rhs); @@ -238,19 +228,14 @@ private McfGraph finish() throws AssertionError { } if (curEntityLineIdx == 0) { // TODO: This should happen on seeing a new node too. - logCtx.addEntry( - Debug.Log.Level.LEVEL_ERROR, - "MCF_MalformedNode", - "Found a 'Node' without properties :: node: '" + curEntity + "'", - fileName, - lineNum); + logError( + "MCF_MalformedNode", "Found a 'Node' without properties :: node: '" + curEntity + "'"); } return graph.build(); } private static McfGraph parseMcfString( - String mcfString, Mcf.McfType type, boolean isResolved, LogWrapper logCtx) - throws IOException { + String mcfString, Mcf.McfType type, boolean isResolved, LogWrapper logCtx) { McfParser parser = McfParser.init(type, isResolved); parser.fileName = IN_MEMORY_FILE_NAME; parser.logCtx = logCtx; @@ -264,7 +249,7 @@ private static McfGraph parseMcfFile( return parser.parseLines(); } - private McfGraph parseLines() throws IOException { + private McfGraph parseLines() { McfGraph g; ArrayList graphs = new ArrayList<>(); while ((g = parseNextNode()) != null) { @@ -327,7 +312,23 @@ private void parseValues(String prop, String values) { graph.putNodes(curEntity, pvs.build()); } + private void logError(String counter, String message) { + if (logCtx != null) { + logCtx.addEntry(Debug.Log.Level.LEVEL_ERROR, counter, message, fileName, lineNum); + } else { + logger.error(counter + " :: " + message); + } + } + private LogCb getLogCb() { + if (logCtx == null) { + return new LogCb(null, Debug.Log.Level.LEVEL_ERROR, fileName, lineNum) { + @Override + public void logError(String counter, String problemMessage) { + logger.error(counter + " :: " + problemMessage); + } + }; + } return new LogCb(logCtx, Debug.Log.Level.LEVEL_ERROR, fileName, lineNum); } diff --git a/util/src/main/java/org/datacommons/util/TestUtil.java b/util/src/main/java/org/datacommons/util/TestUtil.java index b984f7d9..3a1b311d 100644 --- a/util/src/main/java/org/datacommons/util/TestUtil.java +++ b/util/src/main/java/org/datacommons/util/TestUtil.java @@ -38,7 +38,7 @@ public static Mcf.McfGraph graphFromProto(String protoString) throws IOException return expected.build(); } - public static Mcf.McfGraph graphFromMcf(String mcfString) throws IOException { + public static Mcf.McfGraph graphFromMcf(String mcfString) { return McfParser.parseInstanceMcfString(mcfString, false, TestUtil.newLogCtx()); } diff --git a/util/src/main/java/org/datacommons/util/Vocabulary.java b/util/src/main/java/org/datacommons/util/Vocabulary.java index 14b1952c..67b6f214 100644 --- a/util/src/main/java/org/datacommons/util/Vocabulary.java +++ b/util/src/main/java/org/datacommons/util/Vocabulary.java @@ -335,7 +335,8 @@ public static boolean isStatValueProperty(String val) { || lcVal.endsWith("stderror") || lcVal.endsWith("samplesize") || lcVal.endsWith("growthrate") - || lcVal.endsWith("limit"); + || lcVal.endsWith("limit") + || lcVal.endsWith("ratio"); } public static boolean isStatVar(String type) {