【问题标题】:Perform value counts in Python/Pandas on one column, but return values in multiple columns在 Python/Pandas 中对一列执行值计数,但在多列中返回值
【发布时间】:2025-11-26 00:00:01
【问题描述】:

在 Python 中,我试图在 Pandas 列上执行 value_counts。我可以让它工作,但我不知道如何获得其他一些相关的列。
代码:

import pandas as pd

myframe = pd.DataFrame({"Server":["Server_1","Server_1","Server_1","Server_1","Server_1","Server_2","Server_2","Server_2","Server_2","Server_3","Server_3","Server_3","Server_3","Server_3"], 
"CVE_ID":["CVE-2017-1111","CVE-2017-1112","CVE-2017-1113","CVE-2017-1114","CVE-2017-1115","CVE-2017-1111","CVE-2017-1112","CVE-2017-1113","CVE-2017-1114","CVE-2017-1113","CVE-2017-1114","CVE-2017-1115","CVE-2017-1116","CVE-2017-1117"],
"VulnName":["Java Update 1","Java Update 2","Java Update 3","Adobe 1","Chrome 1","Java Update 1","Java Update 2","Java Update 3","Adobe 1","Java Update 3","Adobe 1","Chrome 1","Chrome 2","Chrome 3"],
"ServerOwner":["Alice","Alice","Alice","Alice","Alice","Bob","Bob","Bob","Bob","Carol","Carol","Carol","Carol","Carol"]})

print "The dataframe: \n", myframe
print "Top 10 offending CVEs, Vulnerability and Count: \n"
print myframe['CVE_ID'].value_counts()

最后一行打印出两列:一列 CVE,一列是它发生的次数。但我想打印出类似这样的内容,其中维护了 CVE 和漏洞名称之间的链接(见中间列):

Top 10 offending CVEs, Vulnerability and Count:
CVE-2017-1113   Java Update 1     3
CVE-2017-1114   Java Update 2     3
...etc...

我该怎么做?我所做的一切都不断抛出错误。

【问题讨论】:

    标签: python pandas count row


    【解决方案1】:

    编辑:更改为在输出中具有列名访问权限

    (请注意在 [1] 中添加了 as_index=False.reset_index,参见来源 5 和 6

    [1] 在CVE_ID 列上的第一个groupby 并使用size

    counts = myframe.groupby(['CVE_ID','VulnName','ServerOwner'], as_index=False).size().unstack(fill_value=0).reset_index()
    
    
    ServerOwner         CVE_ID       VulnName  Alice  Bob  Carol
    0            CVE-2017-1111  Java Update 1      1    1      0
    1            CVE-2017-1112  Java Update 2      1    1      0
    2            CVE-2017-1113  Java Update 3      1    1      1
    3            CVE-2017-1114        Adobe 1      1    1      1
    4            CVE-2017-1115       Chrome 1      1    0      1
    5            CVE-2017-1116       Chrome 2      0    0      1
    6            CVE-2017-1117       Chrome 3      0    0      1
    

    [2] 然后对 Alice、Bob 和 Carol 列求和得到:

    counts['Count'] = counts[['Alice','Bob','Carol']].sum(axis=1)
    
    ServerOwner         CVE_ID       VulnName  Alice  Bob  Carol  Count
    0            CVE-2017-1111  Java Update 1      1    1      0      2
    1            CVE-2017-1112  Java Update 2      1    1      0      2
    2            CVE-2017-1113  Java Update 3      1    1      1      3
    3            CVE-2017-1114        Adobe 1      1    1      1      3
    4            CVE-2017-1115       Chrome 1      1    0      1      2
    5            CVE-2017-1116       Chrome 2      0    0      1      1
    6            CVE-2017-1117       Chrome 3      0    0      1      1
    

    [3] 然后在names上使用df.drop删除名称列:

    counts.drop(['Carol','Bob','Alice'],inplace=True,axis=1)
    
    ServerOwner         CVE_ID       VulnName  Count
    0            CVE-2017-1111  Java Update 1      2
    1            CVE-2017-1112  Java Update 2      2
    2            CVE-2017-1113  Java Update 3      3
    3            CVE-2017-1114        Adobe 1      3
    4            CVE-2017-1115       Chrome 1      2
    5            CVE-2017-1116       Chrome 2      1
    6            CVE-2017-1117       Chrome 3      1
    

    [4] 然后在sum 列上使用sort_values

    counts.sort_values(by='Count', ascending=False, inplace=True)
    
    ServerOwner         CVE_ID       VulnName  Count
    2            CVE-2017-1113  Java Update 3      3
    3            CVE-2017-1114        Adobe 1      3
    0            CVE-2017-1111  Java Update 1      2
    1            CVE-2017-1112  Java Update 2      2
    4            CVE-2017-1115       Chrome 1      2
    5            CVE-2017-1116       Chrome 2      1
    6            CVE-2017-1117       Chrome 3      1
    

    综合:

    counts = myframe.groupby(['CVE_ID','VulnName','ServerOwner'], as_index=False).size().unstack(fill_value=0).reset_index()
    counts['Count'] = counts[['Alice','Bob','Carol']].sum(axis=1)
    counts.drop(['Carol','Bob','Alice'],inplace=True,axis=1)
    counts.sort_values(by='Count', ascending=False, inplace=True)
    
    print "The dataframe: \n", myframe
    print "Top 10 offending CVEs, Vulnerability and Count: \n"
    print counts
    
    Top 10 offending CVEs, Vulnerability and Count: 
    
    ServerOwner         CVE_ID       VulnName  Count
    2            CVE-2017-1113  Java Update 3      3
    3            CVE-2017-1114        Adobe 1      3
    0            CVE-2017-1111  Java Update 1      2
    1            CVE-2017-1112  Java Update 2      2
    4            CVE-2017-1115       Chrome 1      2
    5            CVE-2017-1116       Chrome 2      1
    6            CVE-2017-1117       Chrome 3      1
    

    如果需要,此时可以使用reset_index() 重置索引。

    编辑:针对serverOwner索引的评论,您可以重置索引、删除旧索引和重命名新索引:

    counts.reset_index(drop=True, inplace = True)
    counts.index.names = ['index']
    

    给予:

    ServerOwner         CVE_ID       VulnName  Count
    index                                           
    0            CVE-2017-1113  Java Update 3      3
    1            CVE-2017-1114        Adobe 1      3
    2            CVE-2017-1111  Java Update 1      2
    3            CVE-2017-1112  Java Update 2      2
    4            CVE-2017-1115       Chrome 1      2
    5            CVE-2017-1116       Chrome 2      1
    6            CVE-2017-1117       Chrome 3      1
    

    ServerOwner 名称保留为原始 groupby 命令的残余,以详细说明使用了哪一列。)

    此答案的来源:

    [1] Groupby value counts on the dataframe pandas 数据框-熊猫

    [2] Pandas: sum DataFrame rows for given columns

    [3] Delete column from pandas DataFrame

    [4] python, sort descending dataframe with pandas

    [5] Converting a Pandas GroupBy object to DataFrame

    [6] How to GroupBy a Dataframe in Pandas and keep Columns

    【讨论】:

    • 只是出于好奇,第一个 ServerOwner 列是什么,您如何摆脱它?我不能只显示 CVE_ID、VulnName 和 Count 列。 ServerOwner 是索引的新名称吗?
    • 我认为是。如果您执行reset_index(),它将添加到另一个索引列,但ServerOwner 仍将保留。我认为这是因为使用了group by 并使其等于一个新的数据框对象:我不确定这在技术上是否是保存它的正确方法,可能有点粗略。抱歉,我现在无法提供更多帮助 - 早上会看看(这里有点晚了)。但是,是的,我认为在某个阶段它会将Server owner 解释为您的索引。您可以尝试使用df.drop 在过程中的不同点删除该列,看看是否可以摆脱它。
    • @user3688402 查看我对索引解决方案的编辑。非常感谢您的支持和接受。很高兴我能帮忙:)
    【解决方案2】:

    使用join 附加value_counts

    myframe.join(myframe['CVE_ID'].value_counts().rename('Count'), on='CVE_ID')
    
               CVE_ID    Server ServerOwner       VulnName  Count
    0   CVE-2017-1111  Server_1       Alice  Java Update 1      2
    1   CVE-2017-1112  Server_1       Alice  Java Update 2      2
    2   CVE-2017-1113  Server_1       Alice  Java Update 3      3
    3   CVE-2017-1114  Server_1       Alice        Adobe 1      3
    4   CVE-2017-1115  Server_1       Alice       Chrome 1      2
    5   CVE-2017-1111  Server_2         Bob  Java Update 1      2
    6   CVE-2017-1112  Server_2         Bob  Java Update 2      2
    7   CVE-2017-1113  Server_2         Bob  Java Update 3      3
    8   CVE-2017-1114  Server_2         Bob        Adobe 1      3
    9   CVE-2017-1113  Server_3       Carol  Java Update 3      3
    10  CVE-2017-1114  Server_3       Carol        Adobe 1      3
    11  CVE-2017-1115  Server_3       Carol       Chrome 1      2
    12  CVE-2017-1116  Server_3       Carol       Chrome 2      1
    13  CVE-2017-1117  Server_3       Carol       Chrome 3      1
    

    如果要将其限制在前 n 位,(我的示例显示 2),请使用 headhow='inner'

    myframe.join(
        myframe['CVE_ID'].value_counts().head(2).rename('Count'),
        on='CVE_ID', how='inner')
    
               CVE_ID    Server ServerOwner       VulnName  Count
    2   CVE-2017-1113  Server_1       Alice  Java Update 3      3
    7   CVE-2017-1113  Server_2         Bob  Java Update 3      3
    9   CVE-2017-1113  Server_3       Carol  Java Update 3      3
    3   CVE-2017-1114  Server_1       Alice        Adobe 1      3
    8   CVE-2017-1114  Server_2         Bob        Adobe 1      3
    10  CVE-2017-1114  Server_3       Carol        Adobe 1      3
    

    【讨论】:

    • 伙计,总有一种超级简单的单行方式来做事,很好。
    • 您不需要减少CVE_ID 列以便不显示重复的ID吗?即你有三个1113 行,当只需要一个时,因为它是需要的计数,而不是所有者名称?