Я пытаюсь сделать трехмерный график matplot. У меня проблемы с отображением полной оси с хорошо выровненными надписями. Я обрисовал в общих чертах шаги, которые я пробовал, ниже.
1) Я могу установить метки оси Y, используя:
yTicks = list(range(0,90,5)
ax.set_yticks(range(len(yTicks)), True)
Однако, как видите, надписи очень плохо выровнены. Это также не соответствует тому, что я на самом деле определил, а именно, что тики должны считаться на 5, а не на 10.
2) Если я попробую использовать и set_yticklabels
, выравнивание исправится, но будет печататься только часть оси. Вот код и изображение:
ax.set_yticklabels(yTicks, verticalalignment='baseline',
horizontalalignment='left')
Обратите внимание, как ось Y изменилась с 80 на 40.
3) И если я избавлюсь от True
в set_yticks
, все сожмется вместе:
4) Наконец, если я использую как set_yticks
, так и set_yticklabels
, вызывая get_yticks()
в функции меток, это почти работает, но вы можете видеть, что линии осей выходят за пределы «поверхности» графика:
ax.set_yticks(range(len(yTicks)), True)
ax.set_yticklabels(ax.get_yticks(), verticalalignment='baseline',
horizontalalignment='left')
5) Вот более полная версия моего кода для справки:
plt.clf()
ax = plt.axes(projection='3d')
ax.bar3d(x,y,z,
1,1,[val*-1 if val != 0 else 0 for val in z])
xTicks = list(range(0,25,2))
yTicks = list(range(30,90,5))
ax.set_zlim(0, 1)
ax.set_xticks(range(len(xTicks)), True)
ax.set_yticks(range(len(yTicks)), True)
ax.set_xticklabels(ax.get_xticks(),
verticalalignment='baseline',
horizontalalignment='left')
ax.set_yticklabels(ax.get_yticks(),
verticalalignment='baseline',
horizontalalignment='left')
plt.savefig(file_path)
Как я могу заставить его показывать мою полную ось (0-90) с интервалом 5 и правильно ли выровнять?
6) ОБНОВИТЬ: согласно приведенному ниже разговору с @ImportanceOfBeingErnest, вот проблема, с которой я все еще сталкиваюсь, используя следующий код:
x=[15,28,20]; y=[30,50,80]; z=[1,1,1]
plt.clf()
ax = plt.axes(projection='3d')
ax.bar3d(x,y,z,
1,1,[val*-1 if val != 0 else 0 for val in z])
xTicks = list(range(0,25,2))
yTicks = list(range(30,90,5))
ax.set_xticks(xTicks)
ax.set_yticks(yTicks)
ax.set_yticklabels(ax.get_yticks(),
verticalalignment='baseline',
horizontalalignment='left')
ax.set_zlim(0, 1)
plt.savefig(getSaveGraphPath(save_name))
Вы можете увидеть фактические единицы данных полос в примере, но это также не имеет значения. Любые значения дают одинаковые результаты. Проблема не в полосах, а в значениях оси, и я предоставляю эту информацию в своем описании выше. Вы правы насчет set_yticks
(отличного от plt.yticks ()), но удаление этого аргумента дает еще более ужасные результаты. Он буквально перестает считать ось на полпути
Если буквально любое значение дает одинаковые результаты, это будет сутью проблемы. Но я не могу воспроизвести это. Я не вижу изображения, на котором вы утверждаете, что не использовали никаких модификаций оси, поэтому нет, я не могу «увидеть фактические единицы данных полос в примере».
Я добавил свой код выше, чтобы вы могли видеть. Вы можете выбрать любые значения для x, y, z, если они попадают в определенные мной диапазоны меток.
Без фактических данных единственным ответом является тот тривиальный факт, что вы можете установить галочки через ax.set_yticks(np.arange(0,90,5))
.
Я действительно не понимаю, чего еще вы хотите, поскольку все дело в том, что НЕ имеет значения, какие данные, проблема та же. Если вам ДЕЙСТВИТЕЛЬНО нужны значения, используйте x = [15,28,20], y = [30,50,80], z = [1,1,1]. Но дело в том, что вы можете использовать три значения, которые я предоставил, 100 значений или пустые данные. Это не имеет значения, потому что я говорю не о столбцах, а о рендеринге оси графика. Вы определенно должны суметь это воспроизвести. Если вы хотите попробовать конкретные значения, используйте три, которые я предоставил, но это буквально не имеет значения.
Как уже говорилось, вы можете установить галочки через ax.set_yticks
.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x=[15,28,20]; y=[30,50,80]; z=[1,1,1]
ax = plt.axes(projection='3d')
ax.bar3d(x,y,z,
1,1,[val*-1 if val != 0 else 0 for val in z])
yTicks = list(range(30,90,5))
ax.set_yticks(yTicks)
ax.set_yticklabels(ax.get_yticks(),
verticalalignment='baseline',
horizontalalignment='left')
ax.set_zlim(0, 1)
plt.show()
Это покажет желаемые 5 единиц измерения по оси y.
Это правда, но проблема, которая вызвала у меня первоначальный вопрос, заключается в том, что при таком подходе ярлыки, как правило, очень плохо выровнены. Например, обратите внимание, что 85 находится немного впереди своей отметки (это легче увидеть на самом первом изображении графика, которое я включил, но его можно увидеть и на вашем). Невозможно настроить выравнивание с помощью set_yticks, нужно использовать set_ylabels. Однако это создает вышеупомянутые проблемы. Я изо всех сил пытаюсь найти перестановку инструкций, которые 1) устанавливают y-метки и 2) устанавливают выравнивание меток метки, не вызывая проблем.
Ммм, я имею в виду, что вы все еще можете добавить ax.set_yticklabels(ax.get_yticks(), verticalalignment='baseline', horizontalalignment='left')
в код в ответе, если хотите ?!
Я сделал, вот в чем вопрос. Вы можете увидеть, как это выглядит на графике.
Я немного запутался в том, какая команда, по вашему мнению, что именно делает. Я только что обновил свой ответ этой строкой и полученным графиком.
Спасибо за терпение, я скопировал ваш код (только что добавил ax.set_xticks(xTicks)
), но он разрешается по-другому. Во-первых, ваш график заканчивается на 16 (по оси x), хотя код устанавливает диапазон, начинающийся с 0, а мой - на 0. Однако мой не окрашивает всю ось, только насколько далеко. как есть данные. Пожалуйста, просмотрите обновление моего вопроса, чтобы увидеть, что я вижу.
Ну, вы установили некоторые отметки, которые, как кажется, выходят за пределы оси.
Итак, после долгих проб и ошибок единственный способ заставить график правильно отображать оси в различных предельных случаях, заключается в следующем. Я не совсем доволен им (обратите внимание, как последняя метка y-tick не отображается), но это единственная версия, в которой числа фактически находятся рядом с их отметками). Мне пришлось разрешить ограничениям по x и y действовать только в том случае, если данные не превышают их значений, тогда как граница z является жестким пределом. Я не утверждаю, что понимаю, почему все эти перестановки необходимы (все это проблема только при трехмерном построении), но это решение, которое работает для меня:
plt.clf()
ax = plt.axes(projection='3d')
# Need to force include fake NaN data at the axis limits to make sure labels
# render correctly
#
# xLims and yLims create boundaries only if data doesn't stretch beyond them
xstart, xend = xLims
ystart, yend = yLims
x = [xstart] + x + [xend]
y = [ystart] + y + [yend]
z = [numpy.nan] + z + [numpy.nan]
# Plot graph
ax.bar3d(x,y,z,1,1,[val*-1 if val != 0 else 0 for val in z])
# Set z boundary (after graph creation)
ax.set_zbound(zBounds)
# Need to adjust labels slightly to make sure they align properly
use_x_ticks = ax.get_xticks()
### ON SOME SYSTEMS, use_x_ticks = ax.get_xticks()[1:] is needed for correct alignment ###
ax.set_xticklabels([int(x) if x==int(x) else x for x in use_x_ticks],
horizontalalignment='right',
verticalalignment='baseline')
ax.set_yticklabels([int(y) if y==int(y) else y for y in ax.get_yticks()],
verticalalignment='top')
# Save graph
plt.savefig(file_save_path)
Как вы можете видеть ниже, все хорошо выровнено:
Второй аргумент
set_yticks
- это логическое значение, указывающее, является ли первый аргумент второстепенными тиками. Кажется, нет никакого смысла использовать список в качестве второго аргумента. Однако настоящая проблема здесь не может быть решена, потому что мы просто не знаем фактических единиц данных ваших столбцов. См. минимальный воспроизводимый пример.