2015-07-24 3 views
1

Я пытаюсь создать фигуру, в которой каждое измерение многомерного набора данных нанесено на график по каждому другому в сетке подзаголовков. Вот то, что я до сих пор:«Фальшивая» ось тиков и меток с Matplotlib

enter image description here

Х размер определяется по столбцу подзаговор и размерность у определяется ряд. Когда размеры равны, накладывается 1-гистограмма с плотностью по оси y, в противном случае используется 2d гистограмма с плотностью, отображаемой на цвет. При создании каждого подзаголовка я разделяю ось x с первым графиком в этом столбце (используя аргумент sharex в функции Figure.add_subplot). Оси Y разделяются аналогично, за исключением 1d гистограмм.

Это хорошо работает, чтобы держать оси в одном масштабе, но вы можете увидеть проблему в левом верхнем углу. Поскольку большинство осей одинаковы для строк и столбцов, у меня есть только метки в нижней и левой частях фигуры. Проблема в том, что верхняя левая подзадача имеет другую шкалу y, чем остальная часть ее строки.

Я хочу на самом деле иметь тики для осей y других подзаговоров в строке, применяемых к верхнему левому подзаголовку, без, изменяя пределы y подзаголовка. Получение y меток из второго подзаголовка в строке и установка их на первые работы, но фактическое изменение положения тиков не происходит, поскольку пределы осей не совпадают. Я не могу решить, как установить позиции тика в относительных терминах, кроме явного преобразования точек из шкалы одного графика в другой.

EDIT: Так как кто-то спросил, вот базовая версия кода используется для генерации этого:

import numpy as np 
from scipy.stats import gaussian_kde 

def matrix_plot(figure, data, limits, labels): 
    """ 
    Args: 
     figure: matplotlib Figure 
     data: numpy.ndarray, points/observations in rows 
     limits: list of (min, max) values for axis limits 
     labels: list of labels for each dimension 
    """ 

    # Number of dimensions (data columns) 
    ndim = data.shape[1] 

    # Create KDE objects 
    density = [ gaussian_kde(data[:,dim]) for dim in range(ndim) ] 

    # Keep track of subplots 
    plots = np.ndarray((ndim, ndim), dtype=object) 

    # Loop through dimensions twice 
    # dim1 goes by column 
    for dim1 in range(ndim): 
     # dim2 goes by row 
     for dim2 in range(ndim): 

      # Index of plot 
      i = dim2 * ndim + dim1 + 1 

      # Share x-axis with plot at top of column 
      # Share y-axis with plot at beginning of row, unless that 
      # plot or current plot is a 1d plot 
      kwargs = dict() 
      if dim2 > 0: 
       kwargs['sharex'] = plots[0][dim1] 
       if dim1 > 0 and dim1 != dim2: 
        kwargs['sharey'] = plots[dim2][0] 
      elif dim1 > 1: 
       kwargs['sharey'] = plots[dim2][1] 

      # Create new subplot 
      # Pass in shared axis arguments with **kwargs 
      plot = figure.add_subplot(ndim, ndim, i, **kwargs) 
      plots[dim2][dim1] = plot 

      # 1d density plot 
      if dim1 == dim2: 

       # Space to plot over 
       x = np.linspace(limits[dim][0], limits[dim][1], 100) 

       # Plot filled region 
       plot.set_xlim(limits[dim]) 
       plot.fill_between(x, density[dim].evaluate(x)) 

      # 2d density plot 
      else: 

       # Make histogram 
       h, xedges, yedges = np.histogram2d(data[:,dim1], 
        data[:,dim2], range=[limits[dim1], limits[dim2]], 
        bins=250) 

       # Set zero bins to NaN to make empty regions of 
       # plot transparent 
       h[h == 0] = np.nan 

       # Plot without grid 
       plot.imshow(h.T, origin='lower', 
        extent=np.concatenate((limits[dim1], limits[dim2])), 
        aspect='auto') 
       plot.grid(False) 

      # Ticks and labels of except on figure edges 
      plot.tick_params(axis='both', which='both', left='off', 
       right='off', bottom='off', top='off', labelleft='off', 
       labelbottom='off') 
      if dim1 == 0: 
       plot.tick_params(axis='y', left='on', labelleft='on') 
       plot.set_ylabel(labels[dim2]) 
      if dim2 == self._ndim - 1: 
       plot.tick_params(axis='x', bottom='on', labelbottom='on') 
       plot.set_xlabel(labels[dim1]) 

     # Tight layout 
     figure.tight_layout(pad=.1, h_pad=0, w_pad=0) 

А вот то, что я получаю, когда я пытаюсь скопировать тик позиции и этикетки от у-оси второй участок в первом ряду на первый участок:

plots[0][0].set_yticks(plots[0][1].get_yticks()) 
plots[0][0].set_yticklabels(plots[0][1].get_yticklabels()) 

enter image description here

Обратите внимание, как он присваивает клещ позицию по абсолютной шкале, которая значительно выше, тх n масштаб графика плотности. Пределы оси расширяются, чтобы показать тики, поэтому фактическая кривая плотности раздавливается вниз. Кроме того, ярлыки не отображаются.

+0

Возможно, вы можете подделать его с помощью [set_yticklabels] (http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.set_yticklabels) и set_yticks –

+0

Какой код вы использовали для создания? Это поможет понять, как вы это сделали. Кроме того, на самом деле не ответ на ваш вопрос, но вы можете попробовать использовать 'scatter_matrix' из [pandas] (http://pandas.pydata.org/pandas-docs/stable/visualization.html # plotting-tools) - он делает этот точный вид сюжета и должен правильно обрабатывать эти пределы. – Ajean

+0

Я отредактировал вопрос с примером кода и результатами с копированием тиков с графика 2 гистограммы на верхний график плотности. – JaredL

ответ

1

Из комментария Ajean для информирования меня о функции scatter_matrix в пакете pandas, который делает более или менее то, что я пытаюсь сделать здесь. Я проверил источник на GitHub и нашел ту часть, где они «исправить» оси на верхнем левом участке, чтобы соответствовать в строке и общей оси у вместо оси плотности:

if len(df.columns) > 1: 
    lim1 = boundaries_list[0] 
    locs = axes[0][1].yaxis.get_majorticklocs() 
    locs = locs[(lim1[0] <= locs) & (locs <= lim1[1])] 
    adj = (locs - lim1[0])/(lim1[1] - lim1[0]) 

    lim0 = axes[0][0].get_ylim() 
    adj = adj * (lim0[1] - lim0[0]) + lim0[0] 
    axes[0][0].yaxis.set_ticks(adj) 

    if np.all(locs == locs.astype(int)): 
     # if all ticks are int 
     locs = locs.astype(int) 
    axes[0][0].yaxis.set_ticklabels(locs) 

К сожалению, это выглядит как чего я боялся: нет более элегантного способа сделать это, кроме как вручную преобразовать местоположения тиков из одного диапазона в другой. Вот моя версия, которая идет сразу после двойной петли:

# Check there are more plots in the row, just in case 
if ndim > 1: 
    # Get tick locations from 2nd plot in first row 
    ticks = np.asarray(plots[0][1].yaxis.get_majorticklocs()) 

    # Throw out the ones that aren't within the limit 
    # (Copied from pandas code, but probably not necessary) 
    ticks = ticks[(ticks >= limits[0][0]) & (ticks <= limits[0][1])] 

    # Scale ticks to range of [0, 1] (relative to axis limits) 
    ticks_scaled = (ticks - limits[0][0])/(limits[0][1] - limits[0][0]) 

    # Y limits of top-left density plot (was automatically determined 
    #  by matplotlib) 
    dlim = plots[0][0].get_ylim() 

    # Set the ticks scaled to the plot's own y-axis 
    plots[0][0].set_yticks((ticks_scaled * (dlim[1] - dlim[0])) + dlim[0]) 

    # Set tick labels to their original positions on the 2d plot 
    plots[0][0].set_yticklabels(ticks) 

Это результаты, которые я искал.

Смежные вопросы