python - 如何对旧/新值的列进行排序,以使第 i 个旧值 = 第 (i-1) 个新值
问题描述
编辑:欢迎标题建议。这可能有一个名字,但我不知道它是什么,也找不到类似的东西。
Edit2:我重写了这个问题,试图更清楚地解释它。在这两个版本中,我认为我已经通过提出解释、可重现的示例和我自己的解决方案来满足站点标准……如果您可以在否决之前提出改进建议,那将不胜感激。
我有用户从包含这三列的系统输入数据:
- 日期:
%Y-%m-%d %H:%M:%S
格式的时间戳;但是%S=00
对于所有情况 - old:这个观察值的旧值
- 新:这个观察的新值
如果用户在同一分钟内输入数据,那么仅按时间戳排序是不够的。我们最终会得到一个“大块”条目,这些条目的顺序可能正确也可能不正确。为了说明,我用整数替换了日期,并提出了一个正确而混乱的情况:
我们怎么知道数据的顺序是正确的?当每一行的值old
等于前一行的值时new
(忽略我们没有可比较的第一行/最后一行)。换一种说法:old_i = new_(i-1)
。这会在左侧创建匹配的对角线颜色,这些颜色在右侧是混乱的。
其他的建议:
- 可能有多个解决方案,因为两行可能具有相同的值
old
,new
因此可以互换 - 如果一个模棱两可的块本身出现(想象数据只是上面的行
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
下面调用的函数背后的代码,请参见我的要点。输出是正确的:
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]])
解决方案
这是一个图形问题。您可以使用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
数组。
但是,我仍然相信您应该调查您的时间戳。我相信这是一个更简单的问题,只需对时间戳进行排序即可解决。确保在读取/写入文件时不会丢失时间戳的精度。
推荐阅读
- javascript - 如何使用 DOM ul li 显示数组中的项目
- vue.js - Vue JS - 如果输入失去焦点,触发方法
- python - Python regex - 获取介于两者之间的内容
- r - 使用 ggplot2 在 ggsci 中选择颜色
- django - 如何使用 json 和 django 将 HTML 代码插入数据库?
- javascript - 当我想删除列表中的一项时,一切都会被删除 Expo, React Native
- javascript - JS:在 if 语句中向容器添加类
- c# - 2 维随机值数组,其中包含来自 asp.net 网络表单文本框的 2 个用户输入值
- android - 我希望我的 MainActivity.java 有一个按钮,点击该按钮时应该执行用 MapsActivity.java 编写的代码
- java - 用于下载压缩文件夹的 Java 代码