package edu.stanford.nlp.sempre.tables.alter; import java.util.*; import edu.stanford.nlp.sempre.*; import edu.stanford.nlp.sempre.tables.*; import edu.stanford.nlp.sempre.tables.lambdadcs.DenotationUtils; import fig.basic.*; /** * Alter the given table. * * Given a table, the corresponding Example, and a seed (alteredTableIndex), return an altered table. * * @author ppasupat */ public class TableAlterer { public static class Options { @Option(gloss = "verbosity") public int verbose = 0; @Option(gloss = "parameter for the geometric distribution used to cut the number of rows") public double altererGeomDistParam = 0.75; @Option public int maxNumRows = 50; } public static Options opts = new Options(); public final Example ex; public final TableKnowledgeGraph oldGraph; public TableAlterer(Example ex) { this.ex = ex; this.oldGraph = (TableKnowledgeGraph) ex.context.graph; } /** * For each column, perform random draws with replacement until all rows are filled. * * Exceptions: * - If the column has distinct cells, then just permute. * - If the column is sorted, keep it sorted. */ public TableKnowledgeGraph constructAlteredGraph(int alteredTableIndex) { Random altererRandom = new Random(); int numRows = Math.min(oldGraph.numRows(), opts.maxNumRows); numRows -= getGeometricRandom(numRows / 2, altererRandom); List<List<TableCellProperties>> cellsByColumn = new ArrayList<>(); // Fuzzy Matching Set<Value> fuzzyMatchedValues = new HashSet<>(); for (int i = 0; i < ex.numTokens(); i++) { for (int j = i + 1; j < ex.numTokens(); j++) { for (Formula formula : oldGraph.getFuzzyMatchedFormulas( ex.getTokens(), i, j, FuzzyMatchFn.FuzzyMatchFnMode.ENTITY)) { if (formula instanceof ValueFormula) { fuzzyMatchedValues.add(((ValueFormula<?>) formula).value); } } } } if (opts.verbose >= 2) { LogInfo.logs("Fuzzy matched: %s", fuzzyMatchedValues); } // Go over each column for (int j = 0; j < oldGraph.numColumns(); j++) { altererRandom = new Random(); TableColumn oldColumnCells = oldGraph.getColumn(j); List<TableCellProperties> oldColumn = new ArrayList<>(), newColumn = new ArrayList<>(); for (TableCell cell : oldColumnCells.children) oldColumn.add(cell.properties); // Keep the entries that are fuzzy matched Set<TableCellProperties> fuzzyMatchedValuesInColumn = new HashSet<>(); for (TableCellProperties properties : oldColumn) { if (fuzzyMatchedValues.contains(properties.nameValue)) fuzzyMatchedValuesInColumn.add(properties); } newColumn.addAll(fuzzyMatchedValuesInColumn); while (newColumn.size() > numRows) newColumn.remove(newColumn.size() - 1); // Sample the cells boolean isAllDistinct = isAllDistinct(oldColumn); if (isAllDistinct) { // Go from top to bottom, ignoring the ones already added List<TableCellProperties> nonFuzzyMatched = new ArrayList<>(oldColumn); for (TableCellProperties properties : newColumn) nonFuzzyMatched.remove(properties); for (int i = 0; newColumn.size() < numRows; i++) newColumn.add(nonFuzzyMatched.get(i)); } else { // Sample with replacement while (newColumn.size() < numRows) newColumn.add(oldColumn.get(altererRandom.nextInt(numRows))); } Collections.shuffle(newColumn, altererRandom); // Sort? String sorted = ""; for (Pair<String, Comparator<TableCellProperties>> pair : COMPS) { if (isSorted(oldColumn, pair.getSecond())) { sorted = pair.getFirst(); newColumn.sort(pair.getSecond()); break; } } // Done! cellsByColumn.add(newColumn); if (opts.verbose >= 2) { LogInfo.logs("Column %3s%4s %s", isAllDistinct ? "[!]" : "", sorted, oldColumnCells.relationNameValue); } } if (opts.verbose >= 1) LogInfo.logs("numRows = %d | final size = %d columns x %d rows", numRows, cellsByColumn.size(), cellsByColumn.get(0).size()); return new TableKnowledgeGraph(null, oldGraph.columns, cellsByColumn, true); } // ============================================================ // Helper Functions // ============================================================ int getGeometricRandom(int limit, Random random) { int geometricRandom = 0; while (geometricRandom < limit && random.nextDouble() < opts.altererGeomDistParam) geometricRandom++; return geometricRandom; } private boolean isAllDistinct(List<TableCellProperties> properties) { Set<String> ids = new HashSet<>(); for (TableCellProperties x : properties) { if (ids.contains(x.id)) return false; ids.add(x.id); } return true; } private static final Comparator<TableCellProperties> NUMBER_COMP = new Comparator<TableCellProperties>() { @Override public int compare(TableCellProperties o1, TableCellProperties o2) { Collection<Value> v1 = o1.metadata.get(TableTypeSystem.CELL_NUMBER_VALUE), v2 = o2.metadata.get(TableTypeSystem.CELL_NUMBER_VALUE); try { return DenotationUtils.NumberProcessor.singleton.compareValues(v1.iterator().next(), v2.iterator().next()); } catch (Exception e) { throw new ClassCastException(); } } }; private static final Comparator<TableCellProperties> NUMBER_COMP_REV = Collections.reverseOrder(NUMBER_COMP); private static final Comparator<TableCellProperties> DATE_COMP = new Comparator<TableCellProperties>() { @Override public int compare(TableCellProperties o1, TableCellProperties o2) { Collection<Value> v1 = o1.metadata.get(TableTypeSystem.CELL_DATE_VALUE), v2 = o2.metadata.get(TableTypeSystem.CELL_DATE_VALUE); try { return DenotationUtils.DateProcessor.singleton.compareValues(v1.iterator().next(), v2.iterator().next()); } catch (Exception e) { throw new ClassCastException(); } } }; private static final Comparator<TableCellProperties> DATE_COMP_REV = Collections.reverseOrder(DATE_COMP); private static final List<Pair<String, Comparator<TableCellProperties>>> COMPS = Arrays.asList( new Pair<>("[N+]", NUMBER_COMP), new Pair<>("[N-]", NUMBER_COMP_REV), new Pair<>("[D+]", DATE_COMP), new Pair<>("[D-]", DATE_COMP_REV) ); private boolean isSorted(List<TableCellProperties> properties, Comparator<TableCellProperties> comparator) { try { for (int i = 0; i < properties.size() - 1; i++) { if (comparator.compare(properties.get(i), properties.get(i+1)) > 0) return false; } return true; } catch (ClassCastException e) { return false; } } }