package edu.stanford.nlp.sempre.tables.test;
import java.io.*;
import java.util.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.FuzzyMatchFn.FuzzyMatchFnMode;
import edu.stanford.nlp.sempre.MergeFormula.Mode;
import edu.stanford.nlp.sempre.tables.StringNormalizationUtils;
import edu.stanford.nlp.sempre.tables.TableKnowledgeGraph;
import edu.stanford.nlp.sempre.tables.TableTypeSystem;
import edu.stanford.nlp.sempre.tables.test.CustomExample.ExampleProcessor;
import fig.basic.*;
import fig.exec.*;
/**
* Compute various statistics about the dataset.
*
* - Table size (rows, columns, unique cells)
* - Answer type
* - Whether the answer is in the table
*
* Also aggregate column strings and cell word shapes.
*
* @author ppasupat
*/
public class TableStatsComputer implements Runnable {
public static class Options {
@Option(gloss = "Maximum string length to consider")
public int statsMaxStringLength = 70;
}
public static Options opts = new Options();
public static void main(String[] args) {
Execution.run(args, "TableStatsComputerMain", new TableStatsComputer(), Master.getOptionsParser());
}
@Override
public void run() {
PrintWriter out = IOUtils.openOutHard(Execution.getFile("table-stats.tsv"));
TableStatsComputerProcessor processor = new TableStatsComputerProcessor(out);
CustomExample.getDataset(Dataset.opts.inPaths, processor);
processor.analyzeTables();
out.close();
}
static class TableStatsComputerProcessor implements ExampleProcessor {
PrintWriter out;
Evaluation evaluation = new Evaluation();
Map<TableKnowledgeGraph, Integer> tableCounts = new HashMap<>();
Map<String, Integer> columnStrings = new HashMap<>(), cellStrings = new HashMap<>();
Builder builder;
public TableStatsComputerProcessor(PrintWriter out) {
builder = new Builder();
builder.build();
this.out = out;
out.println(String.join("\t", new String[] {
"id", "context", "rows", "columns", "uniqueCells", "targetType", "inTable",
}));
}
@Override
public void run(CustomExample ex) {
List<String> outputFields = new ArrayList<>();
outputFields.add(ex.id);
TableKnowledgeGraph graph = (TableKnowledgeGraph) ex.context.graph;
MapUtils.incr(tableCounts, graph);
outputFields.add(graph.toLispTree().child(2).value);
outputFields.add("" + graph.numRows());
outputFields.add("" + graph.numColumns());
outputFields.add("" + graph.numUniqueCells());
// Answer type. For convenience, just use the first answer from the list
Value value = ((ListValue) ex.targetValue).values.get(0);
evaluation.add("value-number", value instanceof NumberValue);
evaluation.add("value-date", value instanceof DateValue);
evaluation.add("value-text", value instanceof DescriptionValue);
evaluation.add("value-partial-number",
value instanceof DescriptionValue && ((DescriptionValue) value).value.matches(".*[0-9].*"));
// Check if the value is in the table
boolean inTable = false;
if (value instanceof DescriptionValue) {
outputFields.add("text");
Collection<Formula> formulas = graph.getFuzzyMatchedFormulas(((DescriptionValue) value).value, FuzzyMatchFnMode.ENTITY);
inTable = !formulas.isEmpty();
evaluation.add("value-text-in-table", inTable);
} else if (value instanceof NumberValue) {
outputFields.add("number");
// (and (@type @cell) (@p.num ___))
Formula formula = new MergeFormula(Mode.and,
new JoinFormula(Formula.fromString(CanonicalNames.TYPE), Formula.fromString(TableTypeSystem.CELL_TYPE)),
new JoinFormula(Formula.fromString(TableTypeSystem.CELL_NUMBER_VALUE.id), new ValueFormula<Value>(value)));
Value result = builder.executor.execute(formula, ex.context).value;
inTable = result instanceof ListValue && !((ListValue) result).values.isEmpty();
evaluation.add("value-number-in-table", inTable);
} else if (value instanceof DateValue) {
outputFields.add("date");
// (and (@type @cell) (@p.num ___))
Formula formula = new MergeFormula(Mode.and,
new JoinFormula(Formula.fromString(CanonicalNames.TYPE), Formula.fromString(TableTypeSystem.CELL_TYPE)),
new JoinFormula(Formula.fromString(TableTypeSystem.CELL_DATE_VALUE.id), new ValueFormula<Value>(value)));
Value result = builder.executor.execute(formula, ex.context).value;
inTable = result instanceof ListValue && !((ListValue) result).values.isEmpty();
evaluation.add("value-number-in-table", inTable);
} else {
outputFields.add("unknown");
}
evaluation.add("value-any-in-table", inTable);
outputFields.add("" + inTable);
out.println(String.join("\t", outputFields));
}
public void analyzeTables() {
for (Map.Entry<TableKnowledgeGraph, Integer> entry : tableCounts.entrySet()) {
TableKnowledgeGraph table = entry.getKey();
evaluation.add("count", entry.getValue());
table.populateStats(evaluation);
for (String columnString : table.getAllColumnStrings())
addIfOK(columnString, columnStrings);
for (String cellString : table.getAllCellStrings())
addIfOK(cellString, cellStrings);
}
for (Map.Entry<String, Integer> entry : columnStrings.entrySet()) {
evaluation.add("column-strings", entry.getKey(), entry.getValue());
}
for (Map.Entry<String, Integer> entry : cellStrings.entrySet()) {
evaluation.add("cell-strings", entry.getKey(), entry.getValue());
}
evaluation.logStats("tables");
dumpCollection(columnStrings, "columns");
dumpCollection(cellStrings, "cells");
}
void addIfOK(String x, Map<String, Integer> collection) {
x = StringNormalizationUtils.characterNormalize(x).toLowerCase();
if (x.length() <= TableStatsComputer.opts.statsMaxStringLength)
MapUtils.incr(collection, x);
}
void dumpCollection(Map<String, Integer> collection, String filename) {
List<Map.Entry<String, Integer>> entries = new ArrayList<>(collection.entrySet());
Collections.sort(entries, new ValueComparator<String, Integer>(true));
String path = Execution.getFile(filename);
LogInfo.begin_track("Writing to %s (%d entries)", path, entries.size());
PrintWriter out = IOUtils.openOutHard(path);
for (Map.Entry<String, Integer> entry : entries) {
out.printf("%6d : %s\n", entry.getValue(), entry.getKey());
}
out.close();
LogInfo.end_track();
}
}
}