使用Matplotlib

时间:2017-09-04 13:30:57

标签: python matplotlib networkx colorbar

我想使用Networkx和Matplotlib为网络边缘着色,其中每条边(i,j)的值为G[i][j]['label'],介于0和1之间。

然而,通常,这些值要非常接近于零,要么非常接近1.然后很难看到颜色的变化,因为一切都是非常红或非常蓝(使用coolwarm色彩图)。

然后,我的想法是应用过滤器filtR,就像其中一个:

enter image description here

它只是一个多项式函数,它提供从[0,1]到[0,1]的双射,并在0或1附近拉伸更多的值。如果需要,逆是易处理的。

现在,我只是将它应用于边缘的值,以便定义它的颜色:

cm        = plt.get_cmap('coolwarm') 
cNorm     = colors.Normalize(vmin=0., vmax=1.)
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
colorList = []

# The color is defined by filtR(G[i][j]['label'])
val_map   = {(i,j): filtR(G[i][j]['label']) for (i,j) in G.edges()}
values    = [scalarMap.to_rgba(val_map[e]) for e in G.edges()]
edges     = nx.draw_networkx_edges(G,edge_color=values,edge_cmap=plt.get_cmap('coolwarm'))


# Definition of the colorbar :-(
sm = cmx.ScalarMappable(cmap=cmx.coolwarm)
sm.set_array(values)
plt.colorbar(sm)

问题现在是:我想定义相应的颜色栏。

现在,它通过filtR函数显示我的边缘的评估,这是没有意义的:过滤器的唯一目的是修改[0,1]间隔上的颜色重新分区,以便提高图表的可读性。

例如,我得到:

enter image description here

我很满意左边的部分,但不是正确的部分,颜色条应该是这样的:

enter image description here

这里的过滤功能显然不是最好的,但它应该为您提供更好的说明。

我尝试在颜色栏定义之前重新定义values

# Definition of the colorbar :-(

new_val_map = {(i,j): filtR(G[i][j]['label']) for (i,j) in G.edges()}
new_values  = [scalarMap.to_rgba(val_map[e]) for e in G.edges()]

sm = cmx.ScalarMappable(cmap=cmx.coolwarm)
sm.set_array(new_values)
plt.colorbar(sm)

但没有任何改变。

我对Matplotlib的理解是有限的,所提出的代码已经是堆栈溢出答案的拼凑。

3 个答案:

答案 0 :(得分:2)

您必须定义own custom colormap并在自定义cbar中使用它:

import matplotlib.pylab as plt
from matplotlib import colorbar, colors

def make_colormap(seq, name='mycmap'):
    """Return a LinearSegmentedColormap
    seq: a sequence of floats and RGB-tuples. The floats should be increasing
    and in the interval (0,1).
    """
    seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
    cdict = {'red': [], 'green': [], 'blue': []}
    for i, item in enumerate(seq):
        if isinstance(item, float):
            r1, g1, b1 = seq[i - 1]
            r2, g2, b2 = seq[i + 1]
            cdict['red'].append([item, r1, r2])
            cdict['green'].append([item, g1, g2])
            cdict['blue'].append([item, b1, b2])
    return colors.LinearSegmentedColormap(name, cdict)

def generate_cmap(lowColor, highColor, lowBorder, highBorder):
    """Apply edge colors till borders and middle is in grey color"""
    c = colors.ColorConverter().to_rgb
    return make_colormap([c(lowColor), c('grey'),l owBorder, c('grey'), .5, \
     c('grey'), highBorder ,c('grey'), c(highColor)])

fig = plt.figure()
ax = fig.add_axes([.05, .05, .02, .7]) # position of colorbar

cbar = colorbar.ColorbarBase(ax, cmap=generate_cmap('b','r',.15,.85),
  norm=colors.Normalize(vmin=.0, vmax=1)) # set min, max of colorbar
ticks = [0.,.1,.2,.3,.4,.5,.6,.7,.8,.9,1.]
cbar.set_ticks(ticks) # add ticks
plt.show()

enter image description here

答案 1 :(得分:2)

基本上你根本不想改变色彩图。 Instaed您想要创建自定义规范化。为此,您可以继承matplotlib.colors.Normalize并让它返回自定义函数的值。该函数需要将vminvmax之间的值作为输入,并返回[0,1]范围内的值。

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors


class MyNormalize(mcolors.Normalize):
    def __call__(self, value, clip=None):
        # function to normalize any input between vmin and vmax linearly to [0,1]
        n = lambda x: (x-self.vmin)/(self.vmax-self.vmin)
        # nonlinear function between [0,1] and [0,1]
        f = lambda x,a: (2*x)**a*(2*x<1)/2. +(2-(2*(1-1*x))**a)*(2*x>=1)/2.
        return np.ma.masked_array(f(n(value),0.5))


fig, (ax,ax2) = plt.subplots(ncols=2)

x = np.linspace(-0.3,1.2, num=101)
X = (np.sort(np.random.rand(100))*1.5-0.3)

norm=  MyNormalize(vmin=-0.3, vmax=1.2)

ax.plot(x,norm(x))
im = ax2.imshow(X[::-1,np.newaxis], norm=norm, cmap="coolwarm", aspect="auto")
fig.colorbar(im)

plt.show()

enter image description here

所需颜色条的图像反而暗示了部分线性函数,如下面所用的那样。

class MyNormalize2(mcolors.Normalize):
    def __call__(self, value, clip=None):
        n = lambda x: self.vmin+(self.vmax-self.vmin)*x
        x, y = [self.vmin, n(0.2), n(0.8), self.vmax], [0, 0.48,0.52, 1]
        return np.ma.masked_array(np.interp(value, x, y))

enter image description here

答案 2 :(得分:1)

您有自己喜欢的色彩图(请说coolwarm),并且您希望根据filtR函数对其进行扭曲:

enter image description here

Nb:此函数与初始问题中建议的函数相反。

感谢Serenity的启发:必须在色彩映射定义上完成工作:

def distortColorMap(cm,inv = lambda x:x):
    """Inspired from 'make_colormap' in Serenity's answer.

    Inputs : a pre-existing colormap cm, 
             the distorsion function inv

    Output : the distorted colormap"""

    def f(color,inv):
        """In the sequence definition, modifies the position of stops tup[0] according the transformation function.

           Returns the distorted sequence."""
        return map(lambda tup:(inv(tup[0]),tup[1],tup[2]),color)

    # Extract sequences from cm, apply inv
    C = cm.__dict__['_segmentdata']
    cdict = {'red': f(C['red']  ,inv), 'green': f(C['green'],inv), 'blue': f(C['blue'] ,inv)}

    name = 'new_'+cm.__dict__['name']
    return colors.LinearSegmentedColormap(name, cdict)

然后,这很容易使用:

cm        = plt.get_cmap('coolwarm')
cm        = distortColorMap(cm,inv = filtR) # all the job is done here

cNorm     = colors.Normalize(vmin=0., vmax=1.)
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)

# The color is the natural value G[i][j]['label']
val_map   = {(i,j): G[i][j]['label'] for (i,j) in G.edges()}
values    = [scalarMap.to_rgba(val_map[e]) for e in G.edges()]
edges     = nx.draw_networkx_edges(G,edge_color=values,edge_cmap=plt.get_cmap('coolwarm'))


# Definition of the colorbar : just use the new colormap
sm = cmx.ScalarMappable(cmap=cm)
sm.set_array(values)
plt.colorbar(sm)

然后我们得到相应的颜色栏:

enter image description here

这很酷,因为您不再需要定义整个颜色序列(现在所有内容都是根据扭曲函数的定义完成的),因为您仍然可以使用Matplotlib提供的精美色彩映射!

修改

有关filtR功能和我的动机的更多信息。

在此示例中,filtR定义为:

exponent = 7. 
filtR    = lambda y: ((2*y-1)**(1./exponent)+1.)/2.

对于exponent的不同值,我们有一类函数(具有或多或少的平滑行为)。能够从一个定义跳到另一个定义可以帮助确定最佳可视化。

实际上,对于任何e(甚至是奇数),当x为负时,Python不喜欢处理x**1/e。但这不是什么大问题,我们只是恰当地定义了7根(或任何其他奇数指数)。

然而,这不是热点:我们只需要从[0,1]到[0,1]的数学双射。然后我们可以选择最适合我们需求的那个。

例如,我们也可能希望将filtR函数定义为filtR = lambda y: y**4,因为我们希望对最低值有更好的可读性。我们会得到:

enter image description here

它也适用于日志,分段或阶梯函数......

我想要一个通用且灵活的工具,这可以让我快速关注某些特定领域。我不想为每个可视化测试手动创建具有停止和颜色值的序列。

我还希望能够在需要时将此工作重用于其他项目。