Plotly Shapes: Hướng dẫn chú thích và tạo kiểu cho biểu đồ
Tìm hiểu cách nâng tầm câu chuyện dữ liệu với các shape trong Plotly. Hướng dẫn này cung cấp quy trình toàn diện để thêm và tạo kiểu chú thích trong Python, từ hình chữ nhật và đường cơ bản đến hình học tương tác nâng cao.
Plotly là một trong những thư viện Python mạnh mẽ và phổ biến nhất để tạo đồ thị tương tác. Việc tạo biểu đồ cơ bản với Plotly khá đơn giản, nhưng để trực quan hóa dữ liệu thực sự hiệu quả, bạn thường cần thêm các yếu tố bổ sung để dẫn dắt sự chú ý của người xem và cung cấp bối cảnh quan trọng. Đây chính là lúc các shape trong Plotly phát huy giá trị.
Shape là các phần tử đồ họa như hình chữ nhật, đường thẳng và hình tròn mà bạn có thể vẽ chồng lên hình vẽ Plotly để chú thích, làm nổi bật và thêm bối cảnh quan trọng. Chúng là chìa khóa để nâng một biểu đồ tiêu chuẩn thành trực quan hóa giàu thông tin, sâu sắc và đạt chất lượng xuất bản.
Hình ảnh: Tác giả
Trong bài viết này, chúng ta sẽ tìm hiểu mọi thứ bạn cần biết về plotly shapes, từ triển khai cơ bản đến các kỹ thuật tùy biến nâng cao. Với người mới bắt đầu dùng Plotly, việc làm quen với giao diện cấp cao của nó là bước khởi đầu tuyệt vời, và khóa học Introduction to Data Visualization with Plotly in Python có thể giúp bạn nắm vững các nền tảng trước khi đi sâu vào các tùy chỉnh như shapes.
Plotly Shapes là gì?
Plotly shapes là đồ họa vector được vẽ trên hình vẽ để cung cấp bối cảnh bổ sung hoặc làm nổi bật các khu vực cụ thể. Những chú thích này rất quan trọng để xây dựng đồ thị không chỉ đẹp mắt mà còn giàu thông tin.
Tầm quan trọng của shapes trong trực quan hóa dữ liệu
Trong trực quan hóa dữ liệu, tính rõ ràng là điều quan trọng. Trong khi các biểu đồ như scatter hay bar thể hiện dữ liệu, shapes giúp kể câu chuyện đằng sau dữ liệu. Chúng phục vụ hai chức năng chính:
Làm nổi bật xu hướng dữ liệu: Shapes có thể thu hút sự chú ý đến các giai đoạn hoặc sự kiện then chốt trong dữ liệu. Ví dụ, bạn có thể dùng hình chữ nhật để tô bóng giai đoạn suy thoái trên đồ thị chuỗi thời gian GDP hoặc vẽ một đường để đánh dấu thay đổi chính sách quan trọng. Gợi ý trực quan này giúp người xem ngay lập tức nắm được tác động của các yếu tố bên ngoài lên dữ liệu.
Cung cấp chú thích theo bối cảnh: Shapes có thể khoanh tròn hoặc chỉ vào các điểm dữ liệu hay cụm cụ thể, như ngoại lệ, nhóm quan tâm, hoặc ngưỡng quan trọng. Hãy hình dung một biểu đồ kiểm soát chất lượng nơi bạn dùng hình tròn để gắn cờ các phép đo vượt ngoài giới hạn chấp nhận. Điều này giúp việc diễn giải dữ liệu nhanh và trực quan hơn.
Nếu bạn muốn xem nhanh các kiểu trực quan hóa dữ liệu phổ biến, khi nào nên dùng và các trường hợp sử dụng thường gặp, hãy xem cheat sheet Trực quan hóa dữ liệu của chúng tôi.
Tổng quan về các loại shape trong Plotly
Plotly hỗ trợ nhiều loại hình học có thể thêm vào layout của hình vẽ. Mỗi loại được xác định bởi type và các tham số tọa độ cụ thể.
Dưới đây là cái nhìn ngắn gọn về các shape chính:
Hình chữ nhật (type: 'rect'): Xác định bởi hai góc đối diện (x0, y0) và (x1, y1). Hình chữ nhật rất phù hợp để tô bóng vùng quan tâm hoặc làm nổi bật một khoảng dữ liệu cụ thể.
Đường thẳng (type: 'line'): Xác định bởi điểm bắt đầu (x0, y0) và điểm kết thúc (x1, y1). Đường thẳng rất hữu ích để tạo đường xu hướng, ngưỡng hoặc mốc tham chiếu.
Hình tròn (type: 'circle'): Xác định bởi tâm (x0, y0) và bán kính. Hình tròn lý tưởng để nhấn mạnh các điểm dữ liệu riêng lẻ hoặc cụm nhỏ. Lưu ý rằng để tạo hình tròn, bạn thường cần đặt tỷ lệ khung hình bằng nhau cho trục (fig.update_layout(yaxis_scaleanchor="x")) để tránh trông như hình bầu dục.
Path (type: 'path'): Xác định bởi một chuỗi đường dẫn SVG. Path cho phép bạn vẽ các hình tự do phức tạp, bao gồm đa giác. Bạn cung cấp một chuỗi lệnh (như M để di chuyển, L để vẽ đường, Z để đóng đường) để vẽ bất kỳ hình nào bạn cần. Đây là tùy chọn linh hoạt nhất để tạo chú thích hình học tùy chỉnh.
Hiểu các loại cơ bản này là bước đầu để thành thạo chú thích trực quan trong các hình vẽ Plotly của bạn.
Thêm Shapes vào đồ thị Plotly
Triển khai plotly shapes trong trực quan hóa của bạn liên quan đến hai cách tiếp cận chính: phương thức add_shape() cho từng shape riêng lẻ và cách update_layout() cho nhiều shape. Cả hai đều có ưu điểm riêng tùy trường hợp sử dụng và quy trình làm việc của bạn. Hãy xem cách thêm shapes bằng phương thức add_shape().
Cách thêm shapes bằng add_shape()?
Phương thức chính để thêm một shape vào hình vẽ Plotly là fig.add_shape(). Phương thức add_shape() là một hàm của đối tượng plotly.graph_objects.Figure. Bạn tạo hình vẽ cơ sở (ví dụ, scatter hoặc line) rồi gọi phương thức này để xếp lớp shape lên trên.
Điều này đặc biệt hữu ích khi bạn cần thêm shapes theo lập trình dựa trên các điều kiện hoặc giá trị dữ liệu. Hãy xem ví dụ cơ bản nơi chúng ta làm nổi bật một vùng cụ thể trên đồ thị chuỗi thời gian.
import plotly.graph_objects as go
import numpy as np
# Create sample data
x = np.linspace(0,10,100)
y = np.sin(x)+ np.random.normal(0,0.1,100)# Create base figure
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='Signal'))# Add a shape using add_shape method
fig.add_shape(type="rect",# Shape type
x0=2, x1=4,# X coordinates
y0=-2, y1=2,# Y coordinates
fillcolor="rgba(255, 0, 0, 0.3)",# Fill color with transparency
line=dict(color="red", width=2)# Border styling)
fig.show()Được hỗ trợ bởi
Kết quả:
Tính năng add_shape() của plotly trở nên đặc biệt mạnh khi bạn hiểu hệ tọa độ. Shapes có thể tham chiếu các hệ tọa độ khác nhau bằng tham số xref và yref. Chúng chỉ định khung tọa độ: dùng 'x'/'y' để diễn giải theo đơn vị dữ liệu, hoặc 'paper' để dùng vùng vẽ đã chuẩn hóa, nơi 0 – 1 phủ toàn bộ chiều rộng/chiều cao từ góc trái-dưới đến phải-trên. Hãy sửa đồ thị trên bằng các tham số này như dưới đây:
# Shape referenced to data coordinates (default)
fig.add_shape(type="line", x0=1, x1=9, y0=0, y1=0,
line=dict(color="green", width=3))# Shape referenced to paper coordinates (0-1 scale)
fig.add_shape(type="rect", xref="paper", yref="paper",
x0=0.1, x1=0.9, y0=0.1, y1=0.9,
fillcolor="rgba(0, 0, 255, 0.1)")Được hỗ trợ bởi
Kết quả:
Tùy biến hình chữ nhật và đường thẳng
Hình chữ nhật và đường thẳng là những shape được dùng nhiều nhất cho chú thích. Plotly cung cấp nhiều tùy chọn để điều khiển diện mạo của chúng. Để tra cứu nhanh cú pháp Plotly phổ biến, Plotly Express Cheat Sheet là tài nguyên vô giá. Hãy xem cách tùy biến các shape này.
Tùy biến hình chữ nhật
Hình chữ nhật được xác định bởi hai điểm (x0,y0 và x1, y1). Bạn có thể tạo kiểu phần nền và viền riêng biệt.
# Advanced rectangle customization
fig.add_shape(type="rect",
x0=3, x1=7,
y0=-1.5, y1=1.5,# Fill properties
fillcolor="rgba(50, 171, 96, 0.4)",# Line properties
line=dict(
color="darkgreen",
width=3,
dash="dashdot"# Options: solid, dot, dash, longdash, dashdot),# Layer control
layer="below"# Options: above, below)Được hỗ trợ bởi
Kết quả:
Tùy biến đường thẳng
Bạn có thể thêm đường bằng các phương thức chuyên dụng ‘add_hline’ và ‘add_vline’ cho đường ngang và dọc chạy hết biểu đồ, hoặc qua phương thức ‘add_shape’ tổng quát để kiểm soát chính xác hơn. Hãy xem ví dụ:
Những ví dụ này cho thấy bạn có thể dễ dàng thêm và tạo kiểu các shape cơ bản của Plotly để làm cho đồ thị giàu thông tin hơn.
Cấu hình và Tùy biến Nâng cao
Vượt ra ngoài việc thêm shape cơ bản, Plotly cung cấp công cụ mạnh để quản lý nhiều shape và kiểm soát thứ tự hiển thị của chúng. Các tính năng nâng cao này rất cần thiết để tạo trực quan hóa phức tạp, nhiều lớp và động. Hãy xem một số cách cấu hình và tùy biến đồ thị.
Sử dụng plotly.update_layout cho cập nhật động
Trong khi phương thức add_shape() phù hợp để thêm từng shape, phương thức update_layout() hiệu quả hơn để quản lý một tập hợp shape, đặc biệt khi chúng được tạo theo lập trình. Thay vì gọi hàm cho từng shape, bạn có thể truyền trực tiếp danh sách các dictionary shape vào tham số shapes trong update_layout().
Hãy xem ví dụ sử dụng phương thức này:
import plotly.graph_objects as go
import numpy as np
# Sample scatter plot data
x_data = np.linspace(0,10,50)
y_data = np.sin(x_data)+ np.random.normal(0,0.1,50)
fig = go.Figure(data=go.Scatter(x=x_data, y=y_data, mode='markers', name='Data Points'))# Define a list of shapes to add
shapes_to_add =[# A rectangle highlighting a peak areadict(type="rect",
x0=1, x1=2.5, y0=0.8, y1=1.2,
fillcolor="rgba(255, 165, 0, 0.4)",
line_width=0),# A vertical line marking an eventdict(type="line",
x0=7.5, x1=7.5, y0=0, y1=1,
yref="paper",# Span the full plot height
line=dict(color="red", width=2, dash="dot"))]# Add all shapes at once using update_layout
fig.update_layout(
shapes=shapes_to_add
)
fig.show()Được hỗ trợ bởi
Kết quả:
Cách này đặc biệt mạnh cho ứng dụng động, như dashboard Dash nơi shapes cần thay đổi theo đầu vào người dùng. Bạn có thể tạo danh sách shape mới và cập nhật layout trong một thao tác gọn gàng. Cách tiếp cận này giúp mã của bạn ngăn nắp và thường hiệu quả hơn khi thêm số lượng lớn shape cùng lúc.
Xếp lớp và kiểm soát z-index
Khi thêm shapes vào biểu đồ, việc kiểm soát chúng hiển thị trên hay dưới dữ liệu là quan trọng. Plotly quản lý điều này qua thuộc tính layer. Dù không chi tiết như z-index trong CSS, nó vẫn cung cấp kiểm soát cần thiết về thứ tự chồng lớp.
Thuộc tính layer có thể đặt một trong hai giá trị:
'above': Shape được vẽ đè lên các trace dữ liệu. Đây là mặc định và hữu ích cho chú thích cần hiển thị rõ.
'below': Shape được vẽ bên dưới các trace dữ liệu và lưới. Lý tưởng cho tô nền hoặc làm nổi vùng mà không che khuất điểm dữ liệu.
Thứ tự của shapes trong danh sách shapes cũng quan trọng: shape định nghĩa sau sẽ vẽ đè lên shape định nghĩa trước (trong cùng layer).
Đây là ví dụ minh họa hiệu ứng xếp lớp:
import plotly.graph_objects as go
fig = go.Figure(data=go.Scatter(x=[1,2,3,4,5], y=[5,3,4,2,6], mode='markers', marker_size=20, name='Data'))# Add a shape BELOW the data points
fig.add_shape(type="rect",
x0=2.5, y0=2.5, x1=4.5, y1=4.5,
fillcolor="LightPink",
opacity=0.8,
layer="below",# Key parameter
line_width=0)# Add a shape ABOVE the data points
fig.add_shape(type="circle",
x0=0.5, y0=4.5, x1=1.5, y1=5.5,
fillcolor="LightSeaGreen",
opacity=0.6,
layer="above",# Key parameter
line_width=0)
fig.update_layout(
xaxis=dict(showgrid=False),# Turn off gridlines to see effect clearly
yaxis=dict(showgrid=False))
fig.show()Được hỗ trợ bởi
Kết quả:
Như bạn thấy, hình chữ nhật hồng nằm phía sau các marker, trong khi hình tròn xanh phủ lên điểm dữ liệu.
Thành thạo các kỹ thuật layout và xếp lớp là phần quan trọng để tạo trực quan hóa dữ liệu nâng cao và tương tác với Plotly. Với những ai làm việc bằng R, các khái niệm tương tự cũng áp dụng, được trình bày trong khóa Intermediate Interactive Data Visualization with Plotly in R.
Chỉnh sửa shape tương tác
Một trong những tính năng mạnh nhất của Plotly là tính tương tác. Bạn có thể làm cho Plotly shapes có thể chỉnh sửa, cho phép người dùng kéo, thay đổi kích thước và xoay trực tiếp trên biểu đồ. Bật tính năng này bằng cách đặt editable=True trong định nghĩa của shape. Hãy xem ví dụ cách thực hiện:
import plotly.graph_objects as go
import numpy as np
# Create a scatter plot
fig = go.Figure(go.Scatter(
x=np.random.rand(100),
y=np.random.rand(100),
mode='markers'))# Add a draggable rectangle
fig.add_shape(type="rect",
x0=0.2, y0=0.2, x1=0.5, y1=0.5,
line=dict(color="RoyalBlue", width=2),
fillcolor="LightSkyBlue",
opacity=0.5,
editable=True# This makes the shape interactive)
fig.update_layout(
xaxis=dict(range=[0,1]),
yaxis=dict(range=[0,1]),# The 'newshape' button in the modebar allows drawing new shapes# The config dictionary enables editing of existing shapes
dragmode='select')# To enable editing, the modebar must be visible (default) which you will see when hovering over the graph with the mouse.# and the plot's config can ensure editing tools are available.
fig.show(config={'edits':{'shapePosition':True}})Được hỗ trợ bởi
Kết quả:
Các shape tương tác cực kỳ hữu ích cho phân tích dữ liệu khám phá. Ví dụ, người dùng có thể kéo một hình chữ nhật qua các giai đoạn thời gian khác nhau để xem các thống kê tóm tắt tương ứng được cập nhật theo thời gian thực trong ứng dụng Dash. Hoặc họ có thể thay đổi kích thước hình tròn để chọn cụm điểm khác để khảo sát sâu hơn.
Dù mạnh mẽ, hãy lưu ý rằng dùng quá nhiều shape có thể chỉnh sửa có thể ảnh hưởng hiệu năng trong ứng dụng web phức tạp. Tuy nhiên, để tạo công cụ thực sự động và do người dùng dẫn dắt, chúng là không thể thiếu.
Khám phá tính tương tác là phần quan trọng của trực quan hóa dữ liệu hiện đại, chủ đề được đề cập kỹ trong các khóa như Interactive Data Visualization with Plotly in R, chia sẻ nhiều nguyên tắc với bản Python.
Tích hợp Plotly với các thư viện khác
Dù shapes tích hợp sẵn của Plotly rất đa dụng, đôi khi bạn cần tạo hình học cực kỳ phức tạp hoặc tùy chỉnh. Bằng cách tích hợp với các thư viện chuyên biệt như Shapely, bạn có thể định nghĩa các đa giác tinh vi và thêm chúng vào trực quan hóa như các shape tùy chỉnh của Plotly.
Dùng Shapely cho các shape hình học nâng cao
Shapely là thư viện Python mạnh để thao tác và phân tích đối tượng hình học phẳng. Đây là công cụ hàng đầu cho hình học tính toán, thường dùng trong phân tích địa không gian. Bạn có thể dùng nó để tạo các hình phức tạp như hợp của hai hình tròn chồng lấp hoặc đa giác có lỗ, rồi trực quan hóa trong Plotly.
Quy trình gồm hai bước chính:
Định nghĩa hình phức tạp bằng Shapely.
Trích xuất tọa độ từ đối tượng Shapely và định dạng thành chuỗi đường dẫn SVG, để Plotly kết xuất với type: 'path'.
Chuỗi đường dẫn SVG là một ngôn ngữ mini để vẽ. Nó dùng các lệnh như M (moveto) để đặt điểm bắt đầu, L (lineto) để vẽ đường đến điểm mới, và Z để đóng đường bằng cách nối về điểm đầu.
Hãy xem cách tạo một đa giác có phần cắt và thêm vào hình vẽ Plotly.
import plotly.graph_objects as go
from shapely.geometry import Polygon
# 1. Create a complex shape using Shapely# An outer rectangle
outer_coords =[(1,1),(1,5),(8,5),(8,1)]# An inner rectangle (this will be the "hole")
inner_coords =[(3,2),(3,4),(6,4),(6,2)]# Define the polygon with a hole
polygon_with_hole = Polygon(outer_coords,[inner_coords])# 2. Extract coordinates and build the SVG path string# The exterior path
exterior_x, exterior_y = polygon_with_hole.exterior.xy
path =f"M {exterior_x[0]}{exterior_y[0]}"for x, y inzip(exterior_x[1:], exterior_y[1:]):
path +=f" L {x}{y}"
path +=" Z"# Close the exterior path# The interior path (the hole)
interior_x, interior_y = polygon_with_hole.interiors[0].xy
path +=f" M {interior_x[0]}{interior_y[0]}"for x, y inzip(interior_x[1:], interior_y[1:]):
path +=f" L {x}{y}"
path +=" Z"# Close the interior path# Create a figure
fig = go.Figure()# Add the complex shape to the figure
fig.add_shape(type="path",
path=path,
fillcolor="LightSeaGreen",
opacity=0.7,
line_color="DarkSlateGray")# You can add data to see how the shape interacts with it
fig.add_trace(go.Scatter(
x=[2,7,4.5],
y=[3,3,1.5],
mode='markers',
marker=dict(size=15, color='DarkRed')))# Use scaleanchor to ensure the shape's aspect ratio is preserved
fig.update_yaxes(scaleanchor="x", scaleratio=1)
fig.show()Được hỗ trợ bởi
Kết quả:
Kỹ thuật này vô giá cho các ứng dụng địa không gian, như vẽ ranh giới khu vực bán hàng tùy chỉnh, hoặc trong bối cảnh khoa học nơi bạn cần làm nổi bật vùng bất quy tắc quan tâm.
Hiểu cách các loại đồ thị khác nhau được tạo và cách chúng có thể được tăng cường bằng các công cụ như vậy là kỹ năng nền tảng cho bất kỳ người làm dữ liệu nào. Bạn có thể tìm hiểu thêm về các loại đồ thị trong hướng dẫn về cách tạo chúng trong Python.
Tạo kiểu và Tùy biến Thẩm mỹ
Diện mạo mặc định của Plotly shapes là đủ dùng, nhưng tùy biến phong cách là quan trọng để tạo trực quan hóa trau chuốt và chuyên nghiệp. Việc dùng màu sắc, độ mờ và fill hợp lý có thể cải thiện đáng kể khả năng đọc và tính thẩm mỹ.
Màu sắc, độ mờ và pattern fill
Plotly cho bạn khả năng điều khiển chi tiết diện mạo của shapes, cho phép chúng hòa hợp với bảng màu và ngôn ngữ thiết kế của biểu đồ.
Để kiểm soát chính xác màu và độ trong suốt, nên dùng quy cách màu RGBA (Đỏ, Lục, Lam, Alpha). Kênh a (alpha) điều khiển độ mờ, với giá trị 1 là hoàn toàn đục và 0 là trong suốt hoàn toàn.
Điều này rất cần để tạo các vùng làm nổi không gây chướng mắt, nơi shape thêm bối cảnh mà không che khuất dữ liệu bên dưới. Bạn có thể cung cấp giá trị RGBA dưới dạng chuỗi như 'rgba(255, 99, 71, 0.5)'. Hãy xem ví dụ:
import plotly.graph_objects as go
import numpy as np
# Create sample data for styling demonstration
x = np.linspace(0,10,100)
y1 = np.sin(x)+ np.random.normal(0,0.1,100)
y2 = np.cos(x)+ np.random.normal(0,0.1,100)
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y1, name='Signal 1', line=dict(color='blue')))
fig.add_trace(go.Scatter(x=x, y=y2, name='Signal 2', line=dict(color='red')))# RGBA Color Technique 1: Graduated opacity for depth
opacity_levels =[0.15,0.25,0.35]
colors =['255,0,0','0,255,0','0,0,255']for i,(opacity, color)inenumerate(zip(opacity_levels, colors)):
fig.add_shape(type="rect",
x0=i*2.5, x1=(i+1)*2.5,
y0=-3, y1=3,
fillcolor=f"rgba({color},{opacity})",
line=dict(color=f"rgb({color})", width=2),
layer="below")# RGBA Technique 2: Color blending for overlapping regions
fig.add_shape(type="circle",
x0=7.5, x1=9.5,
y0=-1, y1=1,
fillcolor="rgba(255, 255, 0, 0.4)",# Semi-transparent yellow
line=dict(color="orange", width=3),
layer="below")
fig.update_layout(
title="Advanced RGBA Color Techniques in Plotly Shapes",
showlegend=True)
fig.show()Được hỗ trợ bởi
Kết quả:
Chiến lược mô phỏng pattern fill
Mặc dù Plotly không hỗ trợ pattern fill gốc, người làm dữ liệu có thể mô phỏng các pattern fill phức tạp bằng kỹ thuật sáng tạo. Ví dụ, chúng ta có thể tạo pattern trên biểu đồ bằng một hàm như dưới đây:
Tạo hiệu ứng giống chuyển sắc cần xếp lớp chiến lược nhiều shape với độ mờ khác nhau. Chúng ta có thể tạo theo cùng chiến lược dùng cho pattern, như dưới đây:
defcreate_gradient_effect(fig, x0, x1, y0, y1, start_color, end_color, num_bands=20):"""Simulate gradient fill using multiple rectangular bands"""# Parse RGB values from color stringsdefparse_rgb(color_str):if color_str.startswith('rgb('):
values = color_str[4:-1].split(',')return[int(v.strip())for v in values]return[0,0,0]# Default to black if parsing fails
start_rgb = parse_rgb(start_color)ifisinstance(start_color,str)else start_color
end_rgb = parse_rgb(end_color)ifisinstance(end_color,str)else end_color
band_height =(y1 - y0)/ num_bands
for i inrange(num_bands):# Calculate interpolated color
ratio = i /(num_bands -1)
r =int(start_rgb[0]+(end_rgb[0]- start_rgb[0])* ratio)
g =int(start_rgb[1]+(end_rgb[1]- start_rgb[1])* ratio)
b =int(start_rgb[2]+(end_rgb[2]- start_rgb[2])* ratio)# Calculate band position
band_y0 = y0 + i * band_height
band_y1 = band_y0 + band_height
fig.add_shape(type="rect",
x0=x0, x1=x1,
y0=band_y0, y1=band_y1,
fillcolor=f"rgba({r}, {g}, {b}, 0.7)",
line=dict(width=0),# No border for seamless gradient
layer="below")return fig
# Apply gradient effect
gradient_fig = go.Figure()
gradient_fig.add_trace(go.Scatter(x=list(range(10)),
y=np.random.randn(10).cumsum(),
mode='lines', name='Time Series'))
gradient_fig = create_gradient_effect(
gradient_fig,2,8,-3,3,[255,0,0],[0,0,255],# Red to blue gradient
num_bands=30)
gradient_fig.show()Được hỗ trợ bởi
Kết quả:
Khắc phục sự cố và Tối ưu hóa
Mặc dù thêm Plotly shapes nhìn chung khá đơn giản, bạn có thể gặp vấn đề về căn chỉnh hoặc hiệu năng, đặc biệt trong các đồ thị phức tạp. Hiểu các lỗi thường gặp và cách xử lý sẽ tiết kiệm đáng kể thời gian và công sức.
Các vấn đề căn chỉnh shape thường gặp
Một thách thức hay gặp là khiến shapes xuất hiện đúng vị trí mong muốn. Một đường dự định chạy hết chiều rộng biểu đồ có thể bị ngắn, hoặc một hình chữ nhật có thể không thẳng hàng với lưới như kỳ vọng. Những vấn đề này hầu như luôn bắt nguồn từ tham chiếu tọa độ không chính xác.
Plotly dùng hai hệ tọa độ để đặt shapes:
Tọa độ dữ liệu (xref: 'x', yref: 'y'): Đây là mặc định. Vị trí shape gắn với giá trị trên trục x và y. Ví dụ, x0=50 đặt điểm tại mốc 50 trên trục x.
Tọa độ paper (xref: 'paper', yref: 'paper'): Vị trí tương đối với vùng vẽ, nơi 0 là bắt đầu (cạnh trái/dưới) và 1 là kết thúc (cạnh phải/trên). Hệ này độc lập với giá trị dữ liệu hoặc phạm vi trục.
Vấn đề: Bạn muốn vẽ một đường ngang chạy hết biểu đồ tại y=10. Nếu bạn xác định x0 và x1 theo tọa độ dữ liệu, nó chỉ chạy trong phạm vi trục hiện tại. Nếu người dùng zoom hoặc pan, đường sẽ không kéo dài tới mép mới.
Giải pháp: Để shape chạy hết chiều rộng hoặc chiều cao vùng vẽ, hãy dùng tọa độ 'paper' cho trục bạn muốn phủ kín.
import plotly.graph_objects as go
fig = go.Figure(data=go.Scatter(x=[0,10], y=[0,20]))# Incorrect: Vertical line that might not span the full height if the user zooms out.# This line is drawn from y=0 to y=20 in DATA units.
fig.add_shape(type="line",
x0=5, y0=0, x1=5, y1=20,
line=dict(color="Red", width=2, dash="dash"),
name="Incorrect Line"# Name for legend)# Correct: Vertical line that ALWAYS spans the full plot height.# It is drawn from the bottom edge (0) to the top edge (1) of the PLOT area.
fig.add_shape(type="line",
x0=8, y0=0, x1=8, y1=1,
yref="paper",# The key to spanning the full height
line=dict(color="Green", width=3),
name="Correct Line")
fig.update_layout(title_text="Correctly Spanning the Plot Area")
fig.show()Được hỗ trợ bởi
Kết quả:
Luôn kiểm tra kỹ thuộc tính xref và yref để đảm bảo shapes hoạt động như mong đợi trong tương tác của người dùng, như phóng to và kéo.
Lưu ý về hiệu năng
Nếu bạn chỉ thêm vài shape, tác động hiệu năng là không đáng kể. Tuy nhiên, thêm hàng trăm hoặc hàng nghìn shape có thể làm chậm đáng kể việc kết xuất và tương tác. Mỗi shape là một phần tử SVG (Scalable Vector Graphics) riêng trong DOM của trang web. Số lượng lớn phần tử DOM sẽ gây áp lực cho trình duyệt.
Dưới đây là các chiến lược tối ưu hóa hiệu năng:
Giới hạn số lượng shape: Giải pháp đơn giản nhất. Nếu bạn cần trực quan hóa hàng nghìn vùng hình chữ nhật, hãy cân nhắc liệu một kiểu biểu đồ khác, như go.Heatmap, có thể biểu đạt thông tin tương tự hiệu quả hơn dưới dạng một trace thay vì hàng nghìn shape riêng lẻ.
Dùng update_layout cho bổ sung hàng loạt: Như đã đề cập, thêm danh sách shapes qua fig.update_layout(shapes=...) hiệu quả hơn so với gọi fig.add_shape() trong vòng lặp.
Cân nhắc dùng trace WebGL cho dữ liệu: Dù bản thân shapes được kết xuất dưới dạng SVG, bạn có thể cải thiện tổng thể hiệu năng đồ thị phức tạp bằng cách dùng các trace dựa trên WebGL (ví dụ, go.Scattergl) cho dữ liệu chính. Điều này chuyển việc kết xuất dữ liệu sang GPU, giải phóng CPU để xử lý shapes SVG mượt hơn. Hữu ích khi bạn có lượng điểm dữ liệu rất lớn và số lượng shape ở mức vừa phải.
Với hầu hết trường hợp, điều cốt yếu là lưu tâm đến số lượng phần tử shape bạn tạo. Trước khi thêm hàng nghìn Plotly shapes trong vòng lặp, hãy tạm dừng và cân nhắc liệu có chiến lược trực quan hóa hiệu năng hơn không.
Kết luận
Thành thạo Plotly shapes biến đồ thị của bạn từ các biểu diễn dữ liệu đơn thuần thành những câu chuyện giàu bối cảnh và cuốn hút. Chúng ta đã xem từ việc vẽ đường và hình chữ nhật cơ bản đến xếp lớp hình học phức tạp và tương tác.
Bằng cách áp dụng các kỹ thuật này, bạn có thể dẫn dắt sự tập trung của khán giả, làm nổi bật các insight quan trọng và xây dựng trực quan hóa trực quan hơn, truyền đạt rõ ràng và hiệu quả.
Tôi là một biên tập viên nội dung về khoa học dữ liệu. Tôi yêu thích sáng tạo nội dung xoay quanh các chủ đề AI/ML/DS. Tôi cũng khám phá các công cụ AI mới và viết về chúng.
Plotly là một trong những thư viện Python mạnh mẽ và phổ biến nhất để tạo đồ thị tương tác. Việc tạo biểu đồ cơ bản với Plotly khá đơn giản, nhưng để trực quan hóa dữ liệu thực sự hiệu quả, bạn thường cần thêm các yếu tố bổ sung để dẫn dắt sự chú ý của người xem và cung cấp bối cảnh quan trọng. Đây chính là lúc các shape trong Plotly phát huy giá trị.
Shape là các phần tử đồ họa như hình chữ nhật, đường thẳng và hình tròn mà bạn có thể vẽ chồng lên hình vẽ Plotly để chú thích, làm nổi bật và thêm bối cảnh quan trọng. Chúng là chìa khóa để nâng một biểu đồ tiêu chuẩn thành trực quan hóa giàu thông tin, sâu sắc và đạt chất lượng xuất bản.
Hình ảnh: Tác giả
Trong bài viết này, chúng ta sẽ tìm hiểu mọi thứ bạn cần biết về plotly shapes, từ triển khai cơ bản đến các kỹ thuật tùy biến nâng cao. Với người mới bắt đầu dùng Plotly, việc làm quen với giao diện cấp cao của nó là bước khởi đầu tuyệt vời, và khóa học Introduction to Data Visualization with Plotly in Python có thể giúp bạn nắm vững các nền tảng trước khi đi sâu vào các tùy chỉnh như shapes.
Plotly Shapes là gì?
Plotly shapes là đồ họa vector được vẽ trên hình vẽ để cung cấp bối cảnh bổ sung hoặc làm nổi bật các khu vực cụ thể. Những chú thích này rất quan trọng để xây dựng đồ thị không chỉ đẹp mắt mà còn giàu thông tin.
Tầm quan trọng của shapes trong trực quan hóa dữ liệu
Trong trực quan hóa dữ liệu, tính rõ ràng là điều quan trọng. Trong khi các biểu đồ như scatter hay bar thể hiện dữ liệu, shapes giúp kể câu chuyện đằng sau dữ liệu. Chúng phục vụ hai chức năng chính:
Nếu bạn muốn xem nhanh các kiểu trực quan hóa dữ liệu phổ biến, khi nào nên dùng và các trường hợp sử dụng thường gặp, hãy xem cheat sheet Trực quan hóa dữ liệu của chúng tôi.
Tổng quan về các loại shape trong Plotly
Plotly hỗ trợ nhiều loại hình học có thể thêm vào layout của hình vẽ. Mỗi loại được xác định bởi
typevà các tham số tọa độ cụ thể.Dưới đây là cái nhìn ngắn gọn về các shape chính:
type: 'rect'): Xác định bởi hai góc đối diện (x0,y0) và (x1,y1). Hình chữ nhật rất phù hợp để tô bóng vùng quan tâm hoặc làm nổi bật một khoảng dữ liệu cụ thể.type: 'line'): Xác định bởi điểm bắt đầu (x0,y0) và điểm kết thúc (x1,y1). Đường thẳng rất hữu ích để tạo đường xu hướng, ngưỡng hoặc mốc tham chiếu.type: 'circle'): Xác định bởi tâm (x0,y0) và bán kính. Hình tròn lý tưởng để nhấn mạnh các điểm dữ liệu riêng lẻ hoặc cụm nhỏ. Lưu ý rằng để tạo hình tròn, bạn thường cần đặt tỷ lệ khung hình bằng nhau cho trục (fig.update_layout(yaxis_scaleanchor="x")) để tránh trông như hình bầu dục.type: 'path'): Xác định bởi một chuỗi đường dẫn SVG. Path cho phép bạn vẽ các hình tự do phức tạp, bao gồm đa giác. Bạn cung cấp một chuỗi lệnh (nhưMđể di chuyển,Lđể vẽ đường,Zđể đóng đường) để vẽ bất kỳ hình nào bạn cần. Đây là tùy chọn linh hoạt nhất để tạo chú thích hình học tùy chỉnh.Hiểu các loại cơ bản này là bước đầu để thành thạo chú thích trực quan trong các hình vẽ Plotly của bạn.
Thêm Shapes vào đồ thị Plotly
Triển khai plotly shapes trong trực quan hóa của bạn liên quan đến hai cách tiếp cận chính: phương thức
add_shape()cho từng shape riêng lẻ và cáchupdate_layout()cho nhiều shape. Cả hai đều có ưu điểm riêng tùy trường hợp sử dụng và quy trình làm việc của bạn. Hãy xem cách thêm shapes bằng phương thứcadd_shape().Cách thêm shapes bằng add_shape()?
Phương thức chính để thêm một shape vào hình vẽ Plotly là
fig.add_shape(). Phương thứcadd_shape()là một hàm của đối tượngplotly.graph_objects.Figure. Bạn tạo hình vẽ cơ sở (ví dụ, scatter hoặc line) rồi gọi phương thức này để xếp lớp shape lên trên.Điều này đặc biệt hữu ích khi bạn cần thêm shapes theo lập trình dựa trên các điều kiện hoặc giá trị dữ liệu. Hãy xem ví dụ cơ bản nơi chúng ta làm nổi bật một vùng cụ thể trên đồ thị chuỗi thời gian.
Kết quả:
Tính năng
add_shape()của plotly trở nên đặc biệt mạnh khi bạn hiểu hệ tọa độ. Shapes có thể tham chiếu các hệ tọa độ khác nhau bằng tham sốxrefvàyref. Chúng chỉ định khung tọa độ: dùng'x'/'y'để diễn giải theo đơn vị dữ liệu, hoặc'paper'để dùng vùng vẽ đã chuẩn hóa, nơi 0 – 1 phủ toàn bộ chiều rộng/chiều cao từ góc trái-dưới đến phải-trên. Hãy sửa đồ thị trên bằng các tham số này như dưới đây:Kết quả:
Tùy biến hình chữ nhật và đường thẳng
Hình chữ nhật và đường thẳng là những shape được dùng nhiều nhất cho chú thích. Plotly cung cấp nhiều tùy chọn để điều khiển diện mạo của chúng. Để tra cứu nhanh cú pháp Plotly phổ biến, Plotly Express Cheat Sheet là tài nguyên vô giá. Hãy xem cách tùy biến các shape này.
Tùy biến hình chữ nhật
Hình chữ nhật được xác định bởi hai điểm (
x0,y0vàx1,y1). Bạn có thể tạo kiểu phần nền và viền riêng biệt.Kết quả:
Tùy biến đường thẳng
Bạn có thể thêm đường bằng các phương thức chuyên dụng ‘add_hline’ và ‘add_vline’ cho đường ngang và dọc chạy hết biểu đồ, hoặc qua phương thức ‘add_shape’ tổng quát để kiểm soát chính xác hơn. Hãy xem ví dụ:
Kết quả:
Những ví dụ này cho thấy bạn có thể dễ dàng thêm và tạo kiểu các shape cơ bản của Plotly để làm cho đồ thị giàu thông tin hơn.
Cấu hình và Tùy biến Nâng cao
Vượt ra ngoài việc thêm shape cơ bản, Plotly cung cấp công cụ mạnh để quản lý nhiều shape và kiểm soát thứ tự hiển thị của chúng. Các tính năng nâng cao này rất cần thiết để tạo trực quan hóa phức tạp, nhiều lớp và động. Hãy xem một số cách cấu hình và tùy biến đồ thị.
Sử dụng plotly.update_layout cho cập nhật động
Trong khi phương thức
add_shape()phù hợp để thêm từng shape, phương thứcupdate_layout()hiệu quả hơn để quản lý một tập hợp shape, đặc biệt khi chúng được tạo theo lập trình. Thay vì gọi hàm cho từng shape, bạn có thể truyền trực tiếp danh sách các dictionary shape vào tham sốshapestrongupdate_layout().Hãy xem ví dụ sử dụng phương thức này:
Kết quả:
Cách này đặc biệt mạnh cho ứng dụng động, như dashboard Dash nơi shapes cần thay đổi theo đầu vào người dùng. Bạn có thể tạo danh sách shape mới và cập nhật layout trong một thao tác gọn gàng. Cách tiếp cận này giúp mã của bạn ngăn nắp và thường hiệu quả hơn khi thêm số lượng lớn shape cùng lúc.
Xếp lớp và kiểm soát z-index
Khi thêm shapes vào biểu đồ, việc kiểm soát chúng hiển thị trên hay dưới dữ liệu là quan trọng. Plotly quản lý điều này qua thuộc tính layer. Dù không chi tiết như
z-indextrong CSS, nó vẫn cung cấp kiểm soát cần thiết về thứ tự chồng lớp.Thuộc tính
layercó thể đặt một trong hai giá trị:'above': Shape được vẽ đè lên các trace dữ liệu. Đây là mặc định và hữu ích cho chú thích cần hiển thị rõ.'below': Shape được vẽ bên dưới các trace dữ liệu và lưới. Lý tưởng cho tô nền hoặc làm nổi vùng mà không che khuất điểm dữ liệu.Thứ tự của shapes trong danh sách shapes cũng quan trọng: shape định nghĩa sau sẽ vẽ đè lên shape định nghĩa trước (trong cùng layer).
Đây là ví dụ minh họa hiệu ứng xếp lớp:
Kết quả:
Như bạn thấy, hình chữ nhật hồng nằm phía sau các marker, trong khi hình tròn xanh phủ lên điểm dữ liệu.
Thành thạo các kỹ thuật layout và xếp lớp là phần quan trọng để tạo trực quan hóa dữ liệu nâng cao và tương tác với Plotly. Với những ai làm việc bằng R, các khái niệm tương tự cũng áp dụng, được trình bày trong khóa Intermediate Interactive Data Visualization with Plotly in R.
Chỉnh sửa shape tương tác
Một trong những tính năng mạnh nhất của Plotly là tính tương tác. Bạn có thể làm cho Plotly shapes có thể chỉnh sửa, cho phép người dùng kéo, thay đổi kích thước và xoay trực tiếp trên biểu đồ. Bật tính năng này bằng cách đặt
editable=Truetrong định nghĩa của shape. Hãy xem ví dụ cách thực hiện:Kết quả:
Các shape tương tác cực kỳ hữu ích cho phân tích dữ liệu khám phá. Ví dụ, người dùng có thể kéo một hình chữ nhật qua các giai đoạn thời gian khác nhau để xem các thống kê tóm tắt tương ứng được cập nhật theo thời gian thực trong ứng dụng Dash. Hoặc họ có thể thay đổi kích thước hình tròn để chọn cụm điểm khác để khảo sát sâu hơn.
Dù mạnh mẽ, hãy lưu ý rằng dùng quá nhiều shape có thể chỉnh sửa có thể ảnh hưởng hiệu năng trong ứng dụng web phức tạp. Tuy nhiên, để tạo công cụ thực sự động và do người dùng dẫn dắt, chúng là không thể thiếu.
Khám phá tính tương tác là phần quan trọng của trực quan hóa dữ liệu hiện đại, chủ đề được đề cập kỹ trong các khóa như Interactive Data Visualization with Plotly in R, chia sẻ nhiều nguyên tắc với bản Python.
Tích hợp Plotly với các thư viện khác
Dù shapes tích hợp sẵn của Plotly rất đa dụng, đôi khi bạn cần tạo hình học cực kỳ phức tạp hoặc tùy chỉnh. Bằng cách tích hợp với các thư viện chuyên biệt như Shapely, bạn có thể định nghĩa các đa giác tinh vi và thêm chúng vào trực quan hóa như các shape tùy chỉnh của Plotly.
Dùng Shapely cho các shape hình học nâng cao
Shapely là thư viện Python mạnh để thao tác và phân tích đối tượng hình học phẳng. Đây là công cụ hàng đầu cho hình học tính toán, thường dùng trong phân tích địa không gian. Bạn có thể dùng nó để tạo các hình phức tạp như hợp của hai hình tròn chồng lấp hoặc đa giác có lỗ, rồi trực quan hóa trong Plotly.
Quy trình gồm hai bước chính:
type: 'path'.Chuỗi đường dẫn SVG là một ngôn ngữ mini để vẽ. Nó dùng các lệnh như
M(moveto) để đặt điểm bắt đầu,L(lineto) để vẽ đường đến điểm mới, vàZđể đóng đường bằng cách nối về điểm đầu.Hãy xem cách tạo một đa giác có phần cắt và thêm vào hình vẽ Plotly.
Kết quả:
Kỹ thuật này vô giá cho các ứng dụng địa không gian, như vẽ ranh giới khu vực bán hàng tùy chỉnh, hoặc trong bối cảnh khoa học nơi bạn cần làm nổi bật vùng bất quy tắc quan tâm.
Hiểu cách các loại đồ thị khác nhau được tạo và cách chúng có thể được tăng cường bằng các công cụ như vậy là kỹ năng nền tảng cho bất kỳ người làm dữ liệu nào. Bạn có thể tìm hiểu thêm về các loại đồ thị trong hướng dẫn về cách tạo chúng trong Python.
Tạo kiểu và Tùy biến Thẩm mỹ
Diện mạo mặc định của Plotly shapes là đủ dùng, nhưng tùy biến phong cách là quan trọng để tạo trực quan hóa trau chuốt và chuyên nghiệp. Việc dùng màu sắc, độ mờ và fill hợp lý có thể cải thiện đáng kể khả năng đọc và tính thẩm mỹ.
Màu sắc, độ mờ và pattern fill
Plotly cho bạn khả năng điều khiển chi tiết diện mạo của shapes, cho phép chúng hòa hợp với bảng màu và ngôn ngữ thiết kế của biểu đồ.
Để kiểm soát chính xác màu và độ trong suốt, nên dùng quy cách màu RGBA (Đỏ, Lục, Lam, Alpha). Kênh
a(alpha) điều khiển độ mờ, với giá trị1là hoàn toàn đục và0là trong suốt hoàn toàn.Điều này rất cần để tạo các vùng làm nổi không gây chướng mắt, nơi shape thêm bối cảnh mà không che khuất dữ liệu bên dưới. Bạn có thể cung cấp giá trị RGBA dưới dạng chuỗi như
'rgba(255, 99, 71, 0.5)'. Hãy xem ví dụ:Kết quả:
Chiến lược mô phỏng pattern fill
Mặc dù Plotly không hỗ trợ pattern fill gốc, người làm dữ liệu có thể mô phỏng các pattern fill phức tạp bằng kỹ thuật sáng tạo. Ví dụ, chúng ta có thể tạo pattern trên biểu đồ bằng một hàm như dưới đây:
Mô phỏng hiệu ứng chuyển sắc
Tạo hiệu ứng giống chuyển sắc cần xếp lớp chiến lược nhiều shape với độ mờ khác nhau. Chúng ta có thể tạo theo cùng chiến lược dùng cho pattern, như dưới đây:
Kết quả:
Khắc phục sự cố và Tối ưu hóa
Mặc dù thêm Plotly shapes nhìn chung khá đơn giản, bạn có thể gặp vấn đề về căn chỉnh hoặc hiệu năng, đặc biệt trong các đồ thị phức tạp. Hiểu các lỗi thường gặp và cách xử lý sẽ tiết kiệm đáng kể thời gian và công sức.
Các vấn đề căn chỉnh shape thường gặp
Một thách thức hay gặp là khiến shapes xuất hiện đúng vị trí mong muốn. Một đường dự định chạy hết chiều rộng biểu đồ có thể bị ngắn, hoặc một hình chữ nhật có thể không thẳng hàng với lưới như kỳ vọng. Những vấn đề này hầu như luôn bắt nguồn từ tham chiếu tọa độ không chính xác.
Plotly dùng hai hệ tọa độ để đặt shapes:
xref: 'x', yref: 'y'): Đây là mặc định. Vị trí shape gắn với giá trị trên trục x và y. Ví dụ,x0=50đặt điểm tại mốc 50 trên trục x.xref: 'paper', yref: 'paper'): Vị trí tương đối với vùng vẽ, nơi 0 là bắt đầu (cạnh trái/dưới) và 1 là kết thúc (cạnh phải/trên). Hệ này độc lập với giá trị dữ liệu hoặc phạm vi trục.Vấn đề: Bạn muốn vẽ một đường ngang chạy hết biểu đồ tại
y=10. Nếu bạn xác địnhx0vàx1theo tọa độ dữ liệu, nó chỉ chạy trong phạm vi trục hiện tại. Nếu người dùng zoom hoặc pan, đường sẽ không kéo dài tới mép mới.Giải pháp: Để shape chạy hết chiều rộng hoặc chiều cao vùng vẽ, hãy dùng tọa độ
'paper'cho trục bạn muốn phủ kín.Kết quả:
Luôn kiểm tra kỹ thuộc tính
xrefvàyrefđể đảm bảo shapes hoạt động như mong đợi trong tương tác của người dùng, như phóng to và kéo.Lưu ý về hiệu năng
Nếu bạn chỉ thêm vài shape, tác động hiệu năng là không đáng kể. Tuy nhiên, thêm hàng trăm hoặc hàng nghìn shape có thể làm chậm đáng kể việc kết xuất và tương tác. Mỗi shape là một phần tử SVG (Scalable Vector Graphics) riêng trong DOM của trang web. Số lượng lớn phần tử DOM sẽ gây áp lực cho trình duyệt.
Dưới đây là các chiến lược tối ưu hóa hiệu năng:
go.Heatmap, có thể biểu đạt thông tin tương tự hiệu quả hơn dưới dạng một trace thay vì hàng nghìn shape riêng lẻ.update_layoutcho bổ sung hàng loạt: Như đã đề cập, thêm danh sách shapes quafig.update_layout(shapes=...)hiệu quả hơn so với gọifig.add_shape()trong vòng lặp.go.Scattergl) cho dữ liệu chính. Điều này chuyển việc kết xuất dữ liệu sang GPU, giải phóng CPU để xử lý shapes SVG mượt hơn. Hữu ích khi bạn có lượng điểm dữ liệu rất lớn và số lượng shape ở mức vừa phải.Với hầu hết trường hợp, điều cốt yếu là lưu tâm đến số lượng phần tử shape bạn tạo. Trước khi thêm hàng nghìn Plotly shapes trong vòng lặp, hãy tạm dừng và cân nhắc liệu có chiến lược trực quan hóa hiệu năng hơn không.
Kết luận
Thành thạo Plotly shapes biến đồ thị của bạn từ các biểu diễn dữ liệu đơn thuần thành những câu chuyện giàu bối cảnh và cuốn hút. Chúng ta đã xem từ việc vẽ đường và hình chữ nhật cơ bản đến xếp lớp hình học phức tạp và tương tác.
Bằng cách áp dụng các kỹ thuật này, bạn có thể dẫn dắt sự tập trung của khán giả, làm nổi bật các insight quan trọng và xây dựng trực quan hóa trực quan hơn, truyền đạt rõ ràng và hiệu quả.
Sẵn sàng áp dụng những kỹ năng này? Bắt đầu xây dựng các trực quan hóa mạnh mẽ hơn ngay hôm nay bằng cách khám phá khóa học Introduction to Data Visualization with Plotly in Python và đưa kiến thức của bạn vào thực tiễn.