代码之家  ›  专栏  ›  技术社区  ›  Basj

使用Plotly,两个热图相互叠加,不透明度高。JS

  •  0
  • Basj  · 技术社区  · 2 年前

    我正在尝试移植 this answer 100%的情节。JS解决方案。 TL;DR:如何使用Plotly在每个热图的顶部使用不透明度滑块显示两个热图。JS(没有Python)?

    解决方案的开始,但如何添加第二个跟踪?

    const z = [];
    for (let i = 0; i < 500; i++)
      z.push(Array.from({ length: 600 }, () => Math.floor(Math.random() * 100)));
    const data = [{ z: z, colorscale: "YlGnBu", type: "heatmap" }];
    const steps = [];
    for (let i = 0; i <= 100; i++)
      steps.push({ label: i + "%", execute: true, method: "restyle", args: [{ opacity: i / 100 }] });
    const layout = { sliders: [{ name: "slider", steps: steps, active: 100 }] };
    Plotly.newPlot("graph", data, layout);
    <script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
    <div id="graph"></div>

    参考:原始Python解决方案:

    from PIL import Image
    import plotly.graph_objects as go
    import numpy as np
    import scipy.misc
    
    imgA = scipy.misc.face()
    imgB = Image.fromarray(np.random.random(imgA.shape[:2])*255).convert('RGB')
    
    fig = go.Figure([
        go.Image(name='raccoon', z=imgA, opacity=1), # trace 0
        go.Image(name='noise', z=imgB, opacity=0.5)  # trace 1
    ])
    
    slider = {
        'active': 50,
        'currentvalue': {'prefix': 'Noise: '},
        'steps': [{
            'value': step/100,
            'label': f'{step}%',
            'visible': True,
            'execute': True,
            'method': 'restyle',
            'args': [{'opacity': step/100}, [1]]     # apply to trace [1] only
        } for step in range(101)]
    }
    
    fig.update_layout(sliders=[slider])
    fig.show(renderer='browser')
    
    0 回复  |  直到 2 年前
        1
  •  2
  •   EricLavault    2 年前

    第二条轨迹进入 data 阵列也是。需要注意的是,索引很重要:索引1处的跟踪绘制在索引0处的跟踪之上,以此类推。

    对于滑块配置,它应该与python中的相同:每一步更改都会触发具有相同参数的相同“restyle”方法,即。 Plotly.restyle(graphDiv, ...args) 也就是说,与 args 使得方法调用与 signature :

    Plotly.restyle(graphDiv, update [, traceIndices])
    

    现在,最重要的是 哪条痕迹 (traceIndices)滑块应该指向哪个目标,即 指数 或者 名称 对于明确命名的跟踪(默认为 全部的 如果我没记错的话),但在这里,Python和Javascript之间没有变化。

    这是一个完整的示例(在 codepen.io ) :

    // Random z data
    const w = {length: 600};
    const h = {length: 400};
    const z0 = Array.from(h, () => Array.from(w, () => Math.floor(Math.random() * 100)));
    const z1 = Array.from(h, () => Array.from(w, () => Math.floor(Math.random() * 100)));
    
    // Initial opacity for the trace 'above' 
    const op_init = 0.5;
    
    const data = [
      // Nb. Trace 1 drawn on top of trace 0
      {type: 'heatmap', z: z0, colorscale: 'Greys'},                    // trace 0
      {type: 'heatmap', z: z1, colorscale: 'Cividis', opacity: op_init} // trace 1
    ];
    
    // Steps for the opacity slider
    const steps = [];
    const n_steps = 100; // number of steps above step 0
    for (let i = 0; i <= n_steps; i++) {
      steps.push({
        label: i + '%',
        execute: true,
        method: 'restyle',
        args: [{
          opacity: i/n_steps
        }, [1]] // <- Nb. this applies only to trace 1
      });
    }
    
    const layout = {
      width: 600,
      sliders: [{
        steps: steps,
        active: Math.round(op_init * n_steps), // slider default matches op_init 
        pad: {t: 30},
        currentvalue: {prefix: 'opacity: '}
      }]
    };
    
    Plotly.newPlot('plot', data, layout);
    

    图像与热图

    热图仅适用于单通道数据(根据给定的色阶进行单个值到颜色的映射)。

    使用rgb(或rgba、rgba256、hsl、hsla)时,必须使用 image 类型。区别在于 z 必须是 二维数组,其中每个元素是表示颜色的3或4个数字的数组( colormodel 应相应地设置)。

    例如,将由噪声组成的rgb图像设置为背景层:

    const z0 = Array.from(h, () => Array.from(w, () => ['r', 'g', 'b'].map(() => Math.floor(Math.random() * 255)) ));
    
    // ...
    
    const data = [
      {type: 'image', z: z0, colormodel: 'rgb'},                        // trace 0
      {type: 'heatmap', z: z1, colorscale: 'Cividis', opacity: op_init} // trace 1
    ];
    

    这里有第二个例子,我们有一个rgb[a]图像(DOM对象 img )其像素数据表示为一维Uint8Array( uint8Arr ),需要转换为2d:

    const z0 = [];
    const nChannels = uint8Arr.length / img.width / img.height;
    const chunkSize = uint8Arr.length / img.height;
    const z0_model = nChannels === 4 ? 'rgba' : 'rgb';
    
    for (let i = 0; i < uint8Arr.length; i += chunkSize) {
      const chunk = uint8Arr.slice(i, i + chunkSize);
      const row = [];
      for (let j = 0; j < chunk.length; j += nChannels)
        row.push(chunk.slice(j, j + nChannels));
      z0.push(row);
    }
    
    // ...
    
    const data = [
      {type: 'image', z: z0, colormodel: z0_model},                     // trace 0
      {type: 'heatmap', z: z1, colorscale: 'Cividis', opacity: op_init} // trace 1
    ];
    

    Nb。绘制图像时,偏航轴会自动反转(除非另有说明,否则会将图像倒置显示)。这会影响热图y标签的方向,因为它们在同一个图上,但只有标签而不是数据。

    以下是布局设置,确保两条迹线共享相同的纵横比,并且图像方向正确:

    const layout = {
      // ...
      xaxis: {anchor: 'y', scaleanchor: 'y', constrain: 'domain'},
      yaxis: {anchor: 'x', autorange: 'reversed', constrain: 'domain'},
    };