在列上循环时如何在熊猫中生成清晰的图?

时间:2019-03-14 23:16:48

标签: python python-3.x pandas

生成可复制的数据框:

df = pd.DataFrame(np.random.randn(50, 1000), columns=list('ABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDED'))

检查每个变量的分布是否正常(注意:这需要很长时间才能运行)

# Set the column names

columns= df.columns

# Loop over all columns

fig, axs = plt.subplots(len(df.columns), figsize=(5, 25))
for n, col in enumerate(df.columns):
    df[col].hist(ax=axs[n])

结果生成的直方图难以辨认,并且运行时间很长。

时间长度还可以,但是我很好奇是否有人建议生成清晰的直方图(不必花哨),可以对整个数据帧进行快速检查以确保分布的正态性。

5 个答案:

答案 0 :(得分:4)

此代码生成1000个直方图,使您可以足够详细地查看每个直方图,以了解列的正态分布情况:

import pandas as pd
import matplotlib.pyplot as plt

cols = 1000
df = pd.DataFrame(np.random.normal(0, 1, [50, cols]))

# Loop over all columns
fig, ax = plt.subplots(figsize = (16, 10))
for n, col in enumerate(df.columns):
    plt.subplot(25, 40, n+1)
    df[col].hist(ax = plt.gca())
    plt.axis('off')
plt.tight_layout()

plt.savefig('1000_histograms.png', bbox_inches='tight', pad_inches = 0, dpi = 200)

1000 histograms

确定正态性的另一种方法是使用QQ图,与直方图相比,它可能更容易可视化:

import statsmodels.api as sm

cols = 1000
df = pd.DataFrame(np.random.normal(0,1, [50, cols]))

fig, axs = plt.subplots(figsize=(18, 12))
for n, col in enumerate(df.columns):
    plt.subplot(25,40,n+1)
    sm.qqplot(df[col], ax=plt.gca(), #line='45', 
              marker='.', markerfacecolor='C0', markeredgecolor='C0', 
              markersize=2)
#    sm.qqline(ax=plt.gca(), line='45', fmt='lightgray')
    plt.axis('off')

plt.savefig('1000_QQ_plots13.png', bbox_inches='tight', pad_inches=0, dpi=200)

1000 QQ plots

每行对角线越接近45度,列数据分布越正态。

答案 1 :(得分:4)

  1. 绘图与正态性检验
  2. 主张
  3. 输出示例
  4. 相应的代码示例

绘图与正常性测试

正如下面评论中所讨论的,OP问题已更改为成千上万的地块管理。从这个角度来看,Nathaniel answer's is appropriate

但是,我认为未曾说过的意图是确定给定变量是否正态分布,要考虑成千上万个变量。

  

检查每个变量的分布是否正常(注意:这需要很长时间才能运行)

考虑到这一点,(在我看来)让人类查看成千上万的图以发现正态/非正态分布是不适当的方法。有一个法国惯用法:“ usineàgaz”(“加油站”)

因此,此答案侧重于以编程方式执行分析并提供某种更简洁的报告。

主张

对大量列进行数据正态性分析。 它依赖于in this answer表示的建议。

想法是:

  • 对所有列进行分布测试(正态)
  • 将结果资本化为数据框
  • 以图表形式报告正常/非正常比率。
  • 报告非常规列名。

使用这种方法,我们可以进一步使用编程来操纵普通/非普通列。例如,我们可以执行其他分布测试,或仅绘制非正态分布,从而减少实际观察的图形数量。

输出示例:

------------
Columns probably not a normal dist:
  Column  Not_Normal  p-value   Normality
0      V        True      0.0  Not Normal
0      W        True      0.0  Not Normal
0      X        True      0.0  Not Normal
0      Y        True      0.0  Not Normal
0      Z        True      0.0  Not Normal

enter image description here

免责声明:所使用的方法在统计上可能不是“规范”的。使用统计工具时应格外小心,因为每个工具都是其特定的使用领域/用例。

我选择了0.01(1%)的p值,因为它可能是科学出版物中即将出现的标准值,而不是通常的0.05(5%))

一个人应该读https://en.wikipedia.org/wiki/Normality_test

  

单变量正态性检验包括:

  • D'Agostino的K平方检验,
  • Jarque–Bera检验
  • 安德森–达令测试
  • Cramér–von Mises准则,
  • Lilliefors测试
  • Kolmogorov–Smirnov检验
  • Shapiro–Wilk测试,和
  • 皮尔逊的卡方检验。

代码

您的计算机上的行为可能会有所不同,具体取决于RNG(生成随机数)。 以下示例使用numpy进行了5次常规随机采样和5次pareto随机采样。 在这种情况下,正常性测试的效果很好(即使我觉得0.0 p值的测试即使对于pareto随机生成也是可疑的) 不过,我认为我们可以同意,这是关于方法的,而不是实际的结果。

import pandas as pd
import numpy as np
import scipy
from scipy import stats
import seaborn as sb
import matplotlib.pyplot as plt
import sys

print('System: {}'.format(sys.version))
for module in [pd, np, scipy, sb]:
    print('Module {:10s} - version {}'.format(module.__name__, module.__version__))

nb_lines = 10000
headers_normal = 'ABCDE'
headers_pareto = 'VWXYZ'
reapeat_factor = 1
nb_cols = len(list(reapeat_factor * headers_normal))

df_normal = pd.DataFrame(np.random.randn(nb_lines, nb_cols), columns=list(reapeat_factor * headers_normal))

df_pareto = pd.DataFrame((np.random.pareto(12.0, size=(nb_lines,nb_cols )) + 15.) * 4., columns=list(reapeat_factor * headers_pareto))

df = df_normal.join(df_pareto)

alpha = 0.01
df_list = list()

# normality code taken from https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.normaltest.html
cat_map = {True: 'Not Normal',
           False: 'Maybe Normal'}
for col in df.columns:
    k2, p = stats.normaltest(df[col])
    is_not_normal = p < alpha
    tmp_df = pd.DataFrame({'Column': [col],
                           'Not_Normal': [is_not_normal],
                           'p-value': [p],
                           'Normality': cat_map[is_not_normal]
                           })
    df_list.append(tmp_df)

df_results = pd.concat(df_list)
df_results['Normality'] = df_results['Normality'].astype('category')

print('------------')
print('Columns names probably not a normal dist:')
# full data
print(df_results[(df_results['Normality'] == 'Not Normal')])
# only column names
# print(df_results[(df_results['Normality'] == 'Not Normal')]['Column'])
print('------------')
print('Plotting countplot')
sb.countplot(data=df_results, y='Normality', orient='v')
plt.show()

输出:

System: 3.7.2 (default, Feb 21 2019, 17:35:59) [MSC v.1915 64 bit (AMD64)]
Module pandas     - version 0.24.1
Module numpy      - version 1.16.2
Module scipy      - version 1.2.1
Module seaborn    - version 0.9.0
------------
Columns names probably not a normal dist:
  Column  Not_Normal  p-value   Normality
0      V        True      0.0  Not Normal
0      W        True      0.0  Not Normal
0      X        True      0.0  Not Normal
0      Y        True      0.0  Not Normal
0      Z        True      0.0  Not Normal
------------
Plotting countplot

答案 2 :(得分:1)

我真的很喜欢Nathaniel's的答案,但我会加两分钱。

我会去seaborn,尤其是seaborn.distplot。 这将使您可以轻松地将正态分布拟合到每个直方图上,并使可视化更加容易。

import seaborn as sns
from scipy.stats import norm
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

cols = 1000
df = pd.DataFrame(np.random.normal(0, 1, [50, cols]))
from scipy.stats import norm
fig, ax = plt.subplots(figsize = (16, 10))
for i, col in enumerate(df.columns):
    ax=fig.add_subplot(25, 4, i+1)
    sns.distplot(df[col],fit=norm, kde=False,ax=ax)
plt.tight_layout()

此外,我不确定在示例中放置具有相同名称的列是否故意。如果是这种情况,最简单的遍历各列的解决方案是使用.iloc,代码将如下所示:

import seaborn as sns
from scipy.stats import norm
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

 df = pd.DataFrame(np.random.randn(50, 1000), columns=list('ABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDEDABCDABCDED'))

fig, ax = plt.subplots(figsize = (12, 10))
for i, col in enumerate(df.columns):
    plt.subplot(25, 40, i+1)
    sns.distplot(df.iloc[:,i],fit=norm, kde=False,ax=plt.gca())
    plt.axis('off')
plt.tight_layout()

enter image description here

答案 3 :(得分:0)

尝试这样的事情:

plt.figure(figsize=(26, 3 * len(df.columns))
for i, col in enumerate(df.columns):
    plt.subplot(3, 4, i + 1)
    plt.hist(df[col], color='blue', bins=100)
    plt.title(col)

4是列数,3是行数。我想最好写这样的东西,而不是3:

plt.subplot(len(df.columns) / 4, 4, i + 1)

答案 4 :(得分:0)

尝试-紧密布局确保没有重叠,figsize控制每个图的大小。

import pandas as pd, numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.randn(1000, 3*30), columns=list('ABC'*30))

df.hist(figsize=(20,20))
plt.tight_layout()
plt.show()

但是,如果您要进行正态性测试,建议您使用类似以下内容的方法:https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.normaltest.html而不是依靠目视检查,尤其是在您有很多变量的情况下。