Pivoting Data - BareFrame.pivot¶
BareFrame.pivot transforms long-format data (many rows, few columns) into wide-format data (few rows, many columns).
Use it when you need to turn one column's values into new column headers and fill the cells with another column's values.
const wide = df.pivot({
index: "date",
columns: "metric",
values: "value",
});
Without gaslamp¶
Pivoting data manually requires nested loops and careful tracking of indices. You must find unique values, match rows, and build a new structure:
const values = sheet.getDataRange().getValues();
const headers = values[0]; // ["date", "metric", "value"]
const rows = values.slice(1);
// Find unique dates and metrics
const uniqueDates = [...new Set(rows.map(r => r[0]))];
const uniqueMetrics = [...new Set(rows.map(r => r[1]))];
// Build the pivoted structure
const result = [["date", ...uniqueMetrics]];
for (const date of uniqueDates) {
const row = [date];
for (const metric of uniqueMetrics) {
const match = rows.find(r => r[0] === date && r[1] === metric);
row.push(match ? match[2] : null);
}
result.push(row);
}
// result:
// ["date", "sales", "profit"]
// ["2024-01", 100, 20]
// ["2024-02", 110, 25]
If columns are added or the layout changes, the hardcoded indices break immediately.
With gaslamp¶
pivot uses column names instead of indices.
The operation remains correct even if the source data changes structure:
const df = gaslamp.BareFrame.fromSheet(sheet);
const wide = df.pivot({
index: "date",
columns: "metric",
values: "value",
});
console.log(wide.display());
// | date | sales | profit |
// | ------- | ----- | ------ |
// | 2024-01 | 100 | 20 |
// | 2024-02 | 110 | 25 |
Understanding pivot¶
The three option parameters define how the pivot works:
| Parameter | Role | Example |
|---|---|---|
index |
Column whose unique values become row labels | "date" → rows labeled 2024-01, 2024-02, etc. |
columns |
Column whose unique values become new column headers | "metric" → columns sales, profit, etc. |
values |
Column whose values fill the cells | "value" → cell contents are sales/profit numbers |
Common Patterns¶
Sales by Date and Product¶
Pivot sales data to see each product as a column:
const df = gaslamp.BareFrame.fromSheet(sheet);
const byProduct = df.pivot({
index: "date",
columns: "product",
values: "sales",
});
// date | Laptop | Phone | Desk
// ------- | ------ | ----- | -----
// 2024-01 | 2500 | 800 | 150
// 2024-02 | 2700 | 900 | 200
Metrics by Date¶
Pivot metrics to compare multiple measures side by side:
const byMetric = df.pivot({
index: "date",
columns: "metric",
values: "value",
});
// date | cpu_usage | memory_usage | disk_usage
// ------- | --------- | ------------ | ----------
// 2024-01 | 45 | 78 | 62
// 2024-02 | 52 | 81 | 65
Use Cases Beyond Tables¶
Heatmaps¶
pivot creates the data foundation for heatmaps — transforming time-series or categorical data into a matrix where each cell represents an aggregate value.
Once the data is pivoted, you can apply conditional formatting or color scales to visualize patterns.
For example, a heatmap showing website traffic (rows = page, columns = hour, values = visits) makes it easy to spot peak traffic times:
const wide = df.pivot({
index: "page",
columns: "hour",
values: "visits",
});
// Pivoted data can then be rendered as a heatmap with conditional formatting
See the Cookbook for heatmap visualization examples.
Aggregation Before Pivot¶
By default, pivot uses the first matching value when a (index, columns) pair occurs multiple times in the source data.
If your data has duplicate combinations, you must aggregate before pivoting.
Aggregating with groupBy¶
The most common approach is to use groupBy with an aggregation function:
const df = gaslamp.BareFrame.fromSheet(sheet);
// Source: multiple rows per (product, month) combination
// We want the sum of sales per (product, month)
const aggregated = df
.groupBy(["product", "month"])
.sum(["sales"]);
// Now each (product, month) pair appears exactly once
const wide = aggregated.pivot({
index: "product",
columns: "month",
values: "sales",
});
// | product | Jan | Feb | Mar |
// | Laptop | 500 | 600 | 550 | (sum of all Jan Laptop sales, etc.)
// | Phone | 300 | 350 | 400 |
Available aggregation functions: sum, mean, min, max, count
If aggregation is not needed¶
If each (index, columns) pair appears at most once, no aggregation is required:
// Each (date, metric) pair appears exactly once
const wide = df.pivot({
index: "date",
columns: "metric",
values: "value",
});
Handling Missing Intersections¶
When a combination of index and columns has no matching row in the source data,
the cell is filled with null:
// Source data (incomplete):
// product | month | sales
// Laptop | Jan | 100
// Laptop | Mar | 110
// Phone | Jan | 80
// (Phone / Mar is missing)
const wide = df.pivot({
index: "product",
columns: "month",
values: "sales",
});
// product | Jan | Feb | Mar
// ------- | --- | ---- | ---
// Laptop | 100 | null | 110
// Phone | 80 | null | null
Error Handling¶
pivot throws an error immediately if any of the three columns does not exist:
df.pivot({
index: "missing_col",
columns: "month",
values: "sales",
});
// Error: BareFrame.pivot: column "missing_col" does not exist
This makes typos and schema mismatches visible right away.
Practical Workflow¶
Read long-format time-series data from a sheet, pivot it to wide format, and write to a summary sheet:
function workflow_pivot() {
const book = SpreadsheetApp.getActiveSpreadsheet();
const inputSheet = book.getSheetByName("TimeSeries");
const outputSheet = book.getSheetByName("Summary")
?? book.insertSheet("Summary");
const df = gaslamp.BareFrame.fromSheet(inputSheet);
// Transform: long format (date, metric, value) → wide format
const wide = df.pivot({
index: "date",
columns: "metric",
values: "value",
});
wide.toSheet(outputSheet);
console.log(`Pivoted ${df.length} rows → ${wide.length} rows, ${wide.headers.length} columns.`);
}
Next Steps¶
- Unfolding Column Groups — opposite of pivot; expand side-by-side column groups into rows
- DataFrame and Google Sheets — full read/write reference for
BareFrame - API Reference — complete method list for
BareFrame