【问题标题】:Scatter plots in Pandas/Pyplot: How to plot by category [duplicate]Pandas / Pyplot中的散点图:如何按类别绘制[重复]
【发布时间】:2014-03-06 10:50:46
【问题描述】:

我正在尝试使用 Pandas DataFrame 对象在 pyplot 中制作一个简单的散点图,但想要一种有效的方法来绘制两个变量,但符号由第三列(键)指示。我尝试了各种使用 df.groupby 的方法,但都没有成功。下面是一个示例 df 脚本。这会根据“key1”为标记着色,但我希望看到带有“key1”类别的图例。我接近了吗?谢谢。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
fig1 = plt.figure(1)
ax1 = fig1.add_subplot(111)
ax1.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
plt.show()

【问题讨论】:

    标签: python matplotlib pandas


    【解决方案1】:

    使用Seaborn (pip install seaborn) 作为单行者很简单

    sns.scatterplot(x_vars="one", y_vars="two", data=df, hue="key1") :

    import seaborn as sns
    import pandas as pd
    import numpy as np
    np.random.seed(1974)
    
    df = pd.DataFrame(
        np.random.normal(10, 1, 30).reshape(10, 3),
        index=pd.date_range('2010-01-01', freq='M', periods=10),
        columns=('one', 'two', 'three'))
    df['key1'] = (4, 4, 4, 6, 6, 6, 8, 8, 8, 8)
    
    sns.scatterplot(x="one", y="two", data=df, hue="key1")
    

    这是供参考的数据框:

    由于您的数据中有三个变量列,您可能希望绘制所有成对维度:

    sns.pairplot(vars=["one","two","three"], data=df, hue="key1")
    

    https://rasbt.github.io/mlxtend/user_guide/plotting/category_scatter/ 是另一种选择。

    【讨论】:

      【解决方案2】:

      seaborn 有一个包装函数 scatterplot,它可以更有效地执行此操作。

      sns.scatterplot(data = df, x = 'one', y = 'two', data =  'key1'])
      

      【讨论】:

        【解决方案3】:

        从 matplotlib 3.1 开始,您可以使用.legend_elements()。 Automated legend creation 中显示了一个示例。优点是可以使用单个分散调用。

        在这种情况下:

        import numpy as np
        import pandas as pd
        import matplotlib.pyplot as plt
        
        df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), 
                          index = pd.date_range('2010-01-01', freq = 'M', periods = 10), 
                          columns = ('one', 'two', 'three'))
        df['key1'] = (4,4,4,6,6,6,8,8,8,8)
        
        
        fig, ax = plt.subplots()
        sc = ax.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
        ax.legend(*sc.legend_elements())
        plt.show()
        

        如果键没有直接作为数字给出,它看起来像

        import numpy as np
        import pandas as pd
        import matplotlib.pyplot as plt
        
        df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), 
                          index = pd.date_range('2010-01-01', freq = 'M', periods = 10), 
                          columns = ('one', 'two', 'three'))
        df['key1'] = list("AAABBBCCCC")
        
        labels, index = np.unique(df["key1"], return_inverse=True)
        
        fig, ax = plt.subplots()
        sc = ax.scatter(df['one'], df['two'], marker = 'o', c = index, alpha = 0.8)
        ax.legend(sc.legend_elements()[0], labels)
        plt.show()
        

        【讨论】:

        • 我收到一条错误消息,提示“PathCollection”对象没有属性“legends_elements”。我的代码如下。 fig, ax = plt.subplots(1, 1, figsize = (4,4)) scat = ax.scatter(rand_jitter(important_dataframe["workout_type_int"], jitter = 0.04), important_dataframe["distance"], c = color_list, marker = 'o', alpha = 0.9) print(scat.legends_elements()) #ax.legend(*scat.legend_elements())
        • @NandishPatel 检查这个答案的第一句话。还要确保不要混淆legends_elements 和legend_elements。
        • 好的,谢谢。那是一个错字(传说/传说)。自从过去 6 个小时以来,我一直在做一些事情,所以我没有想到 Matplotlib 版本。我以为我用的是最新的。我很困惑文档说有这样的方法,但代码给出了错误。再次感谢你。我现在可以睡觉了。
        【解决方案4】:

        这很 hacky,但您可以使用 one1 作为 Float64Index 一次性完成所有操作:

        df.set_index('one').sort_index().groupby('key1')['two'].plot(style='--o', legend=True)
        

        请注意,从 0.20.3 开始,sorting the index is necessary,图例为 a bit wonky。

        【讨论】:

          【解决方案5】:

          您可以使用 df.plot.scatter,并将数组传递给定义每个点颜色的 c= 参数:

          import numpy as np
          import pandas as pd
          import matplotlib.pyplot as plt
          df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
          df['key1'] = (4,4,4,6,6,6,8,8,8,8)
          colors = np.where(df["key1"]==4,'r','-')
          colors[df["key1"]==6] = 'g'
          colors[df["key1"]==8] = 'b'
          print(colors)
          df.plot.scatter(x="one",y="two",c=colors)
          plt.show()
          

          【讨论】:

            【解决方案6】:

            您也可以尝试Altair 或ggpot,它们专注于声明性可视化。

            import numpy as np
            import pandas as pd
            np.random.seed(1974)
            
            # Generate Data
            num = 20
            x, y = np.random.random((2, num))
            labels = np.random.choice(['a', 'b', 'c'], num)
            df = pd.DataFrame(dict(x=x, y=y, label=labels))
            

            Altair 代码

            from altair import Chart
            c = Chart(df)
            c.mark_circle().encode(x='x', y='y', color='label')
            

            ggplot 代码

            from ggplot import *
            ggplot(aes(x='x', y='y', color='label'), data=df) +\
            geom_point(size=50) +\
            theme_bw()
            

            【讨论】:

              【解决方案7】:

              有了plt.scatter,我只能想到一个:使用代理艺人:

              df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
              df['key1'] = (4,4,4,6,6,6,8,8,8,8)
              fig1 = plt.figure(1)
              ax1 = fig1.add_subplot(111)
              x=ax1.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
              
              ccm=x.get_cmap()
              circles=[Line2D(range(1), range(1), color='w', marker='o', markersize=10, markerfacecolor=item) for item in ccm((array([4,6,8])-4.0)/4)]
              leg = plt.legend(circles, ['4','6','8'], loc = "center left", bbox_to_anchor = (1, 0.5), numpoints = 1)
              

              结果是:

              【讨论】:

                【解决方案8】:

                您可以为此使用 scatter,但这需要为您的 key1 提供数值,并且您不会有图例,正如您所注意到的。

                最好将plot 用于此类离散类别。例如:

                import matplotlib.pyplot as plt
                import numpy as np
                import pandas as pd
                np.random.seed(1974)
                
                # Generate Data
                num = 20
                x, y = np.random.random((2, num))
                labels = np.random.choice(['a', 'b', 'c'], num)
                df = pd.DataFrame(dict(x=x, y=y, label=labels))
                
                groups = df.groupby('label')
                
                # Plot
                fig, ax = plt.subplots()
                ax.margins(0.05) # Optional, just adds 5% padding to the autoscaling
                for name, group in groups:
                    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12, label=name)
                ax.legend()
                
                plt.show()
                

                如果您希望事情看起来像默认的 pandas 样式,那么只需使用 pandas 样式表更新 rcParams 并使用它的颜色生成器。 (我也在稍微调整一下图例):

                import matplotlib.pyplot as plt
                import numpy as np
                import pandas as pd
                np.random.seed(1974)
                
                # Generate Data
                num = 20
                x, y = np.random.random((2, num))
                labels = np.random.choice(['a', 'b', 'c'], num)
                df = pd.DataFrame(dict(x=x, y=y, label=labels))
                
                groups = df.groupby('label')
                
                # Plot
                plt.rcParams.update(pd.tools.plotting.mpl_stylesheet)
                colors = pd.tools.plotting._get_standard_colors(len(groups), color_type='random')
                
                fig, ax = plt.subplots()
                ax.set_color_cycle(colors)
                ax.margins(0.05)
                for name, group in groups:
                    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12, label=name)
                ax.legend(numpoints=1, loc='upper left')
                
                plt.show()
                

                【讨论】:

                • 为什么在上面的 RGB 示例中,符号在图例中显示了两次?如何只显示一次?
                • @SteveSchulist - 使用ax.legend(numpoints=1) 仅显示一个标记。有两个,就像Line2D,通常有一条线连接两个标记。
                • 此代码仅在ax.plot() 命令后添加plt.hold(True) 后对我有效。知道为什么吗?
                • set_color_cycle() 在 matplotlib 1.5 中已弃用。现在有set_prop_cycle()。
                • 非常违反直觉,但谢谢!
                猜你喜欢
                • 1970-01-01
                • 2017-08-21
                • 2020-10-28
                • 1970-01-01
                • 1970-01-01
                • 2013-11-07
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多