Skip to content

Commit 79043d0

Browse files
authored
[deploy] Data Formulator v0.1.7 anchor
Data Formulator v0.1.7 anchor
2 parents 415fd6b + 9447da5 commit 79043d0

24 files changed

+688
-683
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ Transform data and create rich visualizations iteratively with AI 🪄. Try Data
2222

2323
## News 🔥🔥🔥
2424

25+
- [03-20-2025] Data Formulator 0.1.7: Anchoring ⚓︎
26+
- Anchor an intermediate dataset, so that followup data analysis are built on top of the anchored data, not the original one.
27+
- It is handy when: clean a data and work with only the cleaned data; create a subset from the original data or join multiple data, and then focus your analysis from there. The AI agent will be less likely to get confused and work faster. ⚡️⚡️
28+
- Don't forget to update Data Formulator to test it out!
29+
2530
- [02-20-2025] Data Formulator 0.1.6 released!
2631
- Now supports working with multiple datasets at once! Tell Data Formulator which data tables you would like to use in the encoding shelf, and it will figure out how to join the tables to create a visualization to answer your question. 🪄
2732
- Checkout the demo at [[https://github.com/microsoft/data-formulator/releases/tag/0.1.6]](https://github.com/microsoft/data-formulator/releases/tag/0.1.6).

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "data_formulator"
7-
version = "0.1.6.2"
7+
version = "0.1.7"
88

99
requires-python = ">=3.9"
1010
authors = [

src/app/dfSlice.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,11 @@ let deleteChartsRoutine = (state: DataFormulatorState, chartIds: string[]) => {
142142
state.activeThreadChartId = activeThreadChartId;
143143

144144
let unrefedDerivedTableIds = getUnrefedDerivedTableIds(state);
145-
state.tables = state.tables.filter(t => !unrefedDerivedTableIds.includes(t.id));
145+
let tableIdsToDelete = state.tables.filter(t => !t.anchored && unrefedDerivedTableIds.includes(t.id)).map(t => t.id);
146+
147+
state.tables = state.tables.filter(t => !tableIdsToDelete.includes(t.id));
146148
// remove intermediate charts that lead to this table
147-
state.charts = state.charts.filter(c => !(c.intermediate && unrefedDerivedTableIds.includes(c.intermediate.resultTableId)));
149+
state.charts = state.charts.filter(c => !(c.intermediate && tableIdsToDelete.includes(c.intermediate.resultTableId)));
148150
}
149151

150152
export const fetchFieldSemanticType = createAsyncThunk(
@@ -335,6 +337,16 @@ export const dataFormulatorSlice = createSlice({
335337
// separate this, so that we only delete on tier of table a time
336338
state.charts = state.charts.filter(c => !(c.intermediate && c.intermediate.resultTableId == tableId));
337339
},
340+
updateTableAnchored: (state, action: PayloadAction<{tableId: string, anchored: boolean}>) => {
341+
let tableId = action.payload.tableId;
342+
let anchored = action.payload.anchored;
343+
state.tables = state.tables.map(t => t.id == tableId ? {...t, anchored} : t);
344+
},
345+
updateTableDisplayId: (state, action: PayloadAction<{tableId: string, displayId: string}>) => {
346+
let tableId = action.payload.tableId;
347+
let displayId = action.payload.displayId;
348+
state.tables = state.tables.map(t => t.id == tableId ? {...t, displayId} : t);
349+
},
338350
addChallenges: (state, action: PayloadAction<{tableId: string, challenges: { text: string; difficulty: 'easy' | 'medium' | 'hard'; }[]}>) => {
339351
state.activeChallenges = [...state.activeChallenges, action.payload];
340352
},
@@ -371,18 +383,6 @@ export const dataFormulatorSlice = createSlice({
371383
}
372384
})
373385
},
374-
updateChartScaleFactor: (state, action: PayloadAction<{chartId: string, scaleFactor: number}>) => {
375-
let chartId = action.payload.chartId;
376-
let scaleFactor = action.payload.scaleFactor;
377-
378-
state.charts = state.charts.map(chart => {
379-
if (chart.id == chartId) {
380-
return { ...chart, scaleFactor: scaleFactor };
381-
} else {
382-
return chart;
383-
}
384-
})
385-
},
386386
deleteChartById: (state, action: PayloadAction<string>) => {
387387
let chartId = action.payload;
388388
deleteChartsRoutine(state, [chartId]);

src/app/utils.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,10 +549,21 @@ export const resolveChartFields = (chart: Chart, currentConcepts: FieldItem[], r
549549
}
550550

551551
export let getTriggers = (leafTable: DictTable, tables: DictTable[]) => {
552-
// recursively find triggers that ends in leafTable
552+
// recursively find triggers that ends in leafTable (if the leaf table is anchored, we will find till the previous table is anchored)
553553
let triggers : Trigger[] = [];
554554
let t = leafTable;
555-
while(t.derive != undefined) {
555+
while(true) {
556+
557+
// this is when we find an original table
558+
if (t.derive == undefined) {
559+
break;
560+
}
561+
562+
// this is when we find an anchored table (which is not the leaf table)
563+
if (t !== leafTable && t.anchored) {
564+
break;
565+
}
566+
556567
let trigger = t.derive.trigger as Trigger;
557568
triggers = [trigger, ...triggers];
558569
let parentTable = tables.find(x => x.id == trigger.tableId);

src/components/ComponentType.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ export interface FieldItem {
2020
type: Type;
2121
source: FieldSource;
2222
domain: any[];
23+
tableRef: string; // which table it belongs to, it matters when it's an original field or a derived field
24+
2325
transform?: ConceptTransformation;
24-
tableRef?: string; // which table it comes from, it matters when it's an original field
25-
temporary?: true;
26+
temporary?: true; // the field is temporary, and it will be deleted unless it's saved
2627
levels?: {values: any[], reason: string}; // the order in which values in this field would be sorted
2728
semanticType?: string; // the semantic type of the object, inferred by the model
2829
}
@@ -56,6 +57,7 @@ export interface Trigger {
5657

5758
export interface DictTable {
5859
id: string; // name/id of the table
60+
displayId: string; // display id of the table
5961
names: string[]; // column names
6062
types: Type[]; // column types
6163
rows: any[]; // table content, each entry is a row
@@ -69,22 +71,26 @@ export interface DictTable {
6971
// source specifies how the deriviation is done from the source tables, they may be the same, but not necessarily
7072
// in fact, right now dict tables are all triggered from charts
7173
trigger: Trigger,
72-
}
74+
};
75+
anchored: boolean; // whether this table is anchored as a persistent table used to derive other tables
7376
}
7477

7578
export function createDictTable(
7679
id: string, rows: any[],
7780
derive: {code: string, codeExpl: string, source: string[], dialog: any[],
78-
trigger: Trigger} | undefined = undefined) : DictTable {
81+
trigger: Trigger} | undefined = undefined,
82+
anchored: boolean = false) : DictTable {
7983

8084
let names = Object.keys(rows[0])
8185

8286
return {
8387
id,
88+
displayId: `${id}`,
8489
names,
8590
rows,
8691
types: names.map(name => inferTypeFromValueArray(rows.map(r => r[name]))),
8792
derive,
93+
anchored
8894
}
8995
}
9096

@@ -94,7 +100,6 @@ export type Chart = {
94100
encodingMap: EncodingMap,
95101
tableRef: string,
96102
saved: boolean,
97-
scaleFactor?: number,
98103
intermediate?: Trigger // whether this chart is only an intermediate chart (e.g., only used as a spec for transforming tables)
99104
}
100105

src/data/utils.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const loadTextDataWrapper = (title: string, text: string, fileType: strin
1818
if (fileType == "text/csv" || fileType == "text/tab-separated-values") {
1919
table = createTableFromText(tableName, text);
2020
} else if (fileType == "application/json") {
21-
table = createTableFromFromObjectArray(tableName, JSON.parse(text));
21+
table = createTableFromFromObjectArray(tableName, JSON.parse(text), true);
2222
}
2323
return table;
2424
};
@@ -52,10 +52,10 @@ export const createTableFromText = (title: string, text: string): DictTable | un
5252
return row;
5353
});
5454

55-
return createTableFromFromObjectArray(title, values);
55+
return createTableFromFromObjectArray(title, values, true);
5656
};
5757

58-
export const createTableFromFromObjectArray = (title: string, values: any[], derive?: any): DictTable => {
58+
export const createTableFromFromObjectArray = (title: string, values: any[], anchored: boolean, derive?: any): DictTable => {
5959
const len = values.length;
6060
let names: string[] = [];
6161
let cleanNames: string[] = [];
@@ -95,10 +95,12 @@ export const createTableFromFromObjectArray = (title: string, values: any[], der
9595

9696
return {
9797
id: title,
98+
displayId: `${title}`,
9899
names: columnTable.names(),
99100
types: columnTable.names().map(name => (columnTable.column(name) as Column).type),
100101
rows: columnTable.objects(),
101-
derive: derive
102+
derive: derive,
103+
anchored: anchored
102104
}
103105
};
104106

@@ -171,7 +173,7 @@ export const loadBinaryDataWrapper = (title: string, arrayBuffer: ArrayBuffer):
171173
const jsonData = XLSX.utils.sheet_to_json(worksheet);
172174

173175
// Create a table from the JSON data with sheet name included in the title
174-
const sheetTable = createTableFromFromObjectArray(`${title}-${sheetName}`, jsonData);
176+
const sheetTable = createTableFromFromObjectArray(`${title}-${sheetName}`, jsonData, true);
175177
tables.push(sheetTable);
176178
}
177179

src/scss/App.scss

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,15 @@ h2.view-title {
9191

9292
.Resizer.disabled:hover {
9393
border-color: transparent;
94-
}
94+
}
95+
96+
.GroupHeader {
97+
position: sticky;
98+
padding: 4px 8px;
99+
color: rgba(0, 0, 0, 0.6);
100+
font-size: 12px;
101+
}
102+
103+
.GroupItems {
104+
padding: 0;
105+
}

src/scss/EncodingShelf.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
.encoding-shelf-trigger-card {
2525
margin: 6px 2px;
26-
min-width: 160px;
26+
min-width: 80px;
2727
}
2828

2929
.encoding-shelf-compact {

src/scss/VisualizationView.scss

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,6 @@ $accelerate-ease: cubic-bezier(0.4, 0.0, 1, 1);
152152
animation: appear 0.5s ease-out;
153153
}
154154

155-
.focused-vega-thumbnail {
156-
//background-color: blue;
157-
box-shadow: 0 0 5px rgba(33,33,33,.3);
158-
z-index: 1;
159-
}
160-
161155
.vega-thumbnail:hover {
162156
transform: translateY(1px);
163157
box-shadow: 0 0 3px rgba(33,33,33,.2);

0 commit comments

Comments
 (0)