密密麻麻-不同颜色的表面

时间:2019-01-01 02:01:14

标签: python python-3.x jupyter-notebook plotly

我试图在Plotly for Python中绘制几个不同颜色的表面。

具体地说,表面显示了在相空间中不同点处采取动作的预测奖励函数。由于我在每个点上都有几种可能的操作,因此每个表面都是不同的。我想对每个表面进行唯一着色,但与x,y或z坐标无关。

我尝试遵循R中的答案,但是我无法弄清楚自己做错了什么。我总是得到相同的蓝色。由于我在代码的其他部分中使用了PyPlot,因此我从默认的matplotlib表中选择颜色。

这是玩具数据的基本示例。

import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objs as go
import plotly.offline as off

off.init_notebook_mode()

make_int = np.vectorize(int)
cmap = plt.get_cmap("tab10")

saddle = np.array([[x**2-y**2 for x in np.arange(-10,11)] for y in np.arange(-10,11)])
paraboloid = np.array([[x**2 + y**2-100 for x in np.arange(-10,11)] for y in np.arange(-10,11)])

mycolors_a = make_int(256*np.array(cmap(1)[0:3])).reshape((1, 1,-1)).repeat(21, axis = 0).repeat(21, axis =1)
mycolors_b = make_int(256*np.array(cmap(2)[0:3])).reshape((1, 1,-1)).repeat(21, axis = 0).repeat(21, axis =1)
trace_a = go.Surface(z = saddle, surfacecolor = mycolors_a, opacity = .7, showscale = False, name = "Trace A")
trace_b = go.Surface(z = paraboloid, surfacecolor = mycolors_b, opacity = .7, showscale = False, name = "Trace B")

data = [trace_a, trace_b]
off.iplot(data)

产生以下内容:

Output Sample

我应该看到一个蓝色的鞍和一个橙色的抛物面,但是我没有。请注意,即使将参数更改为cmap,我也始终获得相同的蓝色。感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

documentation在这里有点神秘。

  

surfacecolor

     

(列表,numpy数组或Pandas系列的数字,字符串或日期时间。)

     

设置表面颜色值,用于设置独立于z的色标。

我从来没有设法放入一个字符串列表,例如'rgb(0.3,0.5,0)'或RGB元组之类的颜色值。

但是您可以使用所需的颜色定义自己的色阶。

colorscale = [[0, 'rgb' + str(cmap(1)[0:3])], 
              [1, 'rgb' + str(cmap(2)[0:3])]]

,然后提供一个数字数组,其尺寸与绘制的值相同。

colors_saddle = np.zeros(shape=saddle.shape)    

所有值均设置为0,因此将映射到colorscale中的第一种颜色。下一种颜色也一样。

此外,您需要手动设置cmaxcmin

完整代码

import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objs as go
import plotly.offline as off


off.init_notebook_mode()

make_int = np.vectorize(int)
cmap = plt.get_cmap("tab10")

saddle = np.array([[x**2-y**2 for x in np.arange(-10,11)] for y in np.arange(-10,11)])
paraboloid = np.array([[x**2 + y**2-100 for x in np.arange(-10,11)] for y in np.arange(-10,11)])

colors_saddle = np.zeros(shape=saddle.shape)    
colors_paraboloid = np.ones(shape=paraboloid.shape)    

colorscale = [[0, 'rgb' + str(cmap(1)[0:3])], 
              [1, 'rgb' + str(cmap(2)[0:3])]]

trace_a = go.Surface(z=saddle, 
                     surfacecolor=colors_saddle, 
                     opacity=.7, 
                     name="Trace A",
                     cmin=0,
                     cmax=1,
                     colorscale=colorscale)
trace_b = go.Surface(z=paraboloid, 
                     surfacecolor=colors_paraboloid, 
                     opacity=.7, 
                     name="Trace B", 
                     cmin=0,
                     cmax=1,
                     showscale=False,
                     colorscale=colorscale)

data = [trace_a, trace_b]
off.iplot(data)

enter image description here

答案 1 :(得分:0)

您可以将所有表面合并为一个,并在每个表面的色标范围内进行设置 它还可以解决重叠问题,因此您会清楚地看到曲面的线相交like here

    import numpy as np
    import plotly.graph_objs as go

    # normalize values to range [start,end] for getting color from cmap
    def norm_v_in_range(v,start,end):
        v_min = v.min()
        v_max = v.max()
        range_length = (end - start)

        if v_min-v_max == 0 :
            v.fill(range_length/5 + start)
            return v

        return (v-v_min)/(v_max-v_min)*range_length + start

    def combine_all_surfaces_in_one(X,Y,*Z) :

        # prepare colors and ranges for diffrent surfaces
        colors = [  'rgb(180, 110,  20)', 'rgb( 20, 180, 110)', 'rgb(110, 20, 180)',
                    'rgb(180, 180,  20)', 'rgb( 20, 180, 180)', 'rgb(180, 20, 180)',
                    'rgb(180,  20,  20)', 'rgb( 20, 180,  20)', 'rgb( 20, 20, 180)',
                    'rgb(180, 110,  20)', 'rgb( 20, 180, 110)', 'rgb(110, 20, 180)',
                    'rgb(255, 127, 127)', 'rgb(127, 255, 127)']

        N = len(Z)
        points = np.linspace(0, 1, N + 1)
        custom_colorscale = []
        ranges = []

        for i in range(1,N+1) :
            ranges.append([points[i-1],points[i]-0.05])
            custom_colorscale.append([points[i-1], colors[i]])
            custom_colorscale.append([points[i]-0.05,'rgb(255, 250, 220)'])
        custom_colorscale.append([1, 'rgb(220, 250, 220)'])


        # transparent connection between grahps: np.nan in z prevent ploting points
        transparen_link = np.empty_like(X[0], dtype=object)
        transparen_link.fill(np.nan)

        # include first graph
        combined_X = X
        combined_Y = Y
        combined_Z = Z[0]

        # prepare collor matrix for first graph (Z[0])
        start = ranges[0][0]
        end = ranges[0][1]

        custom_surfacecolor = norm_v_in_range(Z[0],start,end)

        # second aray combined with first in backward direction, so connection would on one side of graphs, not intersect them 
        direction = -1

        range_index = 1

        for next_Z in Z[1:] :

            combined_X = np.vstack([combined_X, combined_X[-1], X[::direction][0], X[::direction][0], X[::direction]])
            combined_Y = np.vstack([combined_Y, combined_Y[-1], Y[::direction][0], Y[::direction][0], Y[::direction]])
            combined_Z = np.vstack([combined_Z, combined_Z[-1], transparen_link, next_Z[::direction][0], next_Z[::direction]])

            # prepare collors for next Z_ 
            start = ranges[range_index][0]
            end = ranges[range_index][1]
            next_surfacecolor = norm_v_in_range(next_Z,start,end)
            custom_surfacecolor = np.vstack([custom_surfacecolor,custom_surfacecolor[-1], transparen_link, next_surfacecolor[::direction][0], next_surfacecolor[::direction]])

            # change direction
            direction *= -1

            range_index += 1

        return combined_X, combined_Y, combined_Z, custom_surfacecolor, custom_colorscale



    X = np.arange(-1.2, 1.06, 0.1)
    Y = np.arange(0.2, 1.06, 0.1)
    X, Y = np.meshgrid(X, Y)


    Z1 = 2*np.sin(np.sqrt(20*X**2+20*Y**2))
    Z2 = 2*np.cos(np.sqrt(20*X**2+20*Y**2))
    Z3 = X*2+0.5
    Z4 = Y*0+1.0
    Z5 = Y*0-1.0
    Z6 = Y*0+0.0
    x,y,z,custom_surfacecolor,custom_colorscale = combine_all_surfaces_in_one(X,Y,Z1,Z2,Z3,Z4,Z5)

    # opacity =0.9 - many overlaped areas, better witot it
    fig = go.Figure(data=[go.Surface(x=x, y=y, z=z, 
                                     surfacecolor=custom_surfacecolor, cmin=0, cmax = 1,
                                     colorscale=custom_colorscale,showscale=False,
                                     )] )

    fig.show()