【问题标题】:Pandas - Alternative to rank() function that gives unique ordinal ranks for a columnPandas - rank() 函数的替代方法,它为列提供唯一的序数排名
【发布时间】:2017-02-04 01:08:36
【问题描述】:

此时我正在编写一个 Python 脚本,用于聚合来自多个 Excel 工作表的数据。我选择使用的模块是 Pandas,因为它速度快且易于处理 Excel 文件。该问题仅与 Pandas 的使用有关,而我试图创建一个附加列,其中包含 唯一、仅整数、序数组内的排名。

我的 Python 和 Pandas 知识有限,因为我只是一个初学者。

目标

我正在尝试实现以下数据结构。排名前 10 位的 adwords 广告根据其在 Google 中的位置进行垂直排名。为了做到这一点,我需要在原始数据(见表 2 和表 3)中创建一个列,其中包含一个不包含重复值的纯整数排名。

表 1:我想要实现的数据结构

    device  , weeks   , rank_1   , rank_2   , rank_3   , rank_4   , rank_5
    mobile  , wk 1    , string   , string   , string   , string   , string 
    mobile  , wk 2    , string   , string   , string   , string   , string 
    computer, wk 1    , string   , string   , string   , string   , string
    computer, wk 2    , string   , string   , string   , string   , string

问题

我遇到的确切问题是无法使用 pandas 有效地对行进行排名。我已经尝试了很多事情,但我似乎无法以这种方式对其进行排名。

表 2:我拥有的数据结构

    weeks    device   , website  , ranking  , adtext
    wk 1     mobile   , url1     , *2.1     , string
    wk 1     mobile   , url2     , *2.1     , string
    wk 1     mobile   , url3     , 1.0      , string
    wk 1     mobile   , url4     , 2.9      , string
    wk 1     desktop  , *url5    , 2.1      , string
    wk 1     desktop  , url2     , *1.5     , string
    wk 1     desktop  , url3     , *1.5     , string
    wk 1     desktop  , url4     , 2.9      , string
    wk 2     mobile   , url1     , 2.0      , string
    wk 2     mobile   , *url6    , 2.1      , string
    wk 2     mobile   , url3     , 1.0      , string
    wk 2     mobile   , url4     , 2.9      , string
    wk 2     desktop  , *url5    , 2.1      , string
    wk 2     desktop  , url2     , *2.9     , string
    wk 2     desktop  , url3     , 1.0      , string
    wk 2     desktop  , url4     , *2.9     , string

表 3:我似乎无法创建的表

    weeks    device   , website  , ranking  , adtext  , ranking
    wk 1     mobile   , url1     , *2.1     , string  , 2
    wk 1     mobile   , url2     , *2.1     , string  , 3
    wk 1     mobile   , url3     , 1.0      , string  , 1
    wk 1     mobile   , url4     , 2.9      , string  , 4
    wk 1     desktop  , *url5    , 2.1      , string  , 3
    wk 1     desktop  , url2     , *1.5     , string  , 1
    wk 1     desktop  , url3     , *1.5     , string  , 2
    wk 1     desktop  , url4     , 2.9      , string  , 4
    wk 2     mobile   , url1     , 2.0      , string  , 2
    wk 2     mobile   , *url6    , 2.1      , string  , 3
    wk 2     mobile   , url3     , 1.0      , string  , 1
    wk 2     mobile   , url4     , 2.9      , string  , 4
    wk 2     desktop  , *url5    , 2.1      , string  , 2
    wk 2     desktop  , url2     , *2.9     , string  , 3
    wk 2     desktop  , url3     , 1.0      , string  , 1
    wk 2     desktop  , url4     , *2.9     , string  , 4

标准 .rank(ascending=True) 给出重复值的平均值。但是由于我使用这些等级来垂直组织它们,所以这行不通。

df = df.sort_values(['device', 'weeks', 'ranking'], ascending=[True, True, True])

df['newrank'] = df.groupby(['device', 'week'])['ranking'].rank( ascending=True)

.rank(method="dense", ascending=True) 保持重复值,也没有解决我的问题

df = df.sort_values(['device', 'weeks', 'ranking'], ascending=[True, True, True])

df['newrank'] = df.groupby(['device', 'week'])['ranking'].rank( method="dense", ascending=True)

.rank(method="first", ascending=True) 抛出一个 ValueError

df = df.sort_values(['device', 'weeks', 'ranking'], ascending=[True, True, True])

df['newrank'] = df.groupby(['device', 'week'])['ranking'].rank( method="first", ascending=True)

附录:如果我能找到一种方法在列中添加排名,我会使用 pivot 以下列方式转置表格。

df = pd.pivot_table(df, index = ['device', 'weeks'], columns='website', values='adtext', aggfunc=lambda x: ' '.join(x))

我的问题

我希望你们中的任何人都可以帮助我找到解决此问题的方法。这可以是一个有效的排名脚本,也可以是其他帮助我达到最终数据结构的东西。

谢谢!

塞巴斯蒂安


编辑:不幸的是,我认为我在原始帖子中并不清楚。我正在寻找一个仅给出整数且没有重复值的序数排名。这意味着当存在重复值时,它会随机给一个比另一个更高的排名。

所以我想做的是生成一个排名,用每组的序数值标记每一行。这些组基于周数和设备。我想用这个排名创建一个新列的原因是我可以每周和设备进入前 10 名。

Steven G 也让我举个例子。我在这里提供了。

示例数据可以直接粘贴到python中

!重要提示:此示例中的名称不同。数据框称为占位符,列名称如下:'week'、'website'、'share'、'rank_google'、'device'。

data = {u'week': [u'WK 1', u'WK 2', u'WK 3', u'WK 4', u'WK 2', u'WK 2', u'WK 1',
u'WK 3', u'WK 4', u'WK 3', u'WK 3', u'WK 4', u'WK 2', u'WK 4', u'WK 1', u'WK 1',
u'WK3', u'WK 4', u'WK 4', u'WK 4', u'WK 4', u'WK 2', u'WK 1', u'WK 4', u'WK 4',
u'WK 4', u'WK 4', u'WK 2', u'WK 3', u'WK 4', u'WK 3', u'WK 4', u'WK 3', u'WK 2',
u'WK 2', u'WK 4', u'WK 1', u'WK 1', u'WK 4', u'WK 4', u'WK 2', u'WK 1', u'WK 3',
u'WK 1', u'WK 4', u'WK 1', u'WK 4', u'WK 2', u'WK 2', u'WK 2', u'WK 4', u'WK 4',
u'WK 4', u'WK 1', u'WK 3', u'WK 4', u'WK 4', u'WK 1', u'WK 4', u'WK 3', u'WK 2',
u'WK 4', u'WK 4', u'WK 4', u'WK 4', u'WK 1'],
u'website': [u'site1.nl', u'website2.de', u'site1.nl', u'site1.nl', u'anothersite.com',
u'url2.at', u'url2.at', u'url2.at', u'url2.at', u'anothersite.com', u'url2.at',
u'url2.at', u'url2.at', u'url2.at', u'url2.at', u'anothersite.com', u'url2.at',
u'url2.at', u'url2.at', u'url2.at', u'anothersite.com', u'url2.at', u'url2.at',
u'anothersite.com', u'site2.co.uk', u'sitename2.com', u'sitename.co.uk', u'sitename.co.uk',
u'sitename2.com', u'sitename2.com', u'sitename2.com', u'url3.fi', u'sitename.co.uk',
u'sitename2.com', u'sitename.co.uk', u'sitename2.com', u'sitename2.com', u'ulr2.se',
u'sitename2.com', u'sitename.co.uk', u'sitename2.com', u'sitename2.com', u'sitename2.com',
u'sitename2.com', u'sitename2.com', u'sitename.co.uk', u'sitename.co.uk', u'sitename2.com',
u'facebook.com', u'alsoasite.com', u'ello.com', u'instagram.com', u'alsoasite.com', u'facebook.com',
u'facebook.com', u'singleboersen-vergleich.at', u'facebook.com', u'anothername.com', u'twitter.com',
u'alsoasite.com', u'alsoasite.com', u'alsoasite.com', u'alsoasite.com', u'facebook.com', u'alsoasite.com',
u'alsoasite.com'],
'adtext': [u'site1.nl 3,9 | < 10\xa0%', u'website2.de 1,4 | < 10\xa0%', u'site1.nl 4,3 | < 10\xa0%',
u'site1.nl 3,8 | < 10\xa0%', u'anothersite.com 2,5 | 12,36 %', u'url2.at 1,3 | 78,68 %', u'url2.at 1,2 | 92,58 %',
u'url2.at 1,1 | 85,47 %', u'url2.at 1,2 | 79,56 %', u'anothersite.com 2,8 | < 10\xa0%', u'url2.at 1,2 | 80,48 %',
u'url2.at 1,2 | 85,63 %', u'url2.at 1,1 | 88,36 %', u'url2.at 1,3 | 87,90 %', u'url2.at 1,1 | 83,70 %',
u'anothersite.com 3,1 | < 10\xa0%', u'url2.at 1,2 | 91,00 %', u'url2.at 1,1 | 92,11 %', u'url2.at 1,2 | 81,28 %'
, u'url2.at 1,1 | 86,49 %', u'anothersite.com 2,7 | < 10\xa0%', u'url2.at 1,2 | 83,96 %', u'url2.at 1,2 | 75,48 %'
, u'anothersite.com 3,0 | < 10\xa0%', u'site2.co.uk 3,1 | 16,24 %', u'sitename2.com 2,3 | 34,85 %',
u'sitename.co.uk 3,5 | < 10\xa0%', u'sitename.co.uk 3,6 | < 10\xa0%', u'sitename2.com 2,1 | < 10\xa0%',
u'sitename2.com 2,2 | 13,55 %', u'sitename2.com 2,1 | 47,91 %', u'url3.fi 3,4 | < 10\xa0%',
u'sitename.co.uk 3,1 | 14,15 %', u'sitename2.com 2,4 | 28,77 %', u'sitename.co.uk 3,1 | 22,55 %',
u'sitename2.com 2,1 | 17,03 %', u'sitename2.com 2,1 | 24,46 %', u'ulr2.se 2,7 | < 10\xa0%',
u'sitename2.com 2,0 | 49,12 %', u'sitename.co.uk 3,0 | < 10\xa0%', u'sitename2.com 2,1 | 40,00 %',
u'sitename2.com 2,1 | < 10\xa0%', u'sitename2.com 2,2 | 30,29 %', u'sitename2.com 2,0 |47,48 %',
u'sitename2.com 2,1 | 32,17 %', u'sitename.co.uk 3,2 | < 10\xa0%', u'sitename.co.uk 3,1 | 12,77 %',
u'sitename2.com 2,6 | < 10\xa0%', u'facebook.com 3,2 | < 10\xa0%', u'alsoasite.com 2,3 | < 10\xa0%',
u'ello.com 1,8 | < 10\xa0%',u'instagram.com 5,0 | < 10\xa0%', u'alsoasite.com 2,2 | < 10\xa0%',
u'facebook.com 3,0 | < 10\xa0%', u'facebook.com 3,2 | < 10\xa0%', u'singleboersen-vergleich.at 2,6 | < 10\xa0%',
u'facebook.com 3,4 | < 10\xa0%', u'anothername.com 1,9 | <10\xa0%', u'twitter.com 4,4 | < 10\xa0%',
u'alsoasite.com 1,1 | 12,35 %', u'alsoasite.com 1,1 | 11,22 %', u'alsoasite.com 2,0 | < 10\xa0%',
u'alsoasite.com 1,1| 10,86 %', u'facebook.com 3,4 | < 10\xa0%', u'alsoasite.com 1,1 | 10,82 %',
u'alsoasite.com 1,1 | < 10\xa0%'],
u'share': [u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'12,36 %', u'78,68 %',
u'92,58 %', u'85,47 %', u'79,56 %', u'< 10\xa0%', u'80,48 %', u'85,63 %', u'88,36 %',
u'87,90 %', u'83,70 %', u'< 10\xa0%', u'91,00 %', u'92,11 %', u'81,28 %', u'86,49 %',
u'< 10\xa0%', u'83,96 %', u'75,48 %', u'< 10\xa0%', u'16,24 %', u'34,85 %', u'< 10\xa0%',
u'< 10\xa0%', u'< 10\xa0%', u'13,55 %', u'47,91 %', u'< 10\xa0%', u'14,15 %', u'28,77 %',
u'22,55 %', u'17,03 %', u'24,46 %', u'< 10\xa0%', u'49,12 %', u'< 10\xa0%', u'40,00 %',
u'< 10\xa0%', u'30,29 %', u'47,48 %', u'32,17 %', u'< 10\xa0%', u'12,77 %', u'< 10\xa0%',
u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%',
u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'< 10\xa0%', u'12,35 %', u'11,22 %', u'< 10\xa0%',
u'10,86 %', u'< 10\xa0%', u'10,82 %', u'< 10\xa0%'],
u'rank_google': [u'3,9', u'1,4', u'4,3', u'3,8', u'2,5', u'1,3', u'1,2', u'1,1', u'1,2', u'2,8',
u'1,2', u'1,2', u'1,1', u'1,3', u'1,1', u'3,1', u'1,2', u'1,1', u'1,2', u'1,1', u'2,7', u'1,2',
u'1,2', u'3,0', u'3,1', u'2,3', u'3,5', u'3,6', u'2,1', u'2,2', u'2,1', u'3,4', u'3,1', u'2,4',
u'3,1', u'2,1', u'2,1', u'2,7', u'2,0', u'3,0', u'2,1', u'2,1', u'2,2', u'2,0', u'2,1', u'3,2',
u'3,1', u'2,6', u'3,2', u'2,3', u'1,8', u'5,0', u'2,2', u'3,0', u'3,2', u'2,6', u'3,4', u'1,9',
u'4,4', u'1,1', u'1,1', u'2,0', u'1,1', u'3,4', u'1,1', u'1,1'],
u'device': [u'Mobile', u'Tablet', u'Mobile', u'Mobile', u'Tablet', u'Mobile', u'Tablet', u'Computer',
u'Mobile', u'Tablet', u'Mobile', u'Computer', u'Tablet', u'Tablet', u'Computer', u'Tablet', u'Tablet',
u'Tablet', u'Mobile', u'Computer', u'Tablet', u'Computer', u'Mobile', u'Tablet', u'Tablet', u'Mobile',
u'Tablet', u'Mobile', u'Computer', u'Computer', u'Tablet', u'Mobile', u'Tablet', u'Mobile', u'Tablet',
u'Mobile', u'Mobile', u'Mobile', u'Tablet', u'Computer', u'Tablet', u'Computer', u'Mobile', u'Tablet',
u'Tablet', u'Tablet', u'Mobile', u'Computer', u'Mobile', u'Computer', u'Tablet', u'Tablet', u'Tablet',
u'Mobile', u'Mobile', u'Tablet', u'Mobile', u'Mobile', u'Tablet', u'Mobile', u'Mobile', u'Computer',
u'Mobile', u'Tablet', u'Mobile', u'Mobile']}

placeholder = pd.DataFrame(data)

我在使用带有 method='first' 的 rank() 函数时收到错误

C:\Users\username\code\report-creator>python recomp-report-04.py
Traceback (most recent call last):
  File "recomp-report-04.py", line 71, in <module>
    placeholder['ranking'] = placeholder.groupby(['week', 'device'])['rank_googl
e'].rank(method='first').astype(int)
  File "<string>", line 35, in rank
  File "C:\Users\sthuis\AppData\Local\Continuum\Anaconda2\lib\site-packages\pand
as\core\groupby.py", line 561, in wrapper
    raise ValueError
ValueError

我的解决方案

实际上,答案由@Nickil Maveli 给出。非常感谢您!不过,我认为概述我最终如何整合解决方案可能是明智的。

Rank(method='first') 是获得序数排名的好方法。但由于我正在处理以欧洲方式格式化的数字,pandas 将它们解释为字符串并且无法以这种方式对它们进行排名。我通过 Nickil Maveli 的反应得出了这个结论,并试图单独对每个组进行排名。我是通过以下代码做到的。

for name, group in df.sort_values(by='rank_google').groupby(['weeks', 'device']):
    df['new_rank'] = group['ranking'].rank(method='first').astype(int)

这给了我以下错误:

ValueError: first not supported for non-numeric data

所以这帮助我意识到我应该将列转换为浮点数。我就是这样做的。

# Converting the ranking column to a float
df['ranking'] = df['ranking'].apply(lambda x: float(unicode(x.replace(',','.'))))

# Creating a new column with a rank
df['new_rank'] = df.groupby(['weeks', 'device'])['ranking'].rank(method='first').astype(int)

# Dropping all ranks after the 10
df = df.sort_values('new_rank').groupby(['weeks', 'device']).head(n=10)

# Pivotting the column
df = pd.pivot_table(df, index = ['device', 'weeks'], columns='new_rank', values='adtext', aggfunc=lambda x: ' '.join(x))

# Naming the columns with 'top' + number
df.columns = ['top ' + str(i) for i in list(df.columns.values)]

所以这对我有用。谢谢各位!

【问题讨论】:

  • 我使用的另一种没有成功的方法如下。 grouped = df.sort_values(by='ranking').groupby(['device', 'weeks']) df['new_ranking'] = grouped.count() 灵感来源:link
  • 您应该在问题中的一行代码中给我们一个 df 示例,以便人们可以使用它
  • @Steven,我在帖子中添加了一个示例。这是你的意思吗?
  • 您的帖子很清楚,但是当您有一行代码创建该 df 的示例以便人们可以打开 python 并粘贴它并操作该 df 以获取您的内容时,我们总是很感激正在找。我不能简单地将您的 df 示例复制粘贴到您的代码中,因为它不是这样写的 df = pd.DataFrame(..)
  • 嘿@Steven,我已经部分调整了示例。我需要更改帖子本身的名称,但我现在有点担心。因此我稍后会这样做,因为我不想弄乱代码。

标签: python pandas ranking rank ordinal


【解决方案1】:

我认为您在排序后尝试使用method=first 对它们进行排名的方式导致了问题。

您可以简单地在分组对象本身上使用带有 first arg 的 rank 方法,为每个组提供所需的唯一排名。

df['new_rank'] = df.groupby(['weeks','device'])['ranking'].rank(method='first').astype(int)
print (df['new_rank'])

0     2
1     3
2     1
3     4
4     3
5     1
6     2
7     4
8     2
9     3
10    1
11    4
12    2
13    3
14    1
15    4
Name: new_rank, dtype: int32

执行枢轴操作:

df = df.pivot_table(index=['weeks', 'device'], columns=['new_rank'],
                    values=['adtext'], aggfunc=lambda x: ' '.join(x))

选择与排名数字相关的多索引列的第二级:

df.columns = ['rank_' + str(i) for i in df.columns.get_level_values(1)]
df


数据:(复制)

df = pd.DataFrame({'weeks': ['wk 1', 'wk 1', 'wk 1', 'wk 1', 'wk 1', 'wk 1', 'wk 1', 'wk 1',
                             'wk 2', 'wk 2', 'wk 2', 'wk 2', 'wk 2', 'wk 2', 'wk 2', 'wk 2'],
                  'device': ['mobile', 'mobile', 'mobile', 'mobile', 'desktop', 'desktop', 'desktop', 'desktop',
                             'mobile', 'mobile', 'mobile', 'mobile', 'desktop', 'desktop', 'desktop', 'desktop'],
                  'website': ['url1', 'url2', 'url3', 'url4', 'url5', 'url2', 'url3', 'url4',
                             'url1', 'url16', 'url3', 'url4', 'url5', 'url2', 'url3', 'url4'],
                  'ranking': [2.1, 2.1, 1.0, 2.9, 2.1, 1.5, 1.5, 2.9, 
                              2.0, 2.1, 1.0, 2.9, 2.1, 2.9, 1.0, 2.9],
                  'adtext': ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string',
                             'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string']})

注意:method=first 按照它们在数组/系列中出现的顺序分配排名。

【讨论】:

  • 不幸的是我仍然得到一个 valueError。为了让事情更清楚,我将在我的原始帖子下的评论中添加一个实际示例。
  • 您的意思是说new_rank 的值不是按显示的顺序排列的吗?但这似乎与您预期的DF 中发布的顺序相同。关于值错误,您必须发布问题中包含的完整回溯。
  • 嘿@Nickil,我在最后的原始帖子中添加了错误。我不确定 new_rank 的值是否不在显示的顺序中,因为我还不能复制它。但是,我想知道 如何排名,这些序数排名是基于另一列的值还是排名它是否根据其他东西对行进行排名?我还要感谢您之前的所有努力!非常感谢!
  • 我编辑了帖子,向您展示了用于复制值的DFgroupby.rank 的运行方式没有任何问题。问题可能出在其他地方,因为您只提供了数据样本。为什么RankingImpressionShare 中有时会有额外的逗号?由于那些不正确的解析,我无法模拟它。
  • 嘿@Nickil,这是德语符号。我认为这是我收到错误的原因。在以另一种方式尝试之后,我得到了一个更详细的错误,如下所示。 ValueError: first not supported for non-numeric data 所以当我想通了之后,我决定研究如何将列转换为浮点数!我会及时通知你们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-23
  • 2023-04-04
  • 2020-11-24
  • 2011-12-30
  • 2014-07-19
  • 1970-01-01
相关资源
最近更新 更多