首页 > 解决方案 > 如何对旧/新值的列进行排序,以使第 i 个旧值 = 第 (i-1) 个新值

问题描述

编辑:欢迎标题建议。这可能有一个名字,但我不知道它是什么,也找不到类似的东西。


Edit2:我重写了这个问题,试图更清楚地解释它。在这两个版本中,我认为我已经通过提出解释、可重现的示例和我自己的解决方案来满足站点标准……如果您可以在否决之前提出改进建议,那将不胜感激。


我有用户从包含这三列的系统输入数据:

如果用户在同一分钟内输入数据,那么仅按时间戳排序是不够的。我们最终会得到一个“大块”条目,这些条目的顺序可能正确也可能不正确。为了说明,我用整数替换了日期,并提出了一个正确而混乱的情况:

正确与混乱

我们怎么知道数据的顺序是正确的?当每一行的值old等于前一行的值时new(忽略我们没有可比较的第一行/最后一行)。换一种说法:old_i = new_(i-1)。这会在左侧创建匹配的对角线颜色,这些颜色在右侧是混乱的。

其他的建议:

我的数据集要大得多,所以我的最终解决方案将涉及使用pandas.groupby()像上面这样的函数块。右侧将传递给函数,我需要返回左侧(或一些索引/顺序将我带到左侧)。


这是一个可重现的示例,使用与上面相同的数据,但添加了一个group列和另一个块,以便您可以看到我的groupby()解决方案。

设置和输入混乱的数据:

import pandas as pd
import itertools
df = pd.DataFrame({'group': ['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b'],
                   'date': [0, 1, 1, 1, 1, 2, 3, 4, 4],
                   'old': [1, 8, 2, 2, 5, 5, 4, 10, 7],
                   'new': [2, 5, 5, 8, 2, 4, 7, 1, 10]})
print(df)

### jumbled: the `new` value of a row is not the same as the next row's `old` value
#   group  date  old  new
# 0     a     0    1    2
# 1     a     1    8    5
# 2     a     1    2    5
# 3     a     1    2    8
# 4     a     1    5    2
# 5     a     2    5    4
# 6     b     3    4    7
# 7     b     4   10    1
# 8     b     4    7   10

我写了一个笨拙的解决方案,它要求一种更优雅的方法。有关我在order_rows下面调用的函数背后的代码,请参见我的要点。输出是正确的:

df1 = df.copy()
df1 = df1.groupby(['group'], as_index=False, sort=False).apply(order_rows).reset_index(drop=True)
print(df1)

### correct: the `old` value in each row equals the `new` value of the previous row
#   group date old new
# 0     a    0   1   2
# 1     a    1   2   5
# 2     a    1   5   2
# 3     a    1   2   8
# 4     a    1   8   5
# 5     a    2   5   4
# 6     b    3   4   7
# 7     b    4   7  10
# 8     b    4  10   1

根据networkx建议更新

请注意,上面的项目符号 #2 表明这些模棱两可的块可以在没有先前参考行的情况下发生。在这种情况下,喂食起点df.iloc[0]是不安全的。此外,我发现当用不正确的起点为图形播种时,它似乎只输出它可以成功排序的节点。请注意,传递了 5 行,但只返回了 4 个值。

例子:

import networkx as nx
import numpy as np

df = pd.DataFrame({'group': ['a', 'a', 'a', 'a', 'a'],
                   'date': [1, 1, 1, 1, 1],
                   'old': [8, 1, 2, 2, 5],
                   'new': [5, 2, 5, 8, 2]})

g = nx.from_pandas_edgelist(df[['old', 'new']], 
                            source='old', 
                            target='new', 
                            create_using=nx.DiGraph)

ordered = np.asarray(list(nx.algorithms.traversal.edge_dfs(g, df.old[0])))
ordered

# array([[8, 5],
#        [5, 2],
#        [2, 5],
#        [2, 8]])

标签: pythonpandassorting

解决方案


这是一个图形问题。您可以使用networkx来创建图表,然后numpy用于操作。一个简单的遍历算法,如深度优先搜索,将从源头开始访问所有边。

源只是你的第一个节点(即df.old[0]

以你的例子:

import networkx as nx

g = nx.from_pandas_edgelist(df[['old', 'new']], 
                            source='old', 
                            target='new', 
                            create_using=nx.DiGraph)

ordered = np.asarray(list(nx.algorithms.traversal.edge_dfs(g, df.old[0])))

>>>ordered
array([[ 1,  2],
       [ 2,  5],
       [ 5,  2],
       [ 2,  8],
       [ 8,  5],
       [ 5,  4],
       [ 4,  7],
       [ 7, 10],
       [10,  1]])

您可以只分配回您的数据框:df[['old', 'new']] = ordered. 您可能需要更改一些小细节,例如,如果您的组没有相互连接。但是,如果起点是排序的 df ongroup并且依赖关系是受尊重的组间date old_i = new_(i-1)那么您应该可以只分配回ordered数组。

但是,我仍然相信您应该调查您的时间戳。我相信这是一个更简单的问题,只需对时间戳进行排序即可解决。确保在读取/写入文件时不会丢失时间戳的精度。


推荐阅读