【问题标题】:Cannot make violin plot with different length sub-lists不能用不同长度的子列表制作小提琴图
【发布时间】:2021-11-07 03:32:43
【问题描述】:

我正在尝试使用 python 3.8.10 和 matplotlib 3.3.4 制作小提琴图

import matplotlib.pyplot as plt
import numpy as np
data = []
data.append([65,46,64,59,42,44])
data.append([20,40,44,43,32,20,27,31,20,40,24,26,37,30,29,25,31,65,50,38,41,19,31,38,48,44,51,55,52,25,40,28,50,37,44,21,43,28,36,67,55,58,23,36,28,21,21,39,26,65,18,27,50,70,29,37,25,49,33,31,20,33])

f = plt.figure()
plt.rc('xtick', labelsize = 6)
violin_plot = plt.violinplot(data, showmeans=False, showmedians=False)
for pc in violin_plot["bodies"]:
    pc.set_edgecolor('black')
def adjacent_values(vals, q1, q3):
    upper_adjacent_value = q3 + (q3 - q1) * 1.5
    upper_adjacent_value = np.clip(upper_adjacent_value, q3, vals[-1])
    lower_adjacent_value = q1 - (q3 - q1) * 1.5
    lower_adjacent_value = np.clip(lower_adjacent_value, vals[0], q1)
    return lower_adjacent_value, upper_adjacent_value
quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1)
whiskers = np.array([
    adjacent_values(sorted_array, q1, q3)
    for sorted_array, q1, q3 in zip(data, quartile1, quartile3)])
whiskers_min, whiskers_max = whiskers[:, 0], whiskers[:, 1]
inds = np.arange(1, len(medians) + 1)
plt.scatter(inds, medians, marker="o", color="white", s=30, zorder=3)
plt.vlines(inds, quartile1, quartile3, color="k", linestyle="-", lw=5)
plt.vlines(inds, whiskers_min, whiskers_max, color="k", linestyle="-", lw=1)
plt.savefig('violin_age_by_race.svg', bbox_inches='tight', pad_inches = 0.05)

我从https://matplotlib.org/devdocs/gallery/statistics/customized_violin.html得到的

但上面的代码会产生错误(行号与上面的代码不同,因为我将文件剪掉了,以便为 StackOverflow 制作一个最小的工作示例)

/usr/local/lib/python3.8/dist-packages/numpy/core/_asarray.py:171: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  return array(a, dtype, copy=False, order=order, subok=True)
Traceback (most recent call last):
  File "/tmp/E2Woujgas1.py", line 35, in <module>
    quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1)
  File "<__array_function__ internals>", line 5, in percentile
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 3818, in percentile
    return _quantile_unchecked(
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 3937, in _quantile_unchecked
    r, k = _ureduce(a, func=_quantile_ureduce_func, q=q, axis=axis, out=out,
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 3495, in _ureduce
    axis = _nx.normalize_axis_tuple(axis, nd)
  File "/usr/local/lib/python3.8/dist-packages/numpy/core/numeric.py", line 1391, in normalize_axis_tuple
    axis = tuple([normalize_axis_index(ax, ndim, argname) for ax in axis])
  File "/usr/local/lib/python3.8/dist-packages/numpy/core/numeric.py", line 1391, in <listcomp>
    axis = tuple([normalize_axis_index(ax, ndim, argname) for ax in axis])
numpy.AxisError: axis 1 is out of bounds for array of dimension 1

错误在quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1),所以我按照错误消息的提示进行操作,然后更改为

quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1, dtype = object)

然后我得到一个错误:

TypeError: _percentile_dispatcher() got an unexpected keyword argument 'dtype'

据我所知,由于子列表的长度不同,因此引发了错误,这是不可避免的。该示例包含所有包含 100 个元素的子列表。

我也尝试过创建一个 np 数组:

np_data = np.array(data, dtype = object)
quartile1, medians, quartile3 = np.percentile(np_data, [25, 50, 75], axis=1, dtype = object)

但上述更改给出了关于dtype的相同错误

如何更改此代码以使 numpy 不会抱怨不同长度的子列表?

【问题讨论】:

  • 为什么在调用percentile的时候还继续使用dtype=object?之前的电话已经告诉你这是错误的!

标签: python python-3.x numpy matplotlib


【解决方案1】:

错误不在violinplot!效果很好。

percentile 函数中。

In [23]: np.percentile(data, [25, 50, 75], axis=1)
/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py:3539: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  a = np.asanyarray(a)
Traceback (most recent call last):
  File "<ipython-input-23-32c56e5bfa18>", line 1, in <module>
    np.percentile(data, [25, 50, 75], axis=1)
  File "<__array_function__ internals>", line 5, in percentile
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 3867, in percentile
    return _quantile_unchecked(
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 3986, in _quantile_unchecked
    r, k = _ureduce(a, func=_quantile_ureduce_func, q=q, axis=axis, out=out,
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 3544, in _ureduce
    axis = _nx.normalize_axis_tuple(axis, nd)
  File "/usr/local/lib/python3.8/dist-packages/numpy/core/numeric.py", line 1385, in normalize_axis_tuple
    axis = tuple([normalize_axis_index(ax, ndim, argname) for ax in axis])
  File "/usr/local/lib/python3.8/dist-packages/numpy/core/numeric.py", line 1385, in <listcomp>
    axis = tuple([normalize_axis_index(ax, ndim, argname) for ax in axis])
AxisError: axis 1 is out of bounds for array of dimension 1

data 是一个列表。 percentile 需要一个数组,所以:

In [25]: type(data)
Out[25]: list
In [26]: np.array(data)
<ipython-input-26-d04fee483c4a>:1: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  np.array(data)
Out[26]: 
array([list([65, 46, 64, 59, 42, 44]),
       list([20, 40, 44, 43, 32, 20, 27, 31, 20, 40, 24, 26, 37, 30, 29, 25, 31, 65, 50, 38, 41, 19, 31, 38, 48, 44, 51, 55, 52, 25, 40, 28, 50, 37, 44, 21, 43, 28, 36, 67, 55, 58, 23, 36, 28, 21, 21, 39, 26, 65, 18, 27, 50, 70, 29, 37, 25, 49, 33, 31, 20, 33])],
      dtype=object)

所以你可以在没有警告的情况下从数据中创建一个数组:

In [30]: np_data=np.array(data, dtype=object)
In [31]: np_data
Out[31]: 
array([list([65, 46, 64, 59, 42, 44]),
       list([20, 40, 44, 43, 32, 20, 27, 31, 20, 40, 24, 26, 37, 30, 29, 25, 31, 65, 50, 38, 41, 19, 31, 38, 48, 44, 51, 55, 52, 25, 40, 28, 50, 37, 44, 21, 43, 28, 36, 67, 55, 58, 23, 36, 28, 21, 21, 39, 26, 65, 18, 27, 50, 70, 29, 37, 25, 49, 33, 31, 20, 33])],
      dtype=object)

但请注意,它是 1d,一个列表数组。指定axis=1 是错误的,因为数组没有这样的轴。

不过,在该列表数组上调用 percentile 仍然不起作用:

In [32]: np.percentile(np_data, [25, 50, 75])
Traceback (most recent call last):
  File "<ipython-input-32-31dd33e64b74>", line 1, in <module>
    np.percentile(np_data, [25, 50, 75])
  File "<__array_function__ internals>", line 5, in percentile
 ....
 packages/numpy/lib/function_base.py", line 4009, in _lerp
    diff_b_a = subtract(b, a)
TypeError: unsupported operand type(s) for -: 'list' and 'list'

您可以分别在 2 个列表上执行 percentile

In [34]: np.percentile(np_data[0], [25, 50, 75])
Out[34]: array([44.5 , 52.5 , 62.75])
In [35]: np.percentile(np_data[1], [25, 50, 75])
Out[35]: array([26.25, 34.5 , 44.  ])
In [36]: np.percentile(data[1], [25, 50, 75])
Out[36]: array([26.25, 34.5 , 44.  ])

【讨论】:

    猜你喜欢
    • 2018-12-19
    • 2016-11-08
    • 2017-01-04
    • 2021-01-23
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    相关资源
    最近更新 更多