如何排列旧/新值的列,以使第i个旧值=第(i-1)个新值

2024-04-19 21:41:57 发布

您现在位置:Python中文网/ 问答频道 /正文

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


Edit2:我重写了这个问题,试图把它解释得更清楚。在这两个版本中,我认为我已经通过提出一个解释、可复制的示例和我自己的解决方案满足了站点标准。。。如果您能在投票前提出改进建议,我们将不胜感激。你知道吗


我从包含以下三列的系统中输入了用户数据:

  • 日期:%Y-%m-%d %H:%M:%S格式的时间戳;但是%S=00适用于所有情况
  • 旧:这一观察的旧价值
  • 新:这一观察的新价值

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

correct-vs-jumbled

我们如何知道数据的顺序正确?当每一行的old值等于前一行的new值时(忽略第一行/最后一行中没有可比较的内容)。换句话说:old_i = new_(i-1)。这将在左侧创建匹配的对角线颜色,而在右侧则是杂乱无章的。你知道吗

其他意见:

  • 可能有多个解决方案,因为两行的oldnew的值可能相同,因此可以互换
  • 如果一个模棱两可的块本身出现(假设数据是上面date=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函数背后的代码,请参见我的要点here。输出正确:

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]])

Tags: the数据importdfnewdatevalueas
1条回答
网友
1楼 · 发布于 2024-04-19 21:41:57

这是一个图形问题。可以使用networkx创建图形,然后使用numpy进行操作。一个简单的遍历算法,比如depth-first search,将从一个源开始访问所有的边。你知道吗

源只是您的第一个节点(即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。您可能需要更改一些小细节,例如,如果您的组没有相互连接。但是,如果起点是在groupdate上排序的df,则对old_i = new_(i-1)的依赖关系在组间是受尊重的,那么只需重新分配ordered数组就可以了。你知道吗

不过,我仍然认为你应该调查你的时间戳。我相信这是一个简单的问题,可以通过排序时间戳来解决。在读取/写入文件时,请确保时间戳的精度不会降低。你知道吗

相关问题 更多 >