Python pandas sort_values()与嵌套列表

3
我想通过pandas在Python中对一个嵌套字典进行排序。
import pandas as pd 

# Data structure (nested list):
# {
#   category_name: [[rank, id], ...],
#   ...
# }

all_categories = {
    "category_name1": [[2, 12345], [1, 32512], [3, 32382]],
    "category_name2": [[3, 12345], [9, 25318], [1, 24623]]
}

df = pd.DataFrame(all_categories.items(), columns=['Category', 'Rank'])
df.sort_values(['Rank'], ascending=True, inplace=True) # this only sorts the list of lists

有谁能告诉我如何达成我的目标?我搞不清楚。 通过panda,可以使用sort_values()按第二列进行排序,但我无法弄清如何对嵌套的dict/list进行排序。 我想按排名升序排列,而不是按id排列。

1
你需要向我们展示一些样本数据。 - BENY
这部分让我困惑:all_categories['Rank'][i][rank] - Avishka Dambawinna
我改了一下,我明白你为什么感到困惑。我的意思是按照排名而不是ID进行排序,基于数据结构示例。 - Patrick
4个回答

5

最快的方法是应用sort()函数(请注意,排序是就地进行的,因此在这种情况下不要将其重新赋值给df.Rank):

df.Rank.apply(list.sort)

或者使用带有自定义键sorted() 方法,并将结果赋值给 df.Rank

df.Rank = df.Rank.apply(lambda row: sorted(row, key=lambda x: x[0]))

无论哪种情况都会输出:

>>> df
         Category                                  Rank
0  category_name1  [[1, 32512], [2, 12345], [3, 32382]]
1  category_name2  [[1, 24623], [3, 12345], [9, 25318]]

这是sort()sorted()explode()的性能测试结果,使用perfplot工具绘制:

timing results

import perfplot

def explode(df):
    df = df.explode('Rank')
    df['rank_num'] = df.Rank.str[0]
    df = df.sort_values(['Category', 'rank_num']).groupby('Category', as_index=False).agg(list)
    return df

def apply_sort(df):
    df.Rank.apply(list.sort)
    return df

def apply_sorted(df):
    df.Rank = df.Rank.apply(lambda row: sorted(row, key=lambda x: x[0]))
    return df

perfplot.show(
    setup=lambda n: pd.concat([df] * n),
    n_range=[2 ** k for k in range(25)],
    kernels=[explode, apply_sort, apply_sorted],
    equality_check=None,
)

使用 str.len()loc[] 来遮盖行并按列表长度筛选行:
mask = df.Rank.str.len().ge(10)
df.loc[mask, 'Rank'].apply(list.sort)

1
非常感谢您的见解和perfplot。我只是好奇:您知道如何忽略所有具有少于N=10个条目的“Rank”条目。 - Patrick
1
@Patrick 不用谢。要按长度过滤列表,您可以使用 str.len()loc[] 掩码行(已更新答案)。 - tdy
再次感谢,但是代码没有计算或仅显示n>10的条目,输出与之前相同。我的意思是len(all_categories['Rank']) > 10 - Patrick
@Patrick 正确,当前代码仅将排序限制在掩码行中,但仍保留所有行。如果您想要删除其他行,可以尝试使用以下代码:df = df.loc[mask]; df.Rank.apply(list.sort) - tdy

1

尝试

df = pd.DataFrame(all_categories.items(), columns=['Category', 'Rank']).explode('Rank')
df['Rank'] = df['Rank'].apply(lambda x: sorted(x))

df = df.groupby('Category').agg(list).reset_index()

转为字典

dict(df.agg(list, axis=1).values)

0

尝试:

df = pd.DataFrame(all_categories.items(), columns=['Category', 'Rank'])
df.set_index('Rank', inplace=True)
df.sort_index(inplace=True)
df.reset_index(inplace=True)

或者:

df = pd.DataFrame(all_categories.items(), columns=['Category', 'Rank'])
df = df.set_index('Rank').sort_index().reset_index()

它无法工作,与上面的结果相同,如果我对列表的列表进行排序。它甚至没有按id排序。 - Patrick

0

使用df.explode然后对值进行排序会更加高效,因为它可以向量化处理。

df = df.explode('Rank')
df['rank_num'] = df.Rank.str[0]

df.sort_values(['Category', 'rank_num'])
  .groupby('Category', as_index=False)
  .agg(list)

输出

         Category                                  Rank   rank_num
0  category_name1  [[1, 32512], [2, 12345], [3, 32382]]  [1, 2, 3]
1  category_name2  [[1, 24623], [3, 12345], [9, 25318]]  [1, 3, 9]

我进行了一些计时,发现在这种情况下 explodeapply 慢(我猜是因为 explode 仍需要 groupby + agg)。 - tdy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接