【问题标题】:matplotlib: disregard outliers when plottingmatplotlib:绘图时忽略异常值
【发布时间】:2012-08-06 14:50:12
【问题描述】:

我正在绘制来自各种测试的一些数据。有时在测试中我碰巧有一个异常值(比如 0.1),而所有其他值都小三个数量级。

使用 matplotlib,我在[0, max_data_value] 范围内绘图

我怎样才能只放大我的数据而不显示异常值,这会弄乱我的绘图中的 x 轴?

我是否应该简单地取 95 个百分位数并在 x 轴上设置范围 [0, 95_percentile]

【问题讨论】:

  • 什么样的情节?分散?直方图?
  • 我正在使用直方图进行绘图。

标签: python plot matplotlib percentile outliers


【解决方案1】:

在某些情况下(例如,在直方图中,例如 Joe Kington 的答案中的那个),重新缩放该图可能表明存在异常值,但它们已被缩放比例部分裁剪掉。删除异常值不会与重新缩放具有相同的效果。与检测和删除异常值相比,自动查找适当的坐标轴范围通常更可取且更容易。

这是一个使用百分位数和数据相关边距来实现漂亮视图的自动缩放想法。

# xdata = some x data points ...
# ydata = some y data points ...

# Finding limits for y-axis     
ypbot = np.percentile(ydata, 1)
yptop = np.percentile(ydata, 99)
ypad = 0.2*(yptop - ypbot)
ymin = ypbot - ypad
ymax = yptop + ypad

示例用法:

fig = plt.figure(figsize=(6, 8))

ax1 = fig.add_subplot(211)
ax1.scatter(xdata, ydata, s=1, c='blue')
ax1.set_title('Original')
ax1.axhline(y=0, color='black')

ax2 = fig.add_subplot(212)
ax2.scatter(xdata, ydata, s=1, c='blue')
ax2.axhline(y=0, color='black')
ax2.set_title('Autscaled')
ax2.set_ylim([ymin, ymax])

plt.show()

【讨论】:

    【解决方案2】:

    我认为使用 pandas 分位数很有用,而且更灵活。

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    
    fig = plt.figure()
    ax1 = fig.add_subplot(121)
    ax2 = fig.add_subplot(122)
    
    pd_series = pd.Series(np.random.normal(size=300)) 
    pd_series_adjusted = pd_series[pd_series.between(pd_series.quantile(.05), pd_series.quantile(.95))] 
    
    ax1.boxplot(pd_series)
    ax1.set_title('Original')
    
    ax2.boxplot(pd_series_adjusted)
    ax2.set_title('Adjusted')
    
    plt.show()
    

    【讨论】:

      【解决方案3】:

      我通常通过函数np.clip传递数据,如果您对数据的最大值和最小值有一些合理的估计,就使用它。如果您没有合理的估计,截断数据的直方图会显示尾部的大小,如果异常值真的只是异常值,则尾部应该很小。

      我运行的是这样的:

      import numpy as np
      import matplotlib.pyplot as plt
      
      data = np.random.normal(3, size=100000)
      plt.hist(np.clip(data, -15, 8), bins=333, density=True)
      

      如果您更改裁剪函数中的最小值和最大值,您可以比较结果,直到找到适合您数据的正确值。

      在此示例中,您可以立即看到最大值 8 并不好,因为您删除了很多有意义的信息。 -15 的最小值应该没问题,因为尾巴甚至不可见。

      您可能会编写一些代码,在此基础上找到一些好的界限,根据一些容差最小化尾部的大小。

      【讨论】:

        【解决方案4】:

        对于异常值没有单一的“最佳”测试。理想情况下,您应该包含先验信息(例如,“这个参数不应该超过 x,因为等等……”)。

        大多数异常值检验使用中值绝对偏差,而不是第 95 个百分位或其他基于方差的测量。否则,计算出的方差/标准差将被异常值严重扭曲。

        这是一个实现更常见异常值测试之一的函数。

        def is_outlier(points, thresh=3.5):
            """
            Returns a boolean array with True if points are outliers and False 
            otherwise.
        
            Parameters:
            -----------
                points : An numobservations by numdimensions array of observations
                thresh : The modified z-score to use as a threshold. Observations with
                    a modified z-score (based on the median absolute deviation) greater
                    than this value will be classified as outliers.
        
            Returns:
            --------
                mask : A numobservations-length boolean array.
        
            References:
            ----------
                Boris Iglewicz and David Hoaglin (1993), "Volume 16: How to Detect and
                Handle Outliers", The ASQC Basic References in Quality Control:
                Statistical Techniques, Edward F. Mykytka, Ph.D., Editor. 
            """
            if len(points.shape) == 1:
                points = points[:,None]
            median = np.median(points, axis=0)
            diff = np.sum((points - median)**2, axis=-1)
            diff = np.sqrt(diff)
            med_abs_deviation = np.median(diff)
        
            modified_z_score = 0.6745 * diff / med_abs_deviation
        
            return modified_z_score > thresh
        

        作为使用它的示例,您可以执行以下操作:

        import numpy as np
        import matplotlib.pyplot as plt
        
        # The function above... In my case it's in a local utilities module
        from sci_utilities import is_outlier
        
        # Generate some data
        x = np.random.random(100)
        
        # Append a few "bad" points
        x = np.r_[x, -3, -10, 100]
        
        # Keep only the "good" points
        # "~" operates as a logical not operator on boolean numpy arrays
        filtered = x[~is_outlier(x)]
        
        # Plot the results
        fig, (ax1, ax2) = plt.subplots(nrows=2)
        
        ax1.hist(x)
        ax1.set_title('Original')
        
        ax2.hist(filtered)
        ax2.set_title('Without Outliers')
        
        plt.show()
        

        【讨论】:

        • 这是一个很好的答案(来自我的+1),但我认为“~”在位上不是,不是逻辑上的不 - 在这里似乎无关紧要,因为我不是 100% 清楚,但在其他地方会。 ~False != True,但 not False == True
        • 好点!在 numpy 中,它被重载为逻辑而不是布尔数组(例如~np.array(False) == True),但其他任何事情都不是这种情况。我应该澄清一下。 (附带说明,按照惯例,如果some_array 有多个元素,则not some_array 将引发值错误。因此在上面的示例中需要~。)
        • 感谢您的回复 - 我实际上尝试了“不”并得到了您预测的错误,所以我更加困惑......
        • 当中值偏差为零时会中断。当我天真地加载一个包含超过 50% 零的数据集时,这种情况发生在我身上。
        • @WesleyTansey 您是否找到了一个很好的解决方案来处理 0 错误的设计?我目前正在解决同样的问题。
        【解决方案5】:

        如果您并不像 Joe 提到的那样对拒绝异常值大惊小怪,并且这样做纯粹是出于审美原因,您可以设置绘图的 x 轴限制:

        plt.xlim(min_x_data_value,max_x_data_value)
        

        这些值是您希望显示的限制。

        plt.ylim(min,max) 也可以在 y 轴上设置限制。

        【讨论】:

        • 不过,对于直方图,OP 还需要重新计算 bin。 Matplotlib 使用固定的 bin 边缘。放大时它不会“重新组合”。
        猜你喜欢
        • 2020-11-07
        • 2019-05-07
        • 2019-06-03
        • 1970-01-01
        • 1970-01-01
        • 2011-03-17
        • 2011-03-13
        相关资源
        最近更新 更多