Jason

Obsidian数据可视化 | 如何制作饼图,柱状图,曲线图。从0打造你的个人数据仪表盘

下载文件

核心对比

插件/方式核心理念易用性数据源动态更新能力外观与交互
Mermaid (内置)快速的文本到图表转换器极高静态文本❌ 不支持基础,无交互
Charts简洁的静态报告图表生成器高 (静态) / 低 (动态)静态YAML数据
(动态需DataviewJS辅助)
⚠️ DataviewJS 辅助简洁,有基础交互
Charts View功能强大的动态仪表盘构建器中等静态, CSV, 原生Dataview查询原生支持现代美观,交互丰富
Tracker自动化的个人数据追踪器中等自动扫描笔记内容/元数据完全自动化功能性,特有日历热力图

核心图表支持度/难度评估

难度等级说明:

  • 极简: 语法极其简单,几乎没有学习成本。
  • 简单: 语法直观,通过少量配置即可完成。
  • 中等: 需要理解插件的工作逻辑(如数据源、查询语法),配置步骤更多。
插件/方式 (Plugin/Method)饼图 (Pie Chart)柱状图 (Bar Chart)折线图 (Line Chart)核心优势 (Pros)核心劣势 (Cons)
Mermaid (内置)极简xychart 内测xychart 内测开箱即用,无需安装功能简陋,图表类型极少
Charts简单简单简单创建静态报告的最佳选择动态能力依赖编程,配置复杂
Charts View中等中等中等原生动态数据能力,功能强大学习曲线稍高,配置略繁琐
Tracker中等中等中等“一劳永逸”的自动化习惯追踪不适合做报告,只适合追踪

决策指南:如何选择?

场景一:“我只想快速示意一下,不关心精确数据。”

  • ➡️ 最佳选择:Mermaid
  • 理由:内置功能,无需安装。用几行简单的文本就能生成一个示意饼图,非常高效。

场景二:“我有一份整理好的数据,想为它做一份干净、正式的图表报告。”

  • ➡️ 最佳选择:Charts 插件
  • 理由:这是 Charts 插件最核心的用途。它的 YAML 语法非常直观,专门为这种“数据已知,需要展示”的静态报告场景而设计。

场景三:“我希望图表能‘活’起来,自动从我的笔记里抓取数据并更新。”

  • ➡️ 最佳选择:Charts View 插件
  • 理由:它的王牌功能是原生支持 Dataview 查询。您可以轻松构建一个动态仪表盘,例如按月统计您读过的书籍数量,而无需编写复杂的 DataviewJS 代码。

场景四:“我需要追踪一个日常习惯,并在日历上看到我的坚持情况。”

  • ➡️ 最佳选择:Tracker 插件
  • 理由Tracker 的设计理念就是自动化追踪。它会自动扫描您每日笔记中的特定标记(如任务完成状态、标签),并生成直观的日历热力图,是习惯养成的绝佳工具。

Obsidian数据图表案例-Charts

饼图

type: pie
labels: [Monday,Tuesday,Wednesday,Thursday,Friday]
series:
  - title: Title 1
    data: [1, 3, 3, 4, 5]
width: 50%
labelColors: true

柱状图

type: bar
labels: [Monday, Tuesday, Wednesday, Thursday, Friday]
series:
  - title: Title 1
    data: [2, 2, 3, 4, 5]
  - title: Title 2
    data: [5, 4, 3, 2, 1]
width: 60%
labelColors: true

折线图

type: line
labels: [Monday,Tuesday,Wednesday,Thursday,Friday]
series:
  - title: Title 1
    data: [1, 5, 3, 2, 5]
  - title: Title 2
    data: [3, 4, 2, 4, 1]

Fitness习惯完成比例(集成Dataview,读取日记中的数据)

// 1. 数据查询与计算逻辑修改
const habitToTrack = 'fitness';
const allTasks = dv.pages('"diary"').file.tasks;

const counts = {
    '已完成 (Done)': 0,
    '未完成 (Not Done)': 0
};

for (let task of allTasks) {
    // 只关心包含指定习惯关键词的任务
    if (task.text.includes(habitToTrack)) {
        if (task.completed) {
            counts['已完成 (Done)']++;
        } else {
            counts['未完成 (Not Done)']++;
        }
    }
}

// 2. 构建图表数据
const chartData = {
    type: 'pie',
    data: {
        labels: Object.keys(counts),
        datasets: [{
            label: 'Fitness 打卡状态',
            data: Object.values(counts),
            backgroundColor: [
                'rgba(75, 192, 192, 0.7)',  // 'Done' 的颜色 (绿色系)
                'rgba(255, 99, 132, 0.7)'   // 'Not Done' 的颜色 (红色系)
            ],
            borderColor: [
                'rgba(75, 192, 192, 1)',
                'rgba(255, 99, 132, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        // 【需求1】设置宽度是通过外层容器的样式实现的
        // Charts 插件本身没有直接的 width 参数,但我们可以控制它的容器
        responsive: true,
        maintainAspectRatio: false, // 允许图表自由调整以适应容器
        plugins: {
            title: {
                display: true,
                text: `'${habitToTrack}' 习惯完成状态`
            }
        }
    }
};

// 3. 渲染图表,并设置容器宽度
// 我们给 this.container 这个 div 元素设置一个 style 属性
this.container.style.width = '40%';
this.container.style.height = '400px'; // 建议同时设置一个高度
window.renderChart(chartData, this.container);
type: pie
labels: [a,b,c]
series:
  - title: test
    data: [1,2,3]
tension: 0.2
width: 80%
labelColors: false
fill: false
beginAtZero: false
bestFit: false
bestFitTitle: undefined
bestFitNumber: 0

Obsidian数据图表案例-ChartsView

饼图


type: Pie


data:
  - type: "Wage income per capita (¥)"
    value: 17917
  - type: "Operating net income per capita (¥)"
    value: 5307
  - type: "Property Per Capita Net Income (¥)"
    value: 2791
  - type: "Transfer of net income per capita (¥)"
    value: 6173


options:
  angleField: "value"
  colorField: "type"
  radius: 0.5
  label:
    type: "spider"
    content: "{percentage}\n{name}"
  legend:
    layout: "horizontal"
    position: "bottom"

柱状图

#-----------------#
#- chart type    -#
#-----------------#
type: Bar

#-----------------#
#- chart data    -#
#-----------------#
data:
  - action: "Browse the website"
    pv: 50000
  - action: "Add to cart"
    pv: 35000
  - action: "Generate orders"
    pv: 25000
  - action: "Pay order"
    pv: 15000
  - action: "Seal the deal"
    pv: 8500

#-----------------#
#- chart options -#
#-----------------#
options:
  xField: "pv"
  yField: "action"
  conversionTag: {}

折线图

#-----------------#
#- chart type    -#
#-----------------#
type: TinyLine

#-----------------#
#- chart data    -#
#-----------------#
data: [264, 417, 438, 887, 309, 397, 550, 575, 563, 430, 525, 592, 492, 467, 513, 546, 983, 340, 539, 243, 226, 192]

#-----------------#
#- chart options -#
#-----------------#
options:
  height: 60
  autoFit: false
  smooth: true
  tooltip: false
  annotations:
    - type: "line"
      start: ["min", "mean"]
      end: ["max", "mean"]
      style:
        stroke: "rgba(0, 0, 0, 0.45)"
      text:
        content: "Average"
        offsetY: -2
        style:
          textAlign: "left"
          fontSize: 10
          fill: "rgba(44, 53, 66, 0.45)"
          textBaseline: "bottom"
    - type: "line"
      start: ["min", 800]
      end: ["max", 800]
      style:
        stroke: "rgba(200, 0, 0, 0.55)"
      text:
        content: "Target"
        offsetY: -2
        style:
          textAlign: "left"
          fontSize: 10
          fill: "rgba(44, 53, 66, 0.45)"
          textBaseline: "bottom"

与dataview集成

# 1. 定义图表类型为饼图
type: Pie

# 2. 动态生成数据
data: |
  dataviewjs:
  // 使用 dataviewjs 查询数据
  return dv.pages('"diary"').file.tasks
           // 步骤 a: 只筛选出文本中包含 "fitness" 的任务
           .where(t => t.text.includes('fitness'))
           // 步骤 b: 按任务是否完成进行分组
           .groupBy(t => t.completed ? '已完成' : '未完成')
           // 步骤 c: 将分组结果映射成图表所需的格式
           .map(g => ({ type: g.key, value: g.rows.length }))
           // 步骤 d: 确保返回的是一个标准的数组
           .array();

# 3. 配置图表外观和选项
options:
  # 告诉图表,用数据中的 'value' 字段来决定扇区大小
  angleField: "value"
  
  # 告诉图表,用数据中的 'type' 字段来区分颜色和标签
  colorField: "type"
  
  # 设置饼图的半径大小
  radius: 0.8
  
  # 设置标签样式
  label:
    type: "spider" # 蜘蛛网样式的引导线
    content: "{name}: {value} ({percentage})" # 标签内容格式
    
  # 设置图例
  legend:
    layout: "horizontal"
    position: "bottom"
#-----------------#
#- chart type    -#
#-----------------#
type: Mix

#-----------------#
#- chart data    -#
#-----------------#
data.area:
  - time: 1246406400000
    temperature: [14.3, 27.7]
  - time: 1246492800000
    temperature: [14.5, 27.8]
  - time: 1246579200000
    temperature: [15.5, 29.6]
  - time: 1246665600000
    temperature: [16.7, 30.7]
  - time: 1246752000000
    temperature: [16.5, 25.0]
  - time: 1246838400000
    temperature: [17.8, 25.7]

data.line:
  - time: 1246406400000
    temperature: 21.5
  - time: 1246492800000
    temperature: 22.1
  - time: 1246579200000
    temperature: 23
  - time: 1246665600000
    temperature: 23.8
  - time: 1246752000000
    temperature: 21.4
  - time: 1246838400000
    temperature: 21.3

#-----------------#
#- chart options -#
#-----------------#
options:
  appendPadding: 8
  syncViewPadding: true
  tooltip:
    shared: true
    showMarkers: false
    showCrosshairs: true
    offsetY: -50

options.area:
  axes: {}
  meta:
    time:
      type: 'time'
      mask: 'MM-DD'
      nice: true
      tickInterval: 172800000
      range: [0, 1]
    temperature:
      nice: true
      sync: true
      alias: '温度范围'
  geometries:
    - type: 'area'
      xField: 'time'
      yField: 'temperature'
      mapping: {}

options.line:
  axes: false
  meta:
    time:
      type: 'time'
      mask: 'MM-DD'
      nice: true
      tickInterval: 172800000
      range: [0, 1]
    temperature:
      sync: 'temperature'
      alias: '温度'
  geometries:
    - type: 'line'
      xField: 'time'
      yField: 'temperature'
      mapping: {}
    - type: 'point'
      xField: 'time'
      yField: 'temperature'
      mapping:
        shape: 'circle'
        style:
          fillOpacity: 1

Obsidian数据图表案例-Tracker

饼状图

searchType: task.done, task.notdone
searchTarget: fitness, fitness
folder: diary
endDate: 2025-07-31
pie:
    title: 已完成任务分布
    data: '{{sum(dataset(0))}},{{sum(dataset(1))}}'
    dataColor: '#4daf4a,#377eb8'
    label: 健身了,偷懒了
    ratioInnerRadius: 0.3
    extLabel: '健身了, 偷懒了'
    dataName: 健身了, 偷懒了
	showLegend: true
    legendPosition: right
    legendOrientation: vertical

柱状图

searchType: dvField
searchTarget: 喝水
folder: diary
bar:
    title: 每日饮水量追踪
    yAxisLabel: 饮水量
    yAxisUnit: ml
    barColor: #377eb8
    xAxisTickInterval: 1w
searchType: task.done
searchTarget: fitness,reading,meditation
folder: diary
stack: true
bar:
    title: 每日完成习惯统计
    yAxisLabel: 完成数量
    barColor: '#4daf4a,#377eb8,#ff7f00'

折线图

searchType: dvField
searchTarget: 喝水
folder: diary
endDate: 2025-07-31
line:
    title: 本月饮水量走势
    yAxisLabel: 饮水量
    yAxisUnit: ml
    lineColor: dodgerblue
    showPoint: true
    xAxisTickInterval: 7d
    xAxisTickLabelFormat: MM-DD
    extLabel: '健身了, 偷懒了'
    dataName: 健身了, 偷懒了
	showLegend: true
    legendPosition: right
    legendOrientation: vertical