Tự Học Data Science · 25/02/2024 0

04.09 Text and Annotation

Vi dụ: Tác động của Ngày Lễ đối với Sinh con ở Hoa Kỳ

Hãy quay lại một số dữ liệu mà chúng ta đã làm việc trước đây, trong “Ví dụ: Dữ liệu Tỷ lệ Sinh”, nơi chúng ta đã tạo ra một biểu đồ về số sinh trung bình trong suốt năm dương lịch; như đã đề cập trước đó, dữ liệu này có thể được tải về tại https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv.

Chúng ta sẽ bắt đầu với quy trình làm sạch tương tự chúng ta đã sử dụng ở đó, và vẽ biểu đồ kết quả:

births = pd.read_csv('data/births.csv')quartiles = np.percentile(births['births'], [25, 50, 75])mu, sig = quartiles[1], 0.74 * (quartiles[2] - quartiles[0])births = births.query('(births > @mu - 5 * @sig) & (births < @mu + 5 * @sig)')births['day'] = births['day'].astype(int)births.index = pd.to_datetime(10000 * births.year +                              100 * births.month +                              births.day, format='%Y%m%d')births_by_date = births.pivot_table('births',                                    [births.index.month, births.index.day])births_by_date.index = [pd.datetime(2012, month, day)                        for (month, day) in births_by_date.index]
fig, ax = plt.subplots(figsize=(12, 4))births_by_date.plot(ax=ax);
ảnh ví dụ - data science lại blog của lưu

Khi chúng ta truyền tải dữ liệu như thế này, thì thường có ích khi chú thích một số đặc điểm của biểu đồ để thu hút sự chú ý của đọc giả.Điều này có thể được thực hiện bằng cách thủ công với lệnh plt.text/ax.text, nó sẽ đặt văn bản tại một giá trị x/y cụ thể:

fig, ax = plt.subplots(figsize=(12, 4))births_by_date.plot(ax=ax)# Add labels to the plotstyle = dict(size=10, color='gray')ax.text('2012-1-1', 3950, "New Year's Day", **style)ax.text('2012-7-4', 4250, "Independence Day", ha='center', **style)ax.text('2012-9-4', 4850, "Labor Day", ha='center', **style)ax.text('2012-10-31', 4600, "Halloween", ha='right', **style)ax.text('2012-11-25', 4450, "Thanksgiving", ha='center', **style)ax.text('2012-12-25', 3850, "Christmas ", ha='right', **style)# Label the axesax.set(title='USA births by day of year (1969-1988)',       ylabel='average daily births')# Format the x axis with centered month labelsax.xaxis.set_major_locator(mpl.dates.MonthLocator())ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))ax.xaxis.set_major_formatter(plt.NullFormatter())ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));
ảnh ví dụ - data science lại blog của lưu

Phương thức ax.text nhận vào một vị trí x, một vị trí y, một chuỗi và sau đó là các thuộc tính tùy chọn chỉ định màu sắc, kích thước, kiểu dáng, căn chỉnh và các thuộc tính khác của văn bản.Ở đây chúng ta đã sử dụng ha='right'ha='center', trong đó ha viết tắt cho căn chỉnh ngang (horizonal alignment).Xem docstring của plt.text() và của mpl.text.Text() để biết thêm thông tin về các tùy chọn có sẵn.

Chuyển đổi và Vị trí văn bản

Trong ví dụ trước đó, chúng ta đã gắn kết các chú thích văn bản của chúng ta vào các vị trí dữ liệu. Đôi khi thì tốt hơn khi gắn kết văn bản vào một vị trí trên trục hoặc hình vẽ, độc lập với dữ liệu. Trong Matplotlib, điều này được thực hiện bằng cách sửa đổi transform.

Một framework hiển thị đồ họa nào đó cần một hệ thống để dịch đổi giữa các hệ tọa độ.Ví dụ, một điểm dữ liệu ở $(x, y) = (1, 1)$ cần được biểu diễn ở một vị trí cố định trên hình, mà sau đó cần được biểu diễn dưới dạng pixel trên màn hình.Một cách toán học, việc chuyển đổi tọa độ như vậy tương đối đơn giản, và Matplotlib có một bộ công cụ phát triển tốt được sử dụng trong nội bộ để thực hiện chúng (các công cụ này có thể được khám phá trong phụ mô-đun matplotlib.transforms).

Người dùng thông thường hiếm khi cần quan tâm đến chi tiết của các phép biến đổi này, nhưng đó là kiến thức hữu ích khi xem xét vị trí của văn bản trên một hình ảnh. Có ba phép biến đổi đã được định nghĩa trước có thể hữu ích trong tình huống này:

  • ax.transData: Chuyển đổi liên quan đến tọa độ dữ liệu
  • ax.transAxes: Chuyển đổi liên quan đến trục (theo đơn vị của kích thước trục)
  • fig.transFigure: Chuyển đổi liên quan đến hình ảnh (theo đơn vị của kích thước hình ảnh)

Ở đây chúng ta hãy xem một ví dụ vẽ chữ ở các vị trí khác nhau bằng cách sử dụng các biến đổi này:

fig, ax = plt.subplots(facecolor='lightgray')ax.axis([0, 10, 0, 10])# transform=ax.transData is the default, but we'll specify it anywayax.text(1, 5, ". Data: (1, 5)", transform=ax.transData)ax.text(0.5, 0.1, ". Axes: (0.5, 0.1)", transform=ax.transAxes)ax.text(0.2, 0.2, ". Figure: (0.2, 0.2)", transform=fig.transFigure);
ảnh ví dụ - data science lại blog của lưu

Lưu ý rằng mặc định, văn bản được căn chỉnh phía trên và bên trái của tọa độ đã chỉ định: ở đây, dấu “.” ở đầu mỗi chuỗi sẽ đánh dấu một cách xấp xỉ vị trí tọa độ đã cho.

Các tọa độ transData cung cấp các tọa độ thông thường được liên kết với nhãn trục x và y.Các tọa độ transAxes cung cấp vị trí từ góc dưới bên trái của trục (ở đây là hình chữ nhật màu trắng), dưới dạng một phần của kích thước trục.Các tọa độ transFigure tương tự, nhưng xác định vị trí từ góc dưới bên trái của hình (ở đây là hình chữ nhật màu xám), dưới dạng một phần của kích thước hình.

Lưu ý rằng nếu ta thay đổi giới hạn các trục, chỉ có tọa độ transData sẽ bị ảnh hưởng, trong khi các tọa độ khác vẫn giữ nguyên:

ax.set_xlim(0, 2)ax.set_ylim(-6, 6)fig
ảnh ví dụ - data science lại blog của lưu

Chuyển đổi HTML sang tiếng Việt

Mũi tên và Chú thích

Bên cạnh các đánh dấu thẳng đứng và văn bản, một đánh dấu chú thích hữu ích khác là mũi tên đơn giản.

Vẽ các mũi tên trong Matplotlib thường khó hơn bạn tưởng. Mặc dù có sẵn hàm plt.arrow(), tôi không đề xuất sử dụng nó: các mũi tên mà nó tạo ra là các đối tượng SVG sẽ phụ thuộc vào tỷ lệ khung hình biểu đồ thay đổi, và kết quả hiếm khi là những gì người dùng dự định. Thay vào đó, tôi đề xuất sử dụng hàm plt.annotate(). Hàm này tạo ra một số văn bản và một mũi tên, và các mũi tên có thể được chỉ định một cách rất linh hoạt.

Ở đây chúng ta sẽ sử dụng annotate với một số tùy chọn của nó:

%matplotlib inlinefig, ax = plt.subplots()x = np.linspace(0, 20, 1000)ax.plot(x, np.cos(x))ax.axis('equal')ax.annotate('local maximum', xy=(6.28, 1), xytext=(10, 4),            arrowprops=dict(facecolor='black', shrink=0.05))ax.annotate('local minimum', xy=(5 * np.pi, -1), xytext=(2, -6),            arrowprops=dict(arrowstyle="->",                            connectionstyle="angle3,angleA=0,angleB=-90"));
ảnh ví dụ - data science lại blog của lưu

Cách hiển thị mũi tên được điều khiển thông qua từ điển arrowprops, với nhiều tùy chọn khả dụng.Các tùy chọn này được mô tả khá đầy đủ trong tài liệu trực tuyến của Matplotlib, vì vậy thay vì lặp lại ở đây, có lẽ có ích hơn để nhanh chóng hiển thị một số khả năng có thể.Hãy cho thấy một số tùy chọn có thể sử dụng bằng cách sử dụng biểu đồ tỉ lệ sinh từ trước đó:

fig, ax = plt.subplots(figsize=(12, 4))births_by_date.plot(ax=ax)# Add labels to the plotax.annotate("New Year's Day", xy=('2012-1-1', 4100),  xycoords='data',            xytext=(50, -30), textcoords='offset points',            arrowprops=dict(arrowstyle="->",                            connectionstyle="arc3,rad=-0.2"))ax.annotate("Independence Day", xy=('2012-7-4', 4250),  xycoords='data',            bbox=dict(boxstyle="round", fc="none", ec="gray"),            xytext=(10, -40), textcoords='offset points', ha='center',            arrowprops=dict(arrowstyle="->"))ax.annotate('Labor Day', xy=('2012-9-4', 4850), xycoords='data', ha='center',            xytext=(0, -20), textcoords='offset points')ax.annotate('', xy=('2012-9-1', 4850), xytext=('2012-9-7', 4850),            xycoords='data', textcoords='data',            arrowprops={'arrowstyle': '|-|,widthA=0.2,widthB=0.2', })ax.annotate('Halloween', xy=('2012-10-31', 4600),  xycoords='data',            xytext=(-80, -40), textcoords='offset points',            arrowprops=dict(arrowstyle="fancy",                            fc="0.6", ec="none",                            connectionstyle="angle3,angleA=0,angleB=-90"))ax.annotate('Thanksgiving', xy=('2012-11-25', 4500),  xycoords='data',            xytext=(-120, -60), textcoords='offset points',            bbox=dict(boxstyle="round4,pad=.5", fc="0.9"),            arrowprops=dict(arrowstyle="->",                            connectionstyle="angle,angleA=0,angleB=80,rad=20"))ax.annotate('Christmas', xy=('2012-12-25', 3850),  xycoords='data',             xytext=(-30, 0), textcoords='offset points',             size=13, ha='right', va="center",             bbox=dict(boxstyle="round", alpha=0.1),             arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1));# Label the axesax.set(title='USA births by day of year (1969-1988)',       ylabel='average daily births')# Format the x axis with centered month labelsax.xaxis.set_major_locator(mpl.dates.MonthLocator())ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))ax.xaxis.set_major_formatter(plt.NullFormatter())ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));ax.set_ylim(3600, 5400);
ảnh ví dụ - data science lại blog của lưu

Bạn sẽ nhận thấy rằng các thông số kỹ thuật của các mũi tên và hộp văn bản rất chi tiết: điều này cho phép bạn tạo ra gần như bất kỳ kiểu mũi tên nào bạn muốn.Tuy nhiên, điều đó cũng có nghĩa là những tính năng như vậy thường phải được điều chỉnh thủ công, một quá trình có thể tốn rất nhiều thời gian khi tạo đồ họa chất lượng xuất bản!Cuối cùng, tôi muốn nhấn mạnh rằng sự kết hợp các kiểu như trên không phải lúc nào cũng là phương pháp tốt nhất để trình bày dữ liệu, mà chỉ được bao gồm như là một minh họa cho một số tùy chọn có sẵn.

Có thể tìm thấy thảo luận và ví dụ về các kiểu mũi tên và chú thích có sẵn trong thư viện Matplotlib trong bộ sưu tập ảnh của nó, đặc biệt là trong Demo Chú thích.