如何在Matplotlib中创建“点图”?(不是散点图)

15

我想创建一种统计学中称为“点图”的图表,其中图表中的点数等于观察数。以下是来自mathisfun.com的示例:

example dot plot

在此示例中,X轴上0值上方有六个点,表示值为零的六个观察结果。

看起来,“点图”可以有几种变化形式。在查找如何使用Matplotlib创建此类图时,我只发现了我所知道的表示X和Y值之间关系的数据点的散点图。

我能否使用Matplotlib创建我要创建的这种类型的图?


这只是一个直方图。 - miradulo
4
for x, y in zip(xs, ys): plt.plot([x]*y, list(range(y)), 'ro') plt.show() 翻译为:对于xs和ys中的每个x和y,使用红色圆形绘制[x]*y与y的范围列表之间的线条,然后显示图形。 - eyllanesc
6个回答

20

假设你有一些数据,可以生成以下直方图:

import numpy as np; np.random.seed(13)
import matplotlib.pyplot as plt

data = np.random.randint(0,12,size=72)

plt.hist(data, bins=np.arange(13)-0.5, ec="k")

plt.show()

enter image description here

您可以通过计算直方图并绘制散点图来创建您的点图,点的颜色为白色,如果它们超过直方图给出的数字。

import numpy as np; np.random.seed(13)
import matplotlib.pyplot as plt

data = np.random.randint(0,12,size=72)
bins = np.arange(13)-0.5

hist, edges = np.histogram(data, bins=bins)

y = np.arange(1,hist.max()+1)
x = np.arange(12)
X,Y = np.meshgrid(x,y)

plt.scatter(X,Y, c=Y<=hist, cmap="Greys")

plt.show()

或者您可以将不需要的点设置为nan

Y = Y.astype(np.float)
Y[Y>hist] = np.nan

plt.scatter(X,Y)

输入图片描述


我在另一个数据集上尝试了这种方法,但出现了“ValueError: operands could not be broadcast together with shapes (25,350) (15,)”的错误。这与“'Y<=hist'”有关。您是否知道这是否是常见问题,并且是否有简单的解决方案?谢谢。 - Glenn G.

5

这个答案是建立在eyllanesc在评论中发布的代码上,我认为它足够优雅,值得提供一个说明性的例子。我提供了两个版本:一个简单版本,在该版本中手动设置了格式化参数;第二个版本中,一些格式化参数是根据数据自动设置的。

手动设置的简单版本

import numpy as np                 # v 1.19.2
import matplotlib.pyplot as plt    # v 3.3.2

# Create random data
rng = np.random.default_rng(123) # random number generator
data = rng.integers(0, 13, size=40)
values, counts = np.unique(data, return_counts=True)

# Draw dot plot with appropriate figure size, marker size and y-axis limits
fig, ax = plt.subplots(figsize=(6, 2.25))
for value, count in zip(values, counts):
    ax.plot([value]*count, list(range(count)), 'co', ms=10, linestyle='')
for spine in ['top', 'right', 'left']:
    ax.spines[spine].set_visible(False)
ax.yaxis.set_visible(False)
ax.set_ylim(-1, max(counts))
ax.set_xticks(range(min(values), max(values)+1))
ax.tick_params(axis='x', length=0, pad=8, labelsize=12)

plt.show()

点图手动版


带自动格式化的高级版本

如果您经常使用这种图形,添加一些自动格式化参数以获得适当的图形尺寸和标记大小将非常有用。在下面的例子中,参数被定义为适用于此类型图形通常有用的数据(整数数据,范围为几十个单位,不超过几百个数据点)。

# Create random data
rng = np.random.default_rng(1) # random number generator
data = rng.integers(0, 21, size=100)
values, counts = np.unique(data, return_counts=True)

# Set formatting parameters based on data
data_range = max(values)-min(values)
width = data_range/2 if data_range<30 else 15
height = max(counts)/3 if data_range<50 else max(counts)/4
marker_size = 10 if data_range<50 else np.ceil(30/(data_range//10))

# Create dot plot with appropriate format
fig, ax = plt.subplots(figsize=(width, height))
for value, count in zip(values, counts):
    ax.plot([value]*count, list(range(count)), marker='o', color='tab:blue',
            ms=marker_size, linestyle='')
for spine in ['top', 'right', 'left']:
    ax.spines[spine].set_visible(False)
ax.yaxis.set_visible(False)
ax.set_ylim(-1, max(counts))
ax.set_xticks(range(min(values), max(values)+1))
ax.tick_params(axis='x', length=0, pad=10)

plt.show()

dotplot_automated


1
将您的数据集传递给此函数:

def dot_diagram(dataset):
    values, counts = np.unique(dataset, return_counts=True)
    data_range = max(values)-min(values)
    width = data_range/2 if data_range<30 else 15
    height = max(counts)/3 if data_range<50 else max(counts)/4
    marker_size = 10 if data_range<50 else np.ceil(30/(data_range//10))
    fig, ax = plt.subplots(figsize=(width, height))
    for value, count in zip(values, counts):
        ax.plot([value]*count, list(range(count)), marker='o', color='tab:blue',
                ms=marker_size, linestyle='')
    for spine in ['top', 'right', 'left']:
        ax.spines[spine].set_visible(False)
    ax.yaxis.set_visible(False)
    ax.set_ylim(-1, max(counts))
    ax.set_xticks(range(min(values), max(values)+1))
    ax.tick_params(axis='x', length=0, pad=10)

1

假设这是我的数据:

data  = [5,8,3,7,1,5,3,2,3,3,8,5]

为了画出“点图”,我需要数据(x轴)和频率(y轴)。
pos = [] 
keys = {} # this dict will help to keep track ...

# this loop will give us a list of frequencies to each number
for num in data: 
   if num not in keys:
      keys[num] = 1
      pos.append(1)
   else:
      keys[num] += 1
      apos.append(keys[num])


print(pos)
[1, 1, 1, 1, 1, 2, 2, 1, 3, 4, 2, 3]

plt.scatter(data, pos)
plt.show()

enter image description here


0

最近,我也想到了类似的东西。我已经为我的情况做了以下事情。

希望这对你有所帮助。

好的,我们首先生成频率表,然后从中生成点来制作散点图。就是这样!非常简单。

例如,在您的情况下,我们有0分钟,6个人。这个频率可以转换成

[(0,1),(0,2),(0,3),(0,4),(0,5),(0,6)]

然后,这些点必须使用pyplot.scatter简单地绘制出来。
import numpy as np
import matplotlib.pyplot as plt

def generate_points_for_dotplot(arr):
    freq = np.unique(arr,return_counts=True)
    ls = []
    for (value, count) in zip(freq[0],freq[1]):
        ls += [(value,num) for num in range(count)]
    x = [x for (x,y) in ls]
    y = [y for (x,y) in ls]
    return np.array([x,y])

当然,这个函数返回一个包含两个数组的数组,一个用于x坐标,另一个用于y坐标(因为这是pyplot需要的点的格式!)。现在,我们有了生成所需点的函数,让我们来绘制它。
arr = np.random.randint(1,21,size=100)
x,y = generate_points_for_dotplot(arr)

# Plotting
fig,ax = plt.subplots(figsize = (max(x)/3,3)) # feel free to use Patricks answer to make it more dynamic
ax.scatter(x,y,s=100,facecolors='none',edgecolors='black')
ax.set_xticks(np.unique(x))
ax.yaxis.set_visible(False)
# removing the spines
for spine in ['top', 'right', 'left']:
    ax.spines[spine].set_visible(False)
plt.show()

输出:

dotplot

可能,如果x轴刻度过多,您可以将它们旋转。但是,对于更多的值,这也变得笨拙。


0

简单易行的方法

使用ArviZ

如果您可以使用其他包,我建议使用ArviZ,它在底层使用Matplotlib,并提供了适当的点图。

ArviZ点图文档

示例代码

import matplotlib.pyplot as plt
import numpy as np
import arviz as az


# Data is hardcoded here while a more sophisticated method can be used
data = np.array([0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 4, 4, 5, 5, 5, 5, 5, 8, 8, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12])

# The main plotting function call
ax = az.plot_dot(data, dotcolor="C1", dotsize=0.8)

# Setting title
ax.set_title("Minutes to Eat Breakfast")

plt.show()

输出

Desired dot plot


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接