Matplotlib:具有(多个)断轴

时间:2018-04-15 14:23:47

标签: python matplotlib boxplot

首先,我向您展示了我需要的东西:我需要一个破损的x轴的箱线图,可能不止一次。这个例子就是一个例子 enter image description here

现在:我有两个表单XYX = floatY = int)列表。首先,我根据YXX的整数部分在子列表中对Y进行分组:

number_of_units = int(max(X)) + 1
my_data = []
for i in range(number_of_units):
  my_data.append([])

for i in range(len(X)):
  j = int(X[i] )
  my_data[j].append(Y[i])

以这种方式,my_data是一个列表列表,其中包含number_of_units个子列表。 k个子列表包含与整数部分为X的{​​{1}}值相关联的所有Y值。问题所在:大多数子列表都是空的:k跨越多个数量级,Y的典型值为number_of_units,但大多数10^5都有整数部分Y以便[1,10]中的大多数子列表都是空的。直接后果是,如果我做

my_data

我获得了如下图所示的内容(注意"右上角"红点):

enter image description here

这是由于fig, ax = plt.subplots() ax.boxplot(my_data, 'options') 中大多数子列表的空白:大多数情节显示"零频率"。所以我需要的是在频率为零时打破绘图的x轴。请注意:

  • 必须动态找到必须破坏斧头的点,因为它们随数据而变化。
  • 斧头必须多次被打破的可能性很高

理论观念

  1. 将列表my_data拆分为my_data列表,其中必须根据M的空白进行拆分:如果my_data是第一个空子列表,而my_data[k]是第一个组;然后找到索引为my_data[0],...,my_data[k-1]的第一个非空子列表,然后第二个组开始。当我找到另一个空的子列表时,第二组形成,依此类推。我希望我很清楚。

  2. 为每个新列表列表执行>k。这次没有一个子列表是空的。

  3. 将每个ax.boxplot()绘制为子图,并按照建议here加入所有子图。

  4. 这种方法对我来说有很多困难。主要问题是我不知道先验我需要的子图的数量,这个数字取决于数据集,这是一个我真的不知道怎么做的问题克服。所以我问:

    如何自动定位具有非零频率的X轴区域并仅绘制那些区域,每次区域结束时都会使用基础破碎的轴?

    任何建议都将不胜感激。

    修改

    我的问题不是this questions的重复,因为后者不包含有关如何打破X轴的任何解释。但是,问题12中的信息组合可能会完全解决问题。我实际上正在努力,当问题解决后我会进一步编辑问题。

2 个答案:

答案 0 :(得分:1)

考虑这样构建的数据样本:

import numpy as np
from pylab import *
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from itertools import *
from operator import itemgetter
import scipy.stats as stats

def truncated_power_law(a, m):
x = np.arange(1, m+1, dtype='float')
pmf = 1/x**a
pmf /= pmf.sum()
return stats.rv_discrete(values=(range(1, m+1), pmf))

a, m = 2, 100000
d = truncated_power_law(a=a, m=m)
N = 10**2

X = np.sort(np.asarray(list(set(d.rvs(size=N)))))
Y = []
for i in range(0,len(X)):
Y.append(i*np.random.rand(100))

除了X是分布的幂律之外,不要关心数据。这实现了min(X)max(X)之间的许多值不会出现在样本中。

现在,如果你限制自己做

m_props = {'color': 'red',}
b_props = {'color': 'black', 'linestyle': '-'}
w_props = {'color': 'black', 'linestyle': '-'}
c_props = {'color': 'black', 'linestyle': '-'}

f_ugly, ax_ugly = plt.subplots()
ax_ugly.boxplot(Y, notch = 0, sym = '', positions = X, medianprops = 
        m_props, boxprops = b_props, whiskerprops = w_props, capprops 
        = c_props)

您可以获得以下内容:bad_box

现在考虑一下:

#X is divided in sublists of consecutive values
dominiums = []
for k, g in groupby(enumerate(X), lambda (i,j):i-j):
    dominiums.append(map(itemgetter(1), g))

number_of_subplots = len(dominiums)

k = 0
d = .01
l = .015

f, axes = plt.subplots(nrows = 1, ncols = number_of_subplots, sharex = 
              False, sharey = True, gridspec_kw = {'width_ratios':
              [3*len(dominiums[h]) for h in 
              range(number_of_subplots)],'wspace':0.05})

axes[0].yaxis.tick_left()
axes[0].spines['right'].set_visible(False)

kwargs = dict(transform = axes[0].transAxes, color='k', linewidth = 1, 
         clip_on = False)
axes[0].plot((1-d/1.5,1+d/1.5), (-d,+d), **kwargs)
axes[0].plot((1-d/1.5,1+d/1.5),(1-d,1+d), **kwargs)
kwargs.update(transform = axes[-1].transAxes)
axes[-1].plot((-l,+l), (1-d,1+d), **kwargs)
axes[-1].plot((-l,+l), (-d,+d), **kwargs)

for i in range(number_of_subplots):
    data_in_this_subplot = []
    for j in range(len(dominiums[i])):
        data_in_this_subplot.append([])
        data_in_this_subplot[j] = Y[k]
        k = k + 1

    axes[i].boxplot(data_in_this_subplot, notch = 0, sym = '', 
            positions = dominiums[i], medianprops = m_props, boxprops 
            = b_props, whiskerprops = w_props, capprops = c_props)

    if i != 0:
        axes[i].spines['left'].set_visible(False)
        axes[i].tick_params(axis = 'y', which = 'both', labelright = 
                False, length = 0)
    if i != number_of_subplots -1:
        axes[i].spines['right'].set_visible(False)
        kwargs = dict(transform = axes[i].transAxes, color='k', 
                 linewidth = 1, clip_on=False)
        axes[i].plot((1-l,1+l), (-d,+d), **kwargs)
        axes[i].plot((1-l,1+l),(1-d,1+d), **kwargs)
        kwargs.update(transform = axes[i].transAxes)
        axes[i].plot((-l,+l), (1-d,1+d), **kwargs)
        axes[i].plot((-l,+l), (-d,+d), **kwargs)

使用第一个图的相同数据,后一个代码产生以下内容: good box

恕我直言,这段代码完全回答了这个问题:它自动定位了X轴的相关区域,并且只绘制了那些区域,每当区域结束时都会显示未分解的破碎轴。

解决方案的Weankess :它有许多必须针对每个不同数据集调整的任意参数(例如d,l,{{1}中的数字3 }})

解决方案的强度:您不需要知道先验相关区域的数量(即子图的数量)

感谢wwii的使用回答和评论。

答案 1 :(得分:0)

没有进一步的证据(您的问题缺少XY的最小示例, 通过位置/索引看起来XY相关彼此并且您试图通过放置Ymy_data中,在相关X值的索引处。我想你正在这样做,所以你不必将X值传递给.boxplot(),但这会在你的可视化中产生许多你不想要的空白空间。 / p>

如果您的数据与此假数据类似:

X = [1,2,3,9,10,11,50,51,52]
Y = [590, 673, 49, 399, 551, 19, 618, 358, 106, 84,
     537, 865, 507, 862, 905, 335, 195, 250, 54, 497,
     224, 612, 4, 16, 423, 52, 222, 421, 562, 140, 324,
     599, 295, 836, 887, 222, 790, 860, 917, 100, 348,
     141, 221, 575, 48, 411, 0, 245, 635, 631, 349, 646]

通过在构造X的for循环中添加print语句,可以看到Ymy_datamy_data之间的关系:

....
    my_data[j].append(Y[i])
    print(f'X[{i}]:{X[i]:<6}Y[{i}]:{Y[i]:<6}my_data[{j}:{my_data[j]}')  

>>>
X[0]:1     Y[0]:590   my_data[1:[590]
X[1]:2     Y[1]:673   my_data[2:[673]
X[2]:3     Y[2]:49    my_data[3:[49]
X[3]:9     Y[3]:399   my_data[9:[399]
X[4]:10    Y[4]:551   my_data[10:[551]
X[5]:11    Y[5]:19    my_data[11:[19]
X[6]:50    Y[6]:618   my_data[50:[618]
X[7]:51    Y[7]:358   my_data[51:[358]
X[8]:52    Y[8]:106   my_data[52:[106]

>>>

你可能最好不要首先创建空白空间,然后使用.plot作为X的{​​{1}}将x和y传递给'plot {1}}&#39; positions参数

# again fake Y data
y_s = [[thing] for thing in Y[:len(X)]]
plt.boxplot(y_s, positions=X)

这仍然会在情节中留下很多空白空间。通过将XY分隔为连续的X值切片,然后使用循环创建片段的子图,可以修复(请参阅Dynamically add/create subplots in matplotlib