在 python 中绘制配置文件直方图

Posted

技术标签:

【中文标题】在 python 中绘制配置文件直方图【英文标题】:Plotting profile hitstograms in python 【发布时间】:2014-07-05 17:46:03 【问题描述】:

我正在尝试为 pandas.DataFrame 的两列绘制剖面图。我不希望这会直接出现在 pandas 中,但似乎 matplotlib 中也没有任何内容。我四处搜索,除了rootpy之外的任何包中都找不到它。在我自己花时间写这篇文章之前,我想我会问是否有一个包含配置文件直方图的小包,也许它们以不同的名字被知道。

如果您不知道我所说的“配置文件直方图”是什么意思,请查看 ROOT 实现。 http://root.cern.ch/root/html/TProfile.html

【问题讨论】:

这看起来像 errorbar 情节。 虽然情节确实有误差线(大多数应该),但我不确定你是否明白这一点。 plt.errorbar(xbincenters, ymean, yerr=yerroronmean,fmt='+') 如果我自己计算 xbincenters, ymean 和 yerroronmean 会给我轮廓图但是拥有共享库的目的是让人们不必重新发明像这样的常见任务的***。理想情况下,我想传递两个 DataFrame 列和一些 bin。 由于循环导入原因matplotlib 无法了解pandas。共享库为您提供了构建更大工具的修补玩具,而不是每个 可以想象的工具。那样就是疯狂(对于维护者)。我怀疑您通过GroupBy 在熊猫中的计算是 对不起,你想要cut 而不是GroupBy ***.com/questions/21441259/… 我并不是暗示应该以 matplotlib 依赖于 pandas 的方式将此功能添加到 pandas。查看 pandas.tools.plotting 中的 scatter_matrix 方法之类的依赖项。我认为散点矩阵方法比剖面图方法更不需要。 【参考方案1】:

使用seaborn。来自@MaxNoe 的数据

import numpy as np
import seaborn as sns

# just some random numbers to get started
x = np.random.uniform(-2, 2, 10000)
y = np.random.normal(x**2, np.abs(x) + 1)

sns.regplot(x=x, y=y, x_bins=10, fit_reg=None)

您可以做更多事情(误差带来自引导程序,您可以更改 y 轴上的估计器,添加回归,...)

【讨论】:

如何改变y轴上的估计器和y轴误差的估计器? 见regplot的文档。 (参数x_estimator)。错误是使用引导程序计算的,因此无事可做。【参考方案2】:

虽然@Keith 的回答似乎符合您的意思,但代码量相当大。我认为这可以更简单地完成,因此人们可以掌握关键概念并可以在此基础上进行调整和构建。

让我强调一点:ROOT 所称的 ProfileHistogram 并不是一种特殊的绘图。它一个误差线图。这可以简单地在 matplotlib 中完成。

这是一种特殊的计算,这不是绘图库的任务。这属于 pandas 领域,pandas 擅长这类事情。对于 ROOT 来说,这是一个症状,因为它是一个巨大的单片堆,为此需要一个额外的类。

所以你想要做的是:离散化一些变量x,对于每个bin,计算另一个变量y

这可以通过使用np.digitize 以及pandas 的groupyaggregate 方法轻松完成。

把它们放在一起:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# just some random numbers to get startet
x = np.random.uniform(-2, 2, 10000)
y = np.random.normal(x**2, np.abs(x) + 1)
df = pd.DataFrame('x': x, 'y': y)


# calculate in which bin row belongs base on `x`
# bins needs the bin edges, so this will give as 100 equally sized bins
bins = np.linspace(-2, 2, 101)
df['bin'] = np.digitize(x, bins=bins)
bin_centers = 0.5 * (bins[:-1] + bins[1:])
bin_width = bins[1] - bins[0]

# grouby bin, so we can calculate stuff
binned = df.groupby('bin')
# calculate mean and standard error of the mean for y in each bin
result = binned['y'].agg(['mean', 'sem'])
result['x'] = bin_centers
result['xerr'] = bin_width / 2

# plot it

result.plot(
    x='x',
    y='mean',
    xerr='xerr',
    yerr='sem',
    linestyle='none',
    capsize=0,
    color='black',
)
plt.savefig('result.png', dpi=300)

就像 ROOT ;)

【讨论】:

这只有在每个 bin 都有一个值的情况下才有效。否则,由于 groupby,结果的行数将少于 bin 数。这将导致绘图错误。【参考方案3】:

您可以使用scipy.stats.binned_statistic 轻松完成。

import scipy.stats
import numpy
import matplotlib.pyplot as plt

x = numpy.random.rand(10000)
y = x + scipy.stats.norm(0, 0.2).rvs(10000)

means_result = scipy.stats.binned_statistic(x, [y, y**2], bins=50, range=(0,1), statistic='mean')
means, means2 = means_result.statistic
standard_deviations = numpy.sqrt(means2 - means**2)
bin_edges = means_result.bin_edges
bin_centers = (bin_edges[:-1] + bin_edges[1:])/2.

plt.errorbar(x=bin_centers, y=means, yerr=standard_deviations, linestyle='none', marker='.')

【讨论】:

这很好,但你的 yerr 需要除以 N。你想要平均误差。 bin_N = Counter(means_result.binnumber).values() 将为您提供每个 bin 的 Ns 不太清楚Counter 指的是什么,但我写的是我的意思。剖面图可以显示y 数据的分布宽度(这是我写的),也可以显示y 数据平均值的不确定性。您必须根据情节的目的选择您想要的。根据我的经验,大多数(但不是全部)剖面图都显示了y 分布的宽度,这就是为什么我按照我的方式编写答案的原因。而且,如果您确实需要平均误差,最好将yerr 除以sqrt(N) 而不是N【参考方案4】:

我自己为这个功能制作了一个模块。

import pandas as pd
from pandas import Series, DataFrame
import numpy as np
import matplotlib.pyplot as plt

def Profile(x,y,nbins,xmin,xmax,ax):
    df = DataFrame('x' : x , 'y' : y)

    binedges = xmin + ((xmax-xmin)/nbins) * np.arange(nbins+1)
    df['bin'] = np.digitize(df['x'],binedges)

    bincenters = xmin + ((xmax-xmin)/nbins)*np.arange(nbins) + ((xmax-xmin)/(2*nbins))
    ProfileFrame = DataFrame('bincenters' : bincenters, 'N' : df['bin'].value_counts(sort=False),index=range(1,nbins+1))

    bins = ProfileFrame.index.values
    for bin in bins:
        ProfileFrame.ix[bin,'ymean'] = df.ix[df['bin']==bin,'y'].mean()
        ProfileFrame.ix[bin,'yStandDev'] = df.ix[df['bin']==bin,'y'].std()
        ProfileFrame.ix[bin,'yMeanError'] = ProfileFrame.ix[bin,'yStandDev'] / np.sqrt(ProfileFrame.ix[bin,'N'])

    ax.errorbar(ProfileFrame['bincenters'], ProfileFrame['ymean'], yerr=ProfileFrame['yMeanError'], xerr=(xmax-xmin)/(2*nbins), fmt=None) 
    return ax


def Profile_Matrix(frame):
  #Much of this is stolen from https://github.com/pydata/pandas/blob/master/pandas/tools/plotting.py


    import pandas.core.common as com
    import pandas.tools.plotting as plots
    from pandas.compat import lrange
    from matplotlib.artist import setp

    range_padding=0.05

    df = frame._get_numeric_data()
    n = df.columns.size

    fig, axes = plots._subplots(nrows=n, ncols=n, squeeze=False)

    # no gaps between subplots
    fig.subplots_adjust(wspace=0, hspace=0)

    mask = com.notnull(df)

    boundaries_list = []
    for a in df.columns:
        values = df[a].values[mask[a].values]
        rmin_, rmax_ = np.min(values), np.max(values)
        rdelta_ext = (rmax_ - rmin_) * range_padding / 2.
        boundaries_list.append((rmin_ - rdelta_ext, rmax_+ rdelta_ext))

    for i, a in zip(lrange(n), df.columns):
        for j, b in zip(lrange(n), df.columns):

            common = (mask[a] & mask[b]).values
            nbins = 100
            (xmin,xmax) = boundaries_list[i]

            ax = axes[i, j]
            Profile(df[a][common],df[b][common],nbins,xmin,xmax,ax)

            ax.set_xlabel('')
            ax.set_ylabel('')

            plots._label_axis(ax, kind='x', label=b, position='bottom', rotate=True)
            plots._label_axis(ax, kind='y', label=a, position='left')

            if j!= 0:
                ax.yaxis.set_visible(False)
            if i != n-1:
                ax.xaxis.set_visible(False)

    for ax in axes.flat:
        setp(ax.get_xticklabels(), fontsize=8)
        setp(ax.get_yticklabels(), fontsize=8)

    return axes

【讨论】:

【参考方案5】:

据我所知,matplotlib 仍然不允许直接生成轮廓直方图。 您可以改为查看 Hippodraw,它是 SLAC 开发的一个包,可用作 Python 扩展模块。 这里有一个 Profile 直方图示例:

http://www.slac.stanford.edu/grp/ek/hippodraw/datareps_root.html#datareps_profilehist

【讨论】:

为什么物理学家似乎总是首先开发数据科学工具?无论如何,这似乎提供了我正在寻找的东西,尽管我也可以全力以赴并安装rootpy.org。您是否碰巧知道在 Windows 7 上添加到我精心设计的 Canopy Express 设置中哪个更简单? 因为物理学家很疯狂,你知道:-)(我是其中之一,可以确认)。真诚地,目前我既不使用 Hippodraw 也不使用 rootpy,作为科学的 Python 发行版,我有 WinPython 和 Anaconda(这主要用于 Mayavi)。我只是注意到一个问题。 Windows 安装的链接 (slac.stanford.edu/grp/ek/hippodraw/install_notes.html) 似乎已损坏。 Github 上似乎有一个 fork,但仅适用于 Unix (github.com/plasmodic/hippodraw)。所以在这一点上我不知道原始的 Hippodraw 是否真的可用或不再维护。 好的,谢谢。我将尝试按照上面 cmets 中的描述自己编写此代码,但我是 pandas 的新手,因此我将不胜感激。如果有人想向我展示实现这一点的好方法,我会将该帖子标记为答案。 这里有一个在 Pyroot 中实现的配置文件直方图的简短示例,检查它是否对您的目的有用:seal.web.cern.ch/seal/SEAL_0_3_2/devguide/PyROOT-howto.html 不幸的是,这只是如何使用 TProfile 方法。我的问题是其他任何地方都没有类似的方法。最终,我将把 rootpy(不是 pyroot)添加到我的包中,但现在我将不得不自己编写。

以上是关于在 python 中绘制配置文件直方图的主要内容,如果未能解决你的问题,请参考以下文章

如何按范围对csv文件中的列进行分组并使用python绘制直方图?

matplotlib绘制直方图之基本配置——万能模板案例

Python开发模块:Pygal 绘制直方图

[Python Study Notes]双层直方图绘制

[Python Study Notes]堆叠直方图绘制

如何在 python 中绘制 300x300 RGB 图像的灰度值