![](/img/trans.png)
[英]How can I compare part of the string of a cell on a row with another string on the same row and swap them places if they meet my conditions?
[英]How can I compare records row by row and remove one row that does not meet my condition?
我有一个看起来像这样的集合:
用户名 | 时间戳 | 房间钥匙 | 房门 |
---|---|---|---|
艾米123 | 2022 年 1 月 30 日 18:30:52 | 检索到 | 关闭 |
艾米123 | 2022年1月30日 18:31:30 | 检索到 | 打开 |
鲍勃1 | 2022年1月30日 18:32:30 | 检索到 | 打开 |
鲍勃1 | 2022 年 1 月 30 日 20:45:30 | 检索到 | 关闭 |
鲍勃1 | 2022 年 1 月 30 日 20:46:15 | 回来 | 关闭 |
我想要做的是只返回所有数据,但合并那些在一分钟内并由同一用户完成的数据。 例如 amy123 首先检索,然后打开并在 1 分钟内添加了这 2 条记录,因此我只想在该分钟内返回 amy123 的最新记录。 此外,即使 bob1 有一分钟内的记录,它是一个不同的用户,所以我也会展示它。
最终 output 应如下所示:
用户名 | 时间戳 | 房间钥匙 | 房门 |
---|---|---|---|
艾米123 | 2022年1月30日 18:31:30 | 检索到 | 打开 |
鲍勃1 | 2022年1月30日 18:32:30 | 检索到 | 打开 |
鲍勃1 | 2022 年 1 月 30 日 20:46:15 | 回来 | 关闭 |
如何在 mongodb 管道或使用 python json 中对其进行编码以执行上述操作?
我的想法是首先检查以前的记录是否是相同的用户名。 如果是同一个用户名,那么我会及时检查差异。 如果差<=1分钟,我将返回最新记录。 如果没有,我将返回所有记录。 但是,我不知道如何编码。
提前致谢!
这是一个解决方案,我使用groupby
和Grouper
将时间戳按2 分钟 windows分组
df = pd.DataFrame(
[
{"username": "amy123", "timestamp": "30 Jan 2022 18:30:52", "roomkey": "retrieved", "roomdoor": "closed"},
{"username": "amy123", "timestamp": "30 Jan 2022 18:31:30", "roomkey": "retrieved", "roomdoor": "opened"},
{"username": "bob1", "timestamp": "30 Jan 2022 18:32:30", "roomkey": "retrieved", "roomdoor": "opened"},
{"username": "bob1", "timestamp": "30 Jan 2022 20:45:30", "roomkey": "retrieved", "roomdoor": "closed"},
{"username": "bob1", "timestamp": "30 Jan 2022 20:46:15", "roomkey": "returned", "roomdoor": "closed"},
]
)
df.timestamp = pd.to_datetime(df.timestamp)
df = df.groupby(["username", pd.Grouper(key="timestamp", freq='2Min')]).last().reset_index()
df.drop_duplicates(keep="last")
这是 output。
用户名 | 时间戳 | 房间钥匙 | 房门 |
---|---|---|---|
艾米123 | 2022年1月30日 18:31:30 | 检索到 | 打开 |
鲍勃1 | 2022年1月30日 18:32:30 | 检索到 | 打开 |
鲍勃1 | 2022 年 1 月 30 日 20:46:15 | 回来 | 关闭 |
通过使用$reduce
作为“带有 state 的 for 循环”,这个问题得到了很好的解决。 考虑这个输入集:
{name: "amy", ts: new ISODate("2020-01-01T00:00:20Z"), o1:1, o2:"X1"}
,{name: "amy", ts: new ISODate("2020-01-01T00:00:30"), o1:2, o2:"X2"}
,{name: "amy", ts: new ISODate("2020-01-01T00:00:58"), o1:3, o2:"X3"}
,{name: "amy", ts: new ISODate("2020-01-01T00:01:15"), o1:31, o2:"X31"}
,{name: "amy", ts: new ISODate("2020-01-01T00:01:30"), o1:32, o2:"X32"}
,{name: "amy", ts: new ISODate("2020-01-01T00:02:00"), o1:4, o2:"X4"}
,{name: "amy", ts: new ISODate("2020-01-01T00:02:40"), o1:5, o2:"X5"}
,{name: "amy", ts: new ISODate("2020-01-01T00:04:00"), o1:65, o2:"X65"}
,{name: "amy", ts: new ISODate("2020-01-01T00:04:10"), o1:75, o2:"X75"}
,{name: "amy", ts: new ISODate("2020-01-01T00:20:35"), o1:86, o2:"X86"}
,{name: "amy", ts: new ISODate("2020-01-01T00:20:36"), o1:96, o2:"X96"}
,{name: "bob", ts: new ISODate("2020-01-01T00:00:30"), o1:7, o2:"X7"}
,{name: "bob", ts: new ISODate("2020-01-01T00:01:30"), o1:8, o2:"X8"}
,{name: "bob", ts: new ISODate("2020-01-01T00:01:35"), o1:9, o2:"X9"}
o1
和o2
是其他数据的占位符,不直接属于一分钟分桶方案。 它们将在解决方案中自动进行; 可以在不更改查询的情况下携带任意数量的任何类型的其他字段。
db.foo.aggregate([
// If appropriate, start with a $match stage here to cut down the amount
// of material, especially dates. You probably do not want 1 min buckets
// of everything from day 1. For now, no $match.
// Ensure everything going in date order....
{$sort: {ts:1}},
// Group by name and push the whole doc (which is in date order) onto
// array 'a':
{$group: {_id:"$name", a: {$push: "$$CURRENT"}}},
{$addFields: {a: {$let: {
// Get first element of a in prep for setting init value...
vars: {sd: {$arrayElemAt:["$a",0]}},
in: {$reduce: {
input: "$a",
initialValue: {
prev:"$$sd", // the whole doc
last:"$$sd.ts", // last anchor date, e.g. start of 60 interval
accum: []
},
in: {$cond: [
// If the next ts < 60000 millis beyond anchor..
{$lt:[{$subtract:["$$this.ts", "$$value.last"]}, 60000]},
// then capture it as prev but let last anchor
// and accumulated hits carry forward unchanged
{prev: "$$this",
last: "$$value.last", // carry
accum: "$$value.accum" // carry
},
// else capture it AND reset the anchor AND append the
// previous value to the accum array (because at the this
// point we "overran" the 60 interval). Note that
// $concatArrays wants arrays, not docs as inputs so we
// must wrap $$value.prev (which is a $$CURRENT doc) with []:
{prev: "$$this",
last: "$$this.ts", // reset last to this one
accum: {$concatArrays: [ "$$value.accum", ["$$value.prev"] ] }
}
]
}
}}
}}
}},
// The algo will always leave the very last value (which is ALWAYS one
// we take) "dangling" in prev, so here we simply do one more concat.
// We also take the oppty to both "lift" 'a.accum' to just 'a' and in so
// doing get rid of 'prev' and 'last':
{$addFields: {a: {$concatArrays: [ "$a.accum", [ "$a.prev" ]]} }}
]);
这产生:
{
"_id" : "amy",
"a" : [
{
"_id" : ObjectId("61f7e34ba565bb368b38e2cb"),
"name" : "amy",
"ts" : ISODate("2020-01-01T00:01:15Z"),
"o1" : 31,
"o2" : "X31"
},
{
"_id" : ObjectId("61f7e34ba565bb368b38e2cd"),
"name" : "amy",
"ts" : ISODate("2020-01-01T00:02:00Z"),
"o1" : 4,
"o2" : "X4"
},
{
"_id" : ObjectId("61f7e34ba565bb368b38e2ce"),
"name" : "amy",
"ts" : ISODate("2020-01-01T00:02:40Z"),
"o1" : 5,
"o2" : "X5"
},
{
"_id" : ObjectId("61f7e34ba565bb368b38e2d0"),
"name" : "amy",
"ts" : ISODate("2020-01-01T00:04:10Z"),
"o1" : 75,
"o2" : "X75"
},
{
"_id" : ObjectId("61f7e34ba565bb368b38e2d2"),
"name" : "amy",
"ts" : ISODate("2020-01-01T00:20:36Z"),
"o1" : 96,
"o2" : "X96"
}
]
}
{
"_id" : "bob",
"a" : [
{
"_id" : ObjectId("61f7e34ba565bb368b38e2d3"),
"name" : "bob",
"ts" : ISODate("2020-01-01T00:00:30Z"),
"o1" : 7,
"o2" : "X7"
},
{
"_id" : ObjectId("61f7e34ba565bb368b38e2d5"),
"name" : "bob",
"ts" : ISODate("2020-01-01T00:01:35Z"),
"o1" : 9,
"o2" : "X9"
}
]
}
如果您不想要或不需要通用解决方案,那么您可以推送子集文档而不是推送$$CURRENT
,例如
{$group: {_id:"$name", a: {$push: {ts: "$ts", o1: "$o1"}} }},
管道的 rest 保持不变——但您必须始终在a
数组中包含字段ts
才能正确驱动逻辑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.