Создание линейной диаграммы с разноцветными линиями для разных значений y с помощью matplotlib

#python #matplotlib

#python #matplotlib

Вопрос:

введите описание изображения здесь

Я совсем новичок в python и в последнее время много работаю с pandas и matplotlib для отображения данных на работе. Я пытаюсь построить диаграмму вибрации, где ось x — глубина (в метрах), а ось y — уровень вибрации (макс. осевой Z). Я хочу отобразить уровень вибрации в 4 разных цветах:

  • Зеленый — для низкого уровня (значения <= 1.5)
  • Оранжевый для среднего (значения 1.5 < 2.5)
  • Красный для высоких значений (значения 2.5 < 5)
  • Бордовый для тяжелых (значения> = 5).

Я прочитал документацию для matplotlib multicolored line и сумел создать график, который я хочу создать. Однако я до сих пор не понимаю, почему это работает, есть несколько строк, в которых я не понял код и как он работает на моем графике, поэтому я публикую этот вопрос, чтобы получить некоторую ясность. Мои вопросы:

  • Для чего нужны точки и сегменты? Почему я должен изменять и объединять их?
  • что делает lc.set_array(y) в этом коде?
  • Могу ли я каким-либо образом сделать код короче и аккуратнее, особенно в той части, где я назначаю метку, ширину строки и автозапуск? Могу ли я объединить их все в одну строку? Вместо записи каждой строки для каждого атрибута.

Большое спасибо за вашу помощь!

 import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm

df = pd.read_excel("C:/Users/AmeliaB/Documents/Python/8-5in_plotting.xlsx", header=0)
df['DATETIME'] = pd.to_datetime(df.DATETIME)
# define variables
y = np.array(df["DHT001_Accel_Axial_Z_Max"])
x = np.array(df["Hole_Depth"])

# Create a set of line segments so we can color them individually
# This creates the points as a N x 1 x 2 array so that we can stack points
# together easily to get the segments. The segments array for line collection
# needs to be (numlines) x (points per line) x 2 (for x and y)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

fig, ax = plt.subplots(1)

cmap = ListedColormap(['green', 'orange', 'red', "maroon"])
norm = BoundaryNorm([1, 1.5, 2.5, 5, 5.5], cmap.N)
lc = LineCollection(segments, cmap=cmap, norm=norm)
lc.set_array(y)
lc.set_linewidth(1)  # The thickness of the line
ax.add_collection(lc)
ax.autoscale_view()
fig.colorbar(lc) #Add colorbar
plt.xlabel ("Hole depth")
plt.ylabel ("Vibration level")
plt.show()
  

Ответ №1:

Для чего нужны точки и сегменты? Почему я должен изменять и объединять их?

Стандартный график использует массив значений x ( [x0, x1, x2, ...] и значений y ( [y0, y1, y2, ...] . Они будут соединены как точки (x0, y0) с (x1, y1) to (x2, y2) to … . Но этот подход допускает только один цвет для всего.

Скопированное вами решение использует сегменты одной строки, первый сегмент — « (x0, y0) to (x1, y1) «. Второй сегмент используется повторно (x1, y1) , рисуя (x1, y1) (x2, y2) . Таким сегментам можно присвоить индивидуальные цвета. Для этого необходимо, чтобы каждый сегмент был представлен в виде [[x0, y0], [x1, y1]] (2D-массива). Эти сегменты могут быть созданы из исходных массивов x и y. Все сегменты вместе образуют 3D-массив [[[x0, y0], [x1, y1]], [[x1, y1], [x2, y2]], ... ] .

Соответствующий код выглядит следующим образом:

 points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
  

np.array([x, y]) создает массив 2xN из x и соответствующих позиций y. Вызов .T переносит это в массив Nx2. Чтобы сделать ее похожей на структуру, необходимую для массива 3D-сегментов, массив точек преобразуется в массив Nx1x2 (обратите внимание, что -1 здесь заменяется значением, необходимым для того, чтобы в преобразованном массиве было столько же элементов, сколько в исходном). Теперь точки имеют структуру [[[x0, y0]], [[x1, y1]], ...]

points[:-1] это просто список всех точек, кроме последней. Их можно использовать для начальных точек сегментов.

Аналогично, points[1:] это просто список всех точек, кроме первой. Они могут использоваться для конечных точек сегментов.

np.concatenate([..., ..., axis=1) объединяет два списка точек вместе по их второму измерению ( axis=1 ) в структуре для массива сегментов. Итак, это создает нужный список сегментов : [[[x0, y0], [x1, y1]], [[x1, y1], [x2, y2]], ... ] .

что делает lc.set_array(y) в этом коде?

Здесь set_array присваивается одно «значение цвета» каждому отдельному сегменту каждой линии. Вместо y любого другого числового значения может быть использовано. Замена y на x будет иметь цвета, следующие за осью x. Эти «значения цвета» преобразуются в реальные цвета с использованием назначенной цветовой карты. Наименьшее из значений будет сопоставлено с наименьшим цветом, наибольшее значение будет сопоставлено с наивысшим цветом, а остальные будут плавно следовать между ними. (Это также работает с непрерывными цветовыми картами, например, с диапазоном от синего до белого до красного.)

Могу ли я каким-либо образом сделать код короче и аккуратнее, особенно в той части, где я назначаю метку, ширину строки и автозапуск? Могу ли я объединить их все в одну строку? Вместо записи каждой строки для каждого атрибута.

Теперь есть 3 простых вызова, в которых вы можете добавить дополнительные параметры (размер шрифта, цвет, …) понятным способом. Просто оставьте ненужные параметры и вызовы, matplotlib предоставит соответствующие значения по умолчанию. Изменение всего на один сложный вызов, где было бы неясно, какой параметр к чему применяется, было бы менее читаемым и более сложным в обслуживании.

Обычно при построении кривой пределы для осей x и y вычисляются автоматически. Но для линейных сегментов границы осей не изменяются (это позволяет добавлять стрелки и вспомогательные линии на график, сохраняя начальные границы). Вызов ax.autoscale_view() пересчитает ограничения, если это необходимо.

Комментарии:

1. Привет @JohanC Большое спасибо за подробное объяснение! Теперь это имеет для меня больше смысла. Я раньше не знал, что для этого отрезка требуется 3D-массив … спасибо 🙂