【问题标题】:How to make Matplotlib scatterplots transparent as a group?如何使 Matplotlib 散点图作为一个组透明?
【发布时间】:2015-07-18 10:58:25
【问题描述】:

我正在使用 Matplotlib(python 3.4.0,matplotlib 1.4.3,在 Linux Mint 17 上运行)制作一些散点图。单独为每个点设置 alpha 透明度很容易;有没有办法将它们设置为一个组,以便同一组中的两个重叠点不会改变颜色?

示例代码:

import matplotlib.pyplot as plt
import numpy as np

def points(n=100):
    x = np.random.uniform(size=n)
    y = np.random.uniform(size=n)
    return x, y
x1, y1 = points()
x2, y2 = points()
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
ax.scatter(x1, y1, s=100, color="blue", alpha=0.5)
ax.scatter(x2, y2, s=100, color="red", alpha=0.5)
fig.savefig("test_scatter.png")

这个输出的结果:

但我想要更像这样的东西:

我可以通过另存为 SVG 并在 Inkscape 中手动分组,然后设置透明度来解决问题,但我真的更喜欢我可以编码的东西。有什么建议吗?

【问题讨论】:

  • 可能不会,因为这样做与散点图通常试图显示的内容相反。

标签: python matplotlib transparency scatter-plot


【解决方案1】:

有趣的问题,我认为任何使用透明度都会导致您想要避免的堆叠效果。你可以手动设置一个透明类型的颜色来更接近你想要的结果,

import matplotlib.pyplot as plt
import numpy as np

def points(n=100):
    x = np.random.uniform(size=n)
    y = np.random.uniform(size=n)
    return x, y
x1, y1 = points()
x2, y2 = points()
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
alpha = 0.5
ax.scatter(x1, y1, s=100, lw = 0, color=[1., alpha, alpha])
ax.scatter(x2, y2, s=100, lw = 0, color=[alpha, alpha, 1.])
plt.show()

这种方式不包括不同颜色之间的重叠,但是你得到,

【讨论】:

  • 奖励:它不需要额外的库!
  • 你不能通过蓝色看到红色,反之亦然。
  • 我觉得颜色不对,应该是RGBA元组,而不是RGB所以:[0,0,1,0.5]应该是透明的蓝色
  • @Kev1n91,将 alpha 设置为除 1 以外的任何值(没有 alpha 的 RGB 值的默认值)意味着您可以看到 OP 指定他们不想要的重叠:“从同组不变色”
【解决方案2】:

是的,有趣的问题。您可以使用Shapely 获取此散点图。这是代码:

import matplotlib.pyplot as plt
import matplotlib.patches as ptc
import numpy as np
from shapely.geometry import Point
from shapely.ops import cascaded_union

n = 100
size = 0.02
alpha = 0.5

def points():
    x = np.random.uniform(size=n)
    y = np.random.uniform(size=n)
    return x, y

x1, y1 = points()
x2, y2 = points()
polygons1 = [Point(x1[i], y1[i]).buffer(size) for i in range(n)]
polygons2 = [Point(x2[i], y2[i]).buffer(size) for i in range(n)]
polygons1 = cascaded_union(polygons1)
polygons2 = cascaded_union(polygons2)

fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
for polygon1 in polygons1:
    polygon1 = ptc.Polygon(np.array(polygon1.exterior), facecolor="red", lw=0, alpha=alpha)
    ax.add_patch(polygon1)
for polygon2 in polygons2:
    polygon2 = ptc.Polygon(np.array(polygon2.exterior), facecolor="blue", lw=0, alpha=alpha)
    ax.add_patch(polygon2)
ax.axis([-0.2, 1.2, -0.2, 1.2])

fig.savefig("test_scatter.png")

结果是:

【讨论】:

  • 非常酷的 shapely 用法,我从没想过会这样!你认为descartes 包会简化绘图吗?
  • 谢谢!是的,可以使用descartes 包。在cascaded_union 之后:使用descartes.PolygonPatch 创建补丁,使用matplotlib.collections.PathCollection 并将add_patch 替换为add_collection。这将用更少的行来完成这项工作。
【解决方案3】:

这是一个可怕的、可怕的 hack,但它确实有效。

您会看到,虽然 Matplotlib 将数据点绘制为可以重叠的单独对象,但它会将它们之间的线绘制为单个对象 - 即使该线被数据中的 NaN 分成几部分。

考虑到这一点,您可以这样做:

import numpy as np
from matplotlib import pyplot as plt

plt.rcParams['lines.solid_capstyle'] = 'round'

def expand(x, y, gap=1e-4):
    add = np.tile([0, gap, np.nan], len(x))
    x1 = np.repeat(x, 3) + add
    y1 = np.repeat(y, 3) + add
    return x1, y1

x1, y1 = points()
x2, y2 = points()
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
ax.plot(*expand(x1, y1), lw=20, color="blue", alpha=0.5)
ax.plot(*expand(x2, y2), lw=20, color="red", alpha=0.5)

fig.savefig("test_scatter.png")
plt.show()

每种颜色都会与另一种颜色重叠,但不会与自身重叠。

需要注意的是,您必须注意用于制作每个圆的两点之间的间距。如果它们相距很远,那么分离将在您的绘图上可见,但如果它们靠得太近,matplotlib 根本不会绘制线。这意味着需要根据数据范围来选择分隔,如果您打算制作交互式绘图,那么如果您缩小太多,所有数据点都有可能突然消失,如果您放大,则可能会拉伸太多了。

如您所见,我发现 1e-5 可以很好地分离范围为 [0,1] 的数据。

【讨论】:

  • 这正是我所需要的!对于对数图,添加[0, gap, nan] 不会同时在多个数量级上起作用,因此我乘以[1, 1+gap, nan]
【解决方案4】:

只需将edgecolors='none' 的参数传递给plt.scatter()

【讨论】:

    【解决方案5】:

    如果您要绘制的点不止几个,这里有一个技巧。我必须绘制 >500000 个点,而 shapely 解决方案不能很好地扩展。我还想绘制一个不同于圆形的形状。我选择使用alpha=1 分别绘制每一层,然后使用np.frombuffer 读取生成的图像(如here 所述),然后将alpha 添加到整个图像并使用plt.imshow 绘制叠加层。请注意,此解决方案丧失了对原始 fig 对象和属性的访问权,因此应在绘制之前对图形进行任何其他修改。

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    from matplotlib.figure import Figure
    
    def arr_from_fig(fig):
        canvas = FigureCanvas(fig)
        canvas.draw()
        img = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
        img = img.reshape(fig.canvas.get_width_height()[::-1] + (3,))
        return img
    
    def points(n=100):
        x = np.random.uniform(size=n)
        y = np.random.uniform(size=n)
        return x, y
    
    x1, y1 = points()
    x2, y2 = points()
    imgs = list()
    figsize = (4, 4)
    dpi = 200
    
    for x, y, c in zip([x1, x2], [y1, y2], ['blue', 'red']):
        fig = plt.figure(figsize=figsize, dpi=dpi, tight_layout={'pad':0})
        ax = fig.add_subplot(111)
        ax.scatter(x, y, s=100, color=c, alpha=1)
        ax.axis([-0.2, 1.2, -0.2, 1.2])
        ax.axis('off')
        imgs.append(arr_from_fig(fig))
        plt.close()
    
    
    fig = plt.figure(figsize=figsize)
    alpha = 0.5
    
    alpha_scaled = 255*alpha
    for img in imgs:
        img_alpha = np.where((img == 255).all(-1), 0, alpha_scaled).reshape([*img.shape[:2], 1])
        img_show = np.concatenate([img, img_alpha], axis=-1).astype(int)
        plt.imshow(img_show, origin='lower')
    
    ticklabels = ['{:03.1f}'.format(i) for i in np.linspace(-0.2, 1.2, 8, dtype=np.float16)]
    plt.xticks(ticks=np.linspace(0, dpi*figsize[0], 8), labels=ticklabels)
    plt.yticks(ticks=np.linspace(0, dpi*figsize[1], 8), labels=ticklabels);
    plt.title('Test scatter');
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      • 2021-06-05
      • 2021-01-31
      • 1970-01-01
      • 2016-05-11
      • 1970-01-01
      • 2022-08-03
      相关资源
      最近更新 更多