如何在绘制箱线图时处理NaN值

18

我正在使用matplotlib来绘制一个箱线图,但是有一些缺失值(NaN)。然后我发现它不会显示在具有NaN值的列中的箱线图。你知道如何解决这个问题吗?以下是代码。

import numpy as np
import matplotlib.pyplot as plt

#==============================================================================
# open data
#==============================================================================
filename='C:\\Users\\liren\\OneDrive\\Data\\DATA in the first field-final\\ks.csv'

AllData=np.genfromtxt(filename,delimiter=";",skip_header=0,dtype='str')

TreatmentCode = AllData[1:,0]
RepCode = AllData[1:,1]
KsData= AllData[1:,2:].astype('float')
DepthHeader = AllData[0,2:].astype('float')
TreatmentUnique = np.unique(TreatmentCode)[[3,1,4,2,8,6,9,7,0,5,10],]
nT = TreatmentUnique.size#nT=number of treatments
#nD=number of deepth;nR=numbers of replications;nT=number of treatments;iT=iterms of treatments
nD = 5
nR = 6
KsData_3D = np.zeros((nT,nD,nR)) 

for iT in range(nT):
    Treatment = TreatmentUnique[iT]

    TreatmentFilter = TreatmentCode == Treatment

    KsData_Filtered = KsData[TreatmentFilter,:]
    
    KsData_3D[iT,:,:] = KsData_Filtered.transpose()iD = 4
                      
fig=plt.figure()
ax = fig.add_subplot(111)
plt.boxplot(KsData_3D[:,iD,:].transpose())
ax.set_xticks(range(1,nT+1))
ax.set_xticklabels(TreatmentUnique)
ax.set_title(DepthHeader[iD])

这是最终的图形,其中一些处理在框中没有显示。

在此输入图片描述

1个回答

34

您可以先从数据中删除 NaN,然后绘制过滤后的数据。

为此,您可以使用 np.isnan(data) 找到 NaN,然后使用 ~: 按位反转运算符 对布尔数组进行按位反转。使用该数组索引数据数组,并过滤掉 NaN

filtered_data = data[~np.isnan(data)]

在一个完整的示例中(改编自这里

已测试使用的版本:python 3.10matplotlib 3.5.1seaborn 0.11.2numpy 1.21.5pandas 1.4.2

针对一维数据:

import matplotlib.pyplot as plt
import numpy as np

# fake up some data
np.random.seed(2022)  # so the same data is created each time
spread = np.random.rand(50) * 100
center = np.ones(25) * 50
flier_high = np.random.rand(10) * 100 + 100
flier_low = np.random.rand(10) * -100
data = np.concatenate((spread, center, flier_high, flier_low), 0)

# Add a NaN
data[40] = np.NaN

# Filter data using np.isnan
filtered_data = data[~np.isnan(data)]

# basic plot
plt.boxplot(filtered_data)

plt.show()

enter image description here

对于二维数据:

对于二维数据,您不能简单地使用上面的掩码,因为数据数组的每一列长度都不同。相反,我们可以创建一个列表,其中列表中的每个项目都是数据数组每列的过滤数据。

列表推导式可以在一行中完成此操作:[d[m] for d, m in zip(data.T, mask.T)]

import matplotlib.pyplot as plt
import numpy as np

# fake up some data
np.random.seed(2022)  # so the same data is created each time
spread = np.random.rand(50) * 100
center = np.ones(25) * 50
flier_high = np.random.rand(10) * 100 + 100
flier_low = np.random.rand(10) * -100
data = np.concatenate((spread, center, flier_high, flier_low), 0)

data = np.column_stack((data, data * 2., data + 20.))

# Add a NaN
data[30, 0] = np.NaN
data[20, 1] = np.NaN

# Filter data using np.isnan
mask = ~np.isnan(data)
filtered_data = [d[m] for d, m in zip(data.T, mask.T)]

# basic plot
plt.boxplot(filtered_data)

plt.show()

enter image description here

我将把这留给读者作为练习,去扩展它到三个或更多维度,但你已经有了想法。


上面的解决方案是如何仅使用matplotlib来完成此操作。还有其他选择(在底层使用matplotlib)可用,具有内置此行为,因此无需自己过滤数据。

  1. 使用seaborn,它是matplotlib的高级API。 seaborn.boxplot在底层过滤NaN
import seaborn as sns

sns.boxplot(data=data)

1D

enter image description here

2D

enter image description here


  1. 使用 pandas。如果使用 df.plot(kind='box') 进行绘图,pandas 会忽略 NaN,因为它默认使用 matplotlib 作为绘图后端。
import pandas as pd

df = pd.DataFrame(data)

df.plot(kind='box')

1D

enter image description here

2D

enter image description here


谢谢您提供这段代码,但是有一个问题需要解决... boxplot 不应该默认处理这个问题吗? - Sanjay Manohar
非常好的总结!不过有一个问题。在开始的时候,使用dropna()会不会更简单一些,而不是使用isnan和位反转呢? - undefined

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