目前我正在编写一个聚合来自多个Excel工作表的数据的Python脚本。我选择使用的模块是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(升序=真),给出重复值的平均值。但是因为我使用这些等级来垂直地组织它们,所以这不起作用。
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)
ADDENDUM:如果我找到一种方法在列中添加排名,我会使用pivot来以下列方式转置表。
df = pd.pivot_table(df, index = ['device', 'weeks'], columns='website', values='adtext', aggfunc=lambda x: ' '.join(x))
我的问题
我希望你们中的任何一个能帮助我找到解决这个问题的方法。这可以是一个有效的排名脚本或其他东西,以帮助我达到最终的数据结构。
谢谢!
Sebastiaan
编辑:不幸的是,我认为我在原帖中并不清楚。我正在寻找一个只给出整数且没有重复值的序数排名。这意味着当存在重复值时,它将随机地给出一个比另一个更高的排名。所以我想要做的是生成一个排名,用每个组标记每个行的序数值。这些组基于周数和设备。我想创建一个具有此排名的新列的原因是我可以每周制作前十名和设备。
Steven G也问我一个可以玩的例子。我在这里提供了。
示例数据可以直接粘贴到python
中!重要提示:此示例中的名称不同。数据框称为占位符,列名称如下:“周”,“网站”,“共享”,“rank_google”,“设备”。
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)
我在使用rank ='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')是获得序数排名的好方法。但是由于我使用的是以欧洲方式格式化的数字,因此大熊猫将它们解释为字符串并且不能以这种方式对它们进行排名。我通过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)]
所以这对我有用。谢谢你们!
答案 0 :(得分:1)
我认为您在排序后尝试使用method=first
对它们进行排名的方式会导致问题。
您可以在分组对象本身上使用first
arg的排名方法,为每个组提供所需的唯一排名。
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
按照它们在数组/系列中出现的顺序分配排名。