【问题标题】:How do I shift categorical scatter markers to left and right above xticks (multiple data sets per category)?如何将分类散点标记移动到 xticks 上方的左右(每个类别有多个数据集)?
【发布时间】:2017-04-04 21:30:32
【问题描述】:

我有一个简单的 pandas 数据框,我想用 matplotlib 进行绘图:

import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_excel('SAT_data.xlsx', index_col = 'State')

plt.figure()
plt.scatter(df['Year'], df['Reading'], c = 'blue', s = 25)
plt.scatter(df['Year'], df['Math'], c = 'orange', s = 25)
plt.scatter(df['Year'], df['Writing'], c = 'red', s = 25)

这是我的情节的样子:

我想将蓝色数据点向左移动一点,将红色数据点向右移动一点,因此 x 轴上的每一年都有三个小列的散点数据,而不是全部三个数据集重叠。我尝试并未能正确使用“verts”参数。有没有更好的方法来做到这一点?

【问题讨论】:

    标签: python pandas matplotlib


    【解决方案1】:

    使用偏移变换将允许以点为单位而不是数据单位来移动散点。这样做的好处是它们总是会紧紧地靠在一起,而与图形大小、缩放级别等无关。

    import matplotlib.pyplot as plt
    import numpy as np; np.random.seed(0)
    import matplotlib.transforms as transforms
    
    year = np.random.choice(np.arange(2006,2017), size=(300) ) 
    values = np.random.rand(300, 3)
    
    plt.figure()
    
    offset = lambda p: transforms.ScaledTranslation(p/72.,0, plt.gcf().dpi_scale_trans)
    trans = plt.gca().transData
    
    sc1 = plt.scatter(year, values[:,0], c = 'blue', s = 25, transform=trans+offset(-5))
    plt.scatter(year, values[:,1], c = 'orange', s = 25)
    plt.scatter(year, values[:,2], c = 'red', s = 25, transform=trans+offset(5))
    
    plt.show()
    

    大图:

    普通图:

    放大

    一些解释:

    问题是我们想在数据坐标中的某些数据上添加点偏移量。虽然数据坐标使用transData(我们通常甚至在表面上看不到)自动转换为显示坐标,但添加一些偏移量需要我们更改转换。
    我们通过添加偏移量来做到这一点。虽然我们可以只添加以像素为单位的偏移量(显示坐标),但以点为单位添加偏移量更方便,因此使用与给出的散点大小相同的单位(它们的大小实际上是点的平方)。 所以我们想知道p点有多少像素?这可以通过将p 除以 ppi(每英寸点数)得到英寸,然后乘以 dpi(每英寸点数)得到显示像素。此计算在 ScaledTranslation 中完成。 虽然原则上每英寸的点数是可变的(并由dpi_scale_trans 变换处理),但每英寸的点数是固定的。 Matplotlib 使用 72 ppi,有点像 typesetting standard

    【讨论】:

    • 这里有一些严重的黑魔法 =)。您介意解释一下偏移变换中的数字 72 是从哪里来的吗?
    • 我在答案中添加了解释。希望对您有所帮助。
    【解决方案2】:

    一种快速而肮脏的方法是创建一个小的偏移量dx,然后从蓝色点的x 值中减去它,然后添加到红色点的x 值中。

    dx = 0.1
    plt.scatter(df['Year'] - dx, df['Reading'], c = 'blue', s = 25) 
    plt.scatter(df['Year'],      df['Math'], c = 'orange', s = 25) 
    plt.scatter(df['Year'] + dx, df['Writing'], c = 'red', s = 25)
    

    另一种选择是使用来自seaborn 库的stripplot 函数。有必要将原始数据框融合为长格式,以便每一行包含一年、一个测试和一个分数。然后创建一个stripplot,指定年份为x,得分为y,测试为huesplit 关键字参数控制将类别绘制为每个x 的单独条纹。还有jitter 参数会为x 值添加一些噪音,以便它们占据一些小区域而不是在一条垂直线上。

    import pandas as pd
    import seaborn as sns
    
    # make up example data
    np.random.seed(2017)
    df = pd.DataFrame(columns = ['Reading','Math','Writing'], 
                      data = np.random.normal(540,30,size=(1000,3)))
    df['Year'] = np.random.choice(np.arange(2006,2016),size=1000)
    
    # melt the data into long form
    df1 = pd.melt(df, var_name='Test', value_name='Score',id_vars=['Year'])
    
    # make a stripplot
    fig, ax = plt.subplots(figsize=(10,7))
    sns.stripplot(data = df1, x='Year', y = 'Score', hue = 'Test', 
                  jitter = True, split = True, alpha = 0.7, 
                  palette = ['blue','orange','red'])
    

    输出:

    【讨论】:

      猜你喜欢
      • 2021-02-06
      • 2023-01-13
      • 1970-01-01
      • 2014-12-28
      • 2020-11-03
      • 1970-01-01
      • 2021-10-10
      • 2017-04-03
      • 1970-01-01
      相关资源
      最近更新 更多