熊猫:将宽数据帧重塑为多索引长

时间:2016-04-06 12:59:21

标签: python pandas

我有一个由许多个人评估者生成的照片评级数据集 每个评估者都有几个图像来评价,每个图像,评估者提供几个不同的评级,加上描述。

因此,举例来说,评分者可能会被要求评价3张不同的照片,并以0-5的比例提供单独的评分,以确定每张照片的快乐,悲伤和有趣。此外,要求评估者提供每张照片的简短文字说明。

同一张照片可能会被多个评分者评分,但并非所有照片都会被相同数量的评分者评分。

目前我的数据采用此格式(每个网址代表一张照片):

rater_id | url1 | url2 | url3 | rating_1(1) | rating_2(1) | rating_1(2) | rating_2(2)   | rating_1(3) | rating_2(3) | description(1) | description(2) | description(3)
     001 |   a  |   b  |   c  |     3.0     |     2.5     |     4.0     |     1.5       |     5.0     |     5.0     |  sunny day     |  rainy day     |  foggy day
     002 |   a  |   b  |   d  |     1.0     |     4.5     |     3.0     |     3.5       |     1.0     |     3.5     |  sunshine      |  rain          |  snow

我试图在这里实现一些变革。
首先,我想更改数据框,以便通过照片网址对其进行索引 - 将所有网址字段(url1,url2等)融合到一个长列url中。含义:

url1 | url2 | url3
-----|------|-----
  a  |   b  |  c

变为

url
---
 a
 b
 c

在每个url索引中,有一个rater_id的嵌套索引,然后有一行该评估者对给定照片的评级和描述。
例如:

                | rating_1 | rating_2 | description
url | rater_id
 a  |      001  |    3.0   |   2.5    |  sunny day
    |      002  |    1.0   |   4.5    |  sunshine
----|-----------|----------|----------|------------
 b  |      001  |    4.0   |   1.5    |  rainy day
    |      002  |    4.5   |   3.0    |  rain
----|-----------|----------|----------|------------
 c  |      001  |    5.0   |   5.0    |  foggy day
----|-----------|----------|----------|------------
 d  |      002  |    1.0   |   3.5    |  snow

最后,我想汇总每张照片网址的评分和说明: - 数字评级的均值和方差 - 以制表符分隔的所有描述字符串 - 对每张照片进行评分的评估者数量

例如:

url | rating_1_avg | rating_1_var | rating_2_avg | rating_2_var | all_descriptions      | total_ratings 
 a  |     2.0      |     2.0      |     3.0      |     2.0      | sunny day    sunshine |      2
----|--------------|--------------|--------------|--------------|-----------------------|--------------
 b  |     4.25     |     0.125    |     2.25     |     1.125    | rainy day    rain     |      2
----|--------------|--------------|--------------|--------------|-----------------------|--------------
 c  |     5.0      |     NA       |     5.0      |     NA       | foggy day             |      1
----|--------------|--------------|--------------|--------------|-----------------------|--------------
 d  |     1.0      |     NA       |     3.5      |     NA       | snow                  |      1

我使用Pandas reshaping工具尝试了多种方法,包括meltwide_to_long,但我无法弄清楚如何首先将照片网址设为长格式,然后创建一个嵌套索引来排列数据,如上所述。我对Pandas groupby和基本聚合感到很满意,但这有点超出了我的技能水平。非常感谢任何帮助!

注意:我在这些虚拟数据中给出的字段不是实际数据集中的确切名称,但它们完全遵循相同的命名约定。照片网址均为url1url2等,而评分字段则表示为rating_<rating_category_number>(<url_number>),例如。 rating_1(2)。描述字段表示为description(<url_number>),例如。 description(2)

这是构建初始数据集的Python代码:

df = pd.DataFrame({'id': {0: '001', 1: '002'},
                   'url1': {0: 'a', 1: 'a'},
                   'url2': {0: 'b', 1: 'b'},
                   'url3': {0: 'c', 1: 'd'}})

df['rating_1(1)'] = [3.0, 1]
df['rating_2(1)'] = [2.5, 4.5]
df['rating_1(2)'] = [4.0, 3]
df['rating_2(2)'] = [1.5, 3.5]
df['rating_1(3)'] = [5.0, 1]
df['rating_2(3)'] = [5.0, 3.5]
df['description(1)'] = ['sunny day','sunshine']
df['description(2)'] = ['rainy day','rain']
df['description(3)'] = ['foggy day','snow']

2 个答案:

答案 0 :(得分:2)

您可以先str.contains找到每个类别的列,然后使用不太知名的pd.lreshapemeanvarcountjoin的最后一次汇总列:

#select columns with each category
rat1 = df.columns[df.columns.str.contains(r'rating_1')].tolist()
print rat1
['rating_1(1)', 'rating_1(2)', 'rating_1(3)']

rat2 = df.columns[df.columns.str.contains(r'rating_2')].tolist()
url = df.columns[df.columns.str.contains(r'url')].tolist()
desc = df.columns[df.columns.str.contains(r'description')].tolist()

df =  pd.lreshape(df, {'rat1': rat1, 'rat2': rat2,'url': url,'desc': desc})
print df
  rater_id url  rat2  rat1       desc
0    '001'   a   2.5   3.0  sunny day
1    '002'   a   4.5   1.0   sunshine
2    '001'   b   1.5   4.0  rainy day
3    '002'   b   3.5   3.0       rain
4    '001'   c   5.0   5.0  foggy day
5    '002'   d   3.5   1.0       snow

#aggregate
df = df.groupby(['url']).agg({'rat1':['mean', 'var'],
                              'rat2':['mean', 'var'], 
                              'desc': ' '.join, 
                              'rater_id': 'count'})

#reset multiindex in columns
df.columns = ['_'.join(col) for col in df.columns.values]
print df
     rater_id_count  rat2_mean  rat2_var  rat1_mean  rat1_var  \
url                                                             
a                 2        3.5       2.0        2.0       2.0   
b                 2        2.5       2.0        3.5       0.5   
c                 1        5.0       NaN        5.0       NaN   
d                 1        3.5       NaN        1.0       NaN   

              desc_join  
url                      
a    sunny day sunshine  
b        rainy day rain  
c             foggy day  
d                  snow  

答案 1 :(得分:0)

我会做类似以下的事情

ids_url1 = ['id', 'rating_1(1)', 'rating_2(1)', 'rating_3(1)', 'description(1)']
ids_url2 = ['id', 'rating_1(2)', 'rating_2(2)', 'rating_3(2)', 'description(2)']
ids_url3 = ['id', 'rating_1(3)', 'rating_2(3)', 'rating_3(3)', 'description(3)']

df1 = pd.melt(df, id_vars=ids_url1, value_vars=['url1'])
df2 = pd.melt(df, id_vars=ids_url2, value_vars=['url2'])
df3 = pd.melt(df, id_vars=ids_url3, value_vars=['url3'])
df1.drop(axis=1, labels='variable', inplace=True)
df1.set_index(['value', 'id'], inplace=True)
df1.columns = ["rating_1", "rating_2", "rating_3", "description"]
df2.drop(axis=1, labels='variable', inplace=True)
df2.set_index(['value', 'id'], inplace=True)
df2.columns = ["rating_1", "rating_2", "rating_3", "description"]
df3.drop(axis=1, labels='variable', inplace=True)
df3.set_index(['value', 'id'], inplace=True)
df3.columns = ["rating_1", "rating_2", "rating_3", "description"]

dfn = pd.concat([df1,df2,df3], axis=0)

然后你可以根据需要做groupby并连接结果

dfn.groupby(axis=0, level=0).mean()

       rating_1  rating_2  rating_3
value
a           2.0       3.5       NaN
b           3.5       2.5       NaN
c           5.0       5.0       NaN
d           1.0       3.5       NaN

dfn.groupby(axis=0, level=0)['description'].apply(lambda x: " ".join(x))

value
a    sunny day sunshine
b        rainy day rain
c             foggy day
d                  snow
Name: description, dtype: object