資料透視表
資料透視表是一種強大的資料分析工具,允許使用者快速彙總、分析和視覺化大量資料。它支援多種資料操作和格式設定選項,幫助使用者更直觀地理解資料。
簡介
資料透視表通常用於以下場景:
- 彙總資料:資料透視表可以幫助你快速彙總資料,以便更好地瞭解資料的整體情況。
- 分析資料:資料透視表可以幫助你分析資料,找出資料中的模式和關係。
- 探索資料:資料透視表可以幫助你探索資料,發現資料中的規律和趨勢。
- 視覺化資料:資料透視表可以幫助你將資料視覺化,以便更好地理解資料。
資料透視表通常包含以下幾個要素:
- 行標籤:資料透視表中的行標籤用於對資料進行分組。
- 列標籤:資料透視表中的列標籤用於對資料進行分組。
- 值:資料透視表中的值用於對資料進行彙總。
在資料透視表中,你可以根據需要對行標籤、列標籤和值進行調整,以便更好地分析資料。
資料來源
在 Univer Sheets 中,資料透視表的資料來源可以是一個工作表(Worksheet),也可以是一個資料區域。你可以根據需要選擇不同的資料來源,以便更好地分析資料。 我們會以資料來源的第一行作為預設的列標籤,其他部分作為資料來源。因此我們要求資料來源儘可能的歸整,儘可能避免空行,空列。 在同一列的資料中,我們會自動識別資料型別,以便更好的支援資料透視表的功能。假設在某一列中,資料型別不一致,我們會預設將該列的資料型別設定為字串。
在 Univer Sheets 中,資料透視表會自動響應資料的變化,當資料來源發生變化時,資料透視表會自動更新,以便更好地分析資料。
篩選
資料透視表支援對行標籤和列標籤進行篩選,同時提供篩選欄位,該欄位不會參與資料透視表主要資料分類彙總部分的排版,但是可以用於篩選資料。
資料透視表支援兩種篩選方式:
- 標籤篩選:標籤篩選是直接作用於當前標籤的維度的項(item), 例如在省份維度下,我們可以過濾掉部分省份。
- 值篩選:值篩選需要指定一個值維度,然後會對值欄位的彙總結果進行篩選,例如我們可以過濾掉銷售額小於 1000 的資料。
排序
資料透視表的排序只會在行/列欄位生效,排序方式支援升序和降序,排序方式使用 localCompare 方式。後續我們會支援更多的排序方式,例如拼音排序。
資料透視表的排序支援兩種方式:
- 標籤排序:標籤排序是直接作用於當前標籤的維度的項(item), 例如在省份維度下,我們可以按照省份名稱排序。
- 值排序:值排序需要指定一個值維度,然後會對值欄位的彙總結果進行排序,例如我們可以按照銷售額排序。
彙總
資料透視表支援 11 種 excel 支援的彙總方式,包括:求和、計數、資料計數、平均值、最大值、最小值、階乘、標準差、方差、總體標準差、總體方差。
資料透視表支援多個值維度,同時可以客製化多值維度所在區域(value position)和位置(value index)。
這是很複雜的排版邏輯, 因此我們僅支援了表格檢視,後續我們會根據情況支援更多的檢視。
下鑽
下鑽是指在資料透視表的值區域中,使用者可以透過雙擊某個單元格,檢視該單元格對應的詳細資料。例如,使用者可以在資料透視表中點選某個單元格,檢視該單元格的詳細資料,以便更好地追溯原資料。
分組
Univer Sheets 支援日期、數字和手動分組。
- 日期分組:透過
setDateGroupType設定衍生日期欄位的分組類型。若要按日曆單位對原始日期標籤欄位分組,先透過setDateGroupingMode設定univerAPI.Enum.PivotDateGroupingMode.ExcelLike,再呼叫setExcelDateGroup。 - 數字分組:向
setNumberGroup傳入標籤欄位 ID 和區間設定。 - 手動分組:向
setManualGroups傳入原始來源欄位 ID 和保留值類型的成員分組。每組至少包含兩個成員;傳入空陣列可移除該欄位的全部手動分組。
使用交叉分析篩選器和時間軸可以互動式篩選表格和樞紐報表。
外掛模式
安裝
pnpm add @univerjs-pro/sheets-pivot @univerjs-pro/sheets-pivot-uinpm install @univerjs-pro/sheets-pivot @univerjs-pro/sheets-pivot-uiyarn add @univerjs-pro/sheets-pivot @univerjs-pro/sheets-pivot-uibun add @univerjs-pro/sheets-pivot @univerjs-pro/sheets-pivot-ui使用
import { UniverSheetsPivotTablePlugin } from '@univerjs-pro/sheets-pivot'import { UniverSheetsPivotTableUIPlugin } from '@univerjs-pro/sheets-pivot-ui'import SheetsPivotTableUIZhTW from '@univerjs-pro/sheets-pivot-ui/locale/zh-TW'import SheetsPivotTableZhTW from '@univerjs-pro/sheets-pivot/locale/zh-TW'import { LocaleType, mergeLocales, Univer } from '@univerjs/core'import '@univerjs-pro/sheets-pivot-ui/lib/index.css'import '@univerjs-pro/sheets-pivot/facade'const univer = new Univer({ locale: LocaleType.ZH_TW, locales: { [LocaleType.ZH_TW]: mergeLocales( SheetsPivotTableZhTW, SheetsPivotTableUIZhTW, ), },})外掛註冊
透視表外掛支援針對不同的使用場景靈活註冊,提供兩種模式以適配不同的計算需求。
單獨註冊在主程序
透視表外掛可以單獨在主程序中註冊。這種模式適用於輕量級任務,操作簡單,無需額外配置外掛 config。開發者只需引入 UniverSheetsPivotTablePlugin 即可。
univer.registerPlugin(UniverSheetsPivotTablePlugin)univer.registerPlugin(UniverSheetsPivotTableUIPlugin)主程序和 worker 程序同時註冊
對於需要高效能的場景,透視表外掛支援同時註冊在主程序和 worker 程序中。透過這種模式,外掛可以利用多執行緒架構實現高效的資料分發和計算處理,顯著減輕主程序的計算負擔,並提升整體效能。
在這種模式下,主程序主要負責資料的分發、同步和渲染,worker 程序專注於複雜的計算任務。為實現這一模式,需要在註冊時透過 config 進行精細化配置。
// 瀏覽器主程序外掛註冊檔案// notExecuteFormula 為 true 表示主程序不執行計算,只負責資料的分發、同步和渲染univer.registerPlugin(UniverSheetsPivotTablePlugin, { notExecuteFormula: true })univer.registerPlugin(UniverSheetsPivotTableUIPlugin)// web worker 程序外掛註冊檔案// notExecuteFormula 為 false 表示 worker 程序執行計算univer.registerPlugin(UniverSheetsPivotTablePlugin, { notExecuteFormula: false })如果你擁有 Univer 的商業許可證,請參考在客戶端使用許可證進行配置。
外掛與配置
import type { DateSystem } from '@univerjs/core'interface IUniverSheetsPivotConfig { /** * 定義資料透視表中每個字段可顯示的最大項目數。 * @default 1000 */ maxLimitItemCount?: number /** * 將日期欄位新增至列或欄時,自動建立日期群組。 * @default true */ autoDateGroup?: boolean /** * 日期群組使用的日期系統。 * @default DateSystem.Date1900 */ dateSystem?: DateSystem}行動裝置支援
先設定行動版編輯器。在本頁外掛模式的初始化程式碼中,將下列桌面版註冊替換為同一套件匯出的行動版外掛,並在開啟文件前完成。保留模型外掛、樣式、語言套件與 Facade 匯入,請勿同時註冊兩個版本。
| 套件 | 桌面版註冊 | 行動版註冊 |
|---|---|---|
@univerjs-pro/sheets-pivot-ui | UniverSheetsPivotTableUIPlugin | UniverSheetsPivotTableMobileUIPlugin |
預設模式
列印功能被包含在 @univerjs/preset-sheets-advanced 預設中。
安裝
@univerjs/preset-sheets-advanced 的 UniverSheetsAdvancedPreset 預設在執行時依賴 UniverSheetsDrawingPreset 預設,請先安裝 @univerjs/preset-sheets-drawing。
pnpm add @univerjs/preset-sheets-drawing @univerjs/preset-sheets-advancednpm install @univerjs/preset-sheets-drawing @univerjs/preset-sheets-advancedyarn add @univerjs/preset-sheets-drawing @univerjs/preset-sheets-advancedbun add @univerjs/preset-sheets-drawing @univerjs/preset-sheets-advanced使用
import { UniverSheetsAdvancedPreset } from '@univerjs/preset-sheets-advanced'import UniverPresetSheetsAdvancedZhTW from '@univerjs/preset-sheets-advanced/locales/zh-TW'import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core'import UniverPresetSheetsCoreZhTW from '@univerjs/preset-sheets-core/locales/zh-TW'import { UniverSheetsDrawingPreset } from '@univerjs/preset-sheets-drawing'import UniverPresetSheetsDrawingZhTW from '@univerjs/preset-sheets-drawing/locales/zh-TW'import { createUniver, LocaleType, mergeLocales } from '@univerjs/presets'import '@univerjs/preset-sheets-core/lib/index.css'import '@univerjs/preset-sheets-drawing/lib/index.css'import '@univerjs/preset-sheets-advanced/lib/index.css'const { univerAPI } = createUniver({ locale: LocaleType.ZH_TW, locales: { [LocaleType.ZH_TW]: mergeLocales( UniverPresetSheetsCoreZhTW, UniverPresetSheetsDrawingZhTW, UniverPresetSheetsAdvancedZhTW, ), }, presets: [ UniverSheetsCorePreset(), UniverSheetsDrawingPreset(), UniverSheetsAdvancedPreset(), ],})如果你擁有 Univer 的商業許可證,請參考在客戶端使用許可證進行配置。
預設與配置
interface IUniverSheetsAdvancedPresetConfig { pivot?: Pick<IUniverSheetsPivotConfig, 'maxLimitItemCount'>}interface IUniverSheetsPivotConfig { /** * 定義資料透視表中每個字段可顯示的最大項目數。 * @default 1000 */ maxLimitItemCount?: number}Facade API
完整 Facade API 型別定義,請檢視 FacadeAPI。
引入
外掛模式提示
僅外掛模式需要手動引入 Facade 套件。預設模式已內建對應的 Facade 套件,無需額外引入。
import '@univerjs-pro/sheets-pivot/facade'新增透視表
FWorkbook.addPivotTable 方法用於新增透視表,成功會返回 FPivotTable 例項。
// 需要確保工作表範圍 A1:G9 是非空的const fWorkbook = univerAPI.getActiveWorkbook()const unitId = fWorkbook.getId()const fSheet = fWorkbook.getActiveSheet()const subUnitId = fSheet.getSheetId()const sheetName = fSheet.getSheetName()const sourceInfo = { unitId, subUnitId, sheetName, range: { startRow: 0, startColumn: 0, endRow: 8, endColumn: 6, },}const anchorCellInfo = { unitId, subUnitId, row: 20, col: 8,}const fPivotTable = await fWorkbook.addPivotTable(sourceInfo, univerAPI.Enum.PositionTypeEnum.Existing, anchorCellInfo)const pivotTableId = fPivotTable.getPivotTableId()// 設定狀態,避免重複新增維度let hasAdded = false// 透視表的建立是非同步的,因此需要等待透視表渲染完成後再新增維度univerAPI.addEvent(univerAPI.Event.PivotTableRendered, async (params) => { if (!hasAdded && params.pivotTableId === pivotTableId) { hasAdded = true await fPivotTable.addField(1, univerAPI.Enum.PivotTableFiledAreaEnum.Row, 0) await fPivotTable.addField(1, univerAPI.Enum.PivotTableFiledAreaEnum.Value, 0) }})獲取透視表
FWorkbook.getPivotTableByCell 方法透過錨定單元格獲取透視表例項。如果目標單元格在透視表中,它將返回 FPivotTable 例項。
const fWorkbook = univerAPI.getActiveWorkbook()const unitId = fWorkbook.getId()const fSheet = fWorkbook.getActiveSheet()const subUnitId = fSheet.getSheetId()// 從 FWorkbook 中獲取透視表例項const fPivotTable = fWorkbook.getPivotTableByCell(unitId, subUnitId, 0, 8)// 從 FSheet 中獲取透視表例項const pivotTable2 = fSheet.getPivotTableByCell(0, 8)FPivotTable 例項中有一些方法,可以用來操作透視表。
| 方法 | 描述 |
|---|---|
| remove | 刪除透視表例項 |
| getConfig | 獲取當前透視表的配置資訊,其中包括資料來源範圍,錨定單元格, 維度配置資訊 |
| addField | 新增一個維度 |
| removeField | 刪除一個維度 |
| updateFieldPosition | 更新維度的所在區域以及順序 |
| updateValuePosition | 更新 ∑Value 的位置 |
| setSubtotalType | 設定彙總方式 |
| setLabelSort | 設定標籤排序 |
| setLabelManualFilter | 設定標籤篩選 |
| renameField | 重新命名維度 |
| setValueFilter | 設定值維度 |
| reset | 重置透視表的維度配置 |
| setFieldsConfig | 設定透視表的維度配置 |
| getValueFilters | 獲取透視表的值維度配置 |
事件監聽
完整事件型別定義,請檢視 Events。
univerAPI.Event.BeforePivotTableAdd 事件在新增透視表之前觸發。
const disposable = univerAPI.addEvent(univerAPI.Event.BeforePivotTableAdd, (params) => { const { positionType, targetCellInfo } = params if (positionType === univerAPI.Enum.PositionTypeEnum.Existing && targetCellInfo.sheetName === 'Sheet 1') { // 取消新增透視表操作 params.cancel = true console.log(`無法將透視表新增到 ${targetCellInfo.sheetName} 工作表`) }})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableAdded 事件在新增透視表之後觸發。
const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableAdded, (params) => { const { positionType, targetCellInfo } = params if (positionType === univerAPI.Enum.PositionTypeEnum.Existing) { console.log('在現有工作表中建立了一個透視表') console.log(` 目標工作表名稱為 ${targetCellInfo.sheetName}, 目標行為 ${targetCellInfo.row}, 目標列為 ${targetCellInfo.column} `) } else { console.log('在新工作表中建立了一個透視表') }})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.BeforePivotTableMove 事件在移動透視表之前觸發。
const disposable = univerAPI.addEvent(univerAPI.Event.BeforePivotTableMove, (params) => { const { pivotTableId, targetCellInfo } = params if (pivotTableId === 'pivotTable1' && targetCellInfo.sheetName === 'Sheet 1') { // 取消移動透視表操作 params.cancel = true console.log(`無法將透視表移動到 ${targetCellInfo.sheetName} 工作表`) }})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableMoved 事件在移動透視表之後觸發。
const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableMoved, (params) => { const { pivotTableId, targetCellInfo, originTargetInfo } = params if (pivotTableId === 'pivotTable1') { console.log(` 透視表從行: ${originTargetInfo.row} & 列: ${originTargetInfo.column} 移動到工作表 ${targetCellInfo.sheetName} 行: ${targetCellInfo.row} & 列: ${targetCellInfo.column} `) }})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableRendered 事件在透視表渲染完成後觸發。
// import { unionPivotViewRange } from '@univerjs-pro/sheets-pivot';const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableRendered, (params) => { const { pivotTableId, rangeInfo } = params console.log(`透視表 ${pivotTableId} 已渲染`) if (rangeInfo) { console.log(unionPivotViewRange(rangeInfo)) }})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableRemoved 事件在透視表被刪除後觸發。
const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableRemoved, (params) => { const { pivotTableId } = params console.log(`透視表 ${pivotTableId} 已被刪除`)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldAdded 事件在透視表新增維度後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldAdded, (params) => { const { pivotTableId, fieldId, fieldArea, fieldIndex } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(fieldSetting)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldRemoved 事件在透視表刪除維度後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldRemoved, (params) => { const { pivotTableId, fieldId } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(fieldSetting)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldMoved 事件在透視表移動維度後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldMoved, (params) => { const { pivotTableId, fieldId, fieldArea, fieldIndex } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(`源名稱 ${fieldSetting.sourceName} 顯示名稱 ${fieldSetting.displayName}`)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldCollapseChanged 事件在透視表展開/摺疊維度後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldCollapseChanged, (params) => { const { pivotTableId, fieldId, isCollapsed } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(`源名稱 ${fieldSetting.sourceName} 顯示名稱 ${fieldSetting.displayName}`)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldFilterChanged 事件在透視表維度篩選後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldFilterChanged, (params) => { const { pivotTableId, fieldId, filter } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(`檢查列表篩選值為 ${fieldSetting.filterInfo.checklist}`)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldSortChanged 事件在透視表維度排序後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldSortChanged, (params) => { const { pivotTableId, fieldId, sort } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(`排序資訊為 ${fieldSetting.sortInfo}`)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableFieldSettingChanged 事件在透視表維度設定變更後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableFieldSettingChanged, (params) => { const { pivotTableId, fieldId, setting } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const fieldSetting = pivotTable.getFieldSetting(fieldId) console.log(`源名稱 ${fieldSetting.sourceName} 顯示名稱 ${fieldSetting.displayName}`)})// 移除事件監聽器,使用 `disposable.dispose()`univerAPI.Event.PivotTableValuePositionChanged 事件在透視表值位置變更後觸發。
const fWorkbook = univerAPI.getActiveWorkbook()const disposable = univerAPI.addEvent(univerAPI.Event.PivotTableValuePositionChanged, (params) => { const { pivotTableId, valueId, position } = params const pivotTable = fWorkbook.getPivotTableById(pivotTableId) const valueSetting = pivotTable.getValueSetting(valueId) console.log(`源名稱 ${valueSetting.sourceName} 顯示名稱 ${valueSetting.displayName}`)})// 移除事件監聽器,使用 `disposable.dispose()`值顯示方式
使用 setShowDataAs() 將值欄位顯示為列、欄或總計的百分比,也可顯示相對於基準項目的差異。這只改變顯示計算,不會取代來源資料。getShowDataAs() 讀取設定,resetShowDataAs() 還原一般計算。
const valueId = fPivotTable.getFieldIdsByArea( univerAPI.Enum.PivotTableFiledAreaEnum.Value,)[0]if (valueId) { await fPivotTable.setShowDataAs(valueId, { type: univerAPI.Enum.PivotShowAsTypeEnum.percentOfGrandTotal, }) console.log(fPivotTable.getShowDataAs(valueId))}範例中的 fPivotTable 是既有樞紐分析表。請使用值欄位 ID,而非來源欄或列欄位 ID。差異與累計等模式還需要設定對應的基準欄位與基準項目。
隨工作表複製樞紐分析表
註冊樞紐分析表外掛後,複製工作表也會複製表上的樞紐分析表並保留欄位設定。每個副本會取得新的樞紐分析表 ID。若資料來源位於被複製的工作表,參照會改為指向新工作表;若來源位於其他工作表或活頁簿,則保留原參照。樞紐分析表副本會隨工作表複製操作一起復原和重做。
const workbook = univerAPI.getActiveWorkbook()if (!workbook) throw new Error('No active workbook')const copiedSheet = workbook.duplicateActiveSheet()console.log(copiedSheet.getSheetName())多重篩選與日期條件
setOptions({ allowMultipleFilters: true }) 允許同一欄位同時使用成員選取、標籤/日期條件和值篩選。省略或設為 false 時,不同類別的篩選互相取代;關閉選項時若存在類別衝突,會清除報表篩選。同一欄位仍最多只能有一個值篩選。
setDateFilter() 支援 dateEqual、dateOlderThan、dateBetween 等固定日期條件。expected 使用活頁簿日期系統中的 Excel 序列值,區間條件傳入兩個值。useWholeDay: true 忽略一天內的時間;省略或設為 false 時按精確序列值比較。參數使用報表欄位 ID,而非來源欄索引。以下沿用前文的 fPivotTable,並要求資料來源包含 Date 欄。
getDateFilter() 回傳獨立的條件副本,clearDateFilter() 僅清除日期條件。操作支援復原及協作。請檢查回傳的布林值:已生效的交叉分析篩選器可能阻止不相容的篩選條件。
if (!fPivotTable) throw new Error('PivotTable not found')await fPivotTable.setOptions({ allowMultipleFilters: true })const dateField = Object.values(fPivotTable.getConfig()?.fieldsConfig.dimension ?? {}) .find((field) => field.sourceName === 'Date')if (!dateField) throw new Error('Add the Date field to the report first')await fPivotTable.setDateFilter(dateField.id, { operator: univerAPI.Enum.ST_PivotFilterOperatorEnum.dateEqual, expected: 45293, useWholeDay: true,})console.log(fPivotTable.getDateFilter(dateField.id))await fPivotTable.clearDateFilter(dateField.id)GETPIVOTDATA
使用 =GETPIVOTDATA("Sales",$I$1,"Region","East"),從左上角位於 I1 的可見樞紐分析表報表讀取彙總值。值欄位可使用顯示名稱或來源欄位名稱。錨點必須是單一儲存格參照,欄位/項目參數必須成對提供。
目前實作從錨點最多向下掃描 200 列、向右掃描 50 欄可見報表,不查詢隱藏的來源記錄。找不到欄位、項目或結果時回傳 #REF!;參數數量不合法或錨點不是單一儲存格參照時回傳 #VALUE!。
你覺得這篇文檔如何?