首先,我向您展示了我需要的东西:我需要一个破损的x轴的箱线图,可能不止一次。这个例子就是一个例子
现在:我有两个表单X
和Y
(X = float
,Y = int
)列表。首先,我根据Y
(X
和X
的整数部分在子列表中对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
我获得了如下图所示的内容(注意"右上角"红点):
这是由于fig, ax = plt.subplots()
ax.boxplot(my_data, 'options')
中大多数子列表的空白:大多数情节显示"零频率"。所以我需要的是在频率为零时打破绘图的x轴。请注意:
理论观念
将列表my_data
拆分为my_data
列表,其中必须根据M
的空白进行拆分:如果my_data
是第一个空子列表,而my_data[k]
是第一个组;然后找到索引为my_data[0],...,my_data[k-1]
的第一个非空子列表,然后第二个组开始。当我找到另一个空的子列表时,第二组形成,依此类推。我希望我很清楚。
为每个新列表列表执行>k
。这次没有一个子列表是空的。
将每个ax.boxplot()
绘制为子图,并按照建议here加入所有子图。
这种方法对我来说有很多困难。主要问题是我不知道先验我需要的子图的数量,这个数字取决于数据集,这是一个我真的不知道怎么做的问题克服。所以我问:
如何自动定位具有非零频率的X轴区域并仅绘制那些区域,每次区域结束时都会使用基础破碎的轴?
任何建议都将不胜感激。
修改
我的问题不是this questions的重复,因为后者不包含有关如何打破X轴的任何解释。但是,问题1和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)
现在考虑一下:
#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)
恕我直言,这段代码完全回答了这个问题:它自动定位了X轴的相关区域,并且只绘制了那些区域,每当区域结束时都会显示未分解的破碎轴。
解决方案的Weankess :它有许多必须针对每个不同数据集调整的任意参数(例如d,l
,{{1}中的数字3
}})
解决方案的强度:您不需要知道先验相关区域的数量(即子图的数量)
感谢wwii的使用回答和评论。
答案 1 :(得分:0)
没有进一步的证据(您的问题缺少X
和Y
的最小示例,
通过位置/索引看起来X
和Y
值相关彼此并且您试图通过放置Y
值在 my_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语句,可以看到Y
,my_data
和my_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)
这仍然会在情节中留下很多空白空间。通过将X
和Y
分隔为连续的X
值切片,然后使用循环创建片段的子图,可以修复(请参阅Dynamically add/create subplots in matplotlib )