繁体   English   中英

如何逐行比较记录并删除不符合我条件的行?

[英]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分钟,我将返回最新记录。 如果没有,我将返回所有记录。 但是,我不知道如何编码。

提前致谢!

这是一个解决方案,我使用groupbyGrouper将时间戳按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"}

o1o2是其他数据的占位符,不直接属于一分钟分桶方案。 它们将在解决方案中自动进行; 可以在更改查询的情况下携带任意数量的任何类型的其他字段。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM