【问题标题】:Compare many date ranges in google app engine datastore (Many to many, Python)比较谷歌应用引擎数据存储中的许多日期范围(多对多,Python)
【发布时间】:2012-06-22 17:27:37
【问题描述】:

我在谷歌应用引擎数据存储中有两个数据集。

class First_Set(db.Model):
  start_time = db.DateTimeProperty()
  end_time = db.DateTimeProperty()
  data1 = db.FloatProperty()
  ...

class Second_Set(db.Model):
  start_time = db.DateTimeProperty()
  end_time = db.DateTimeProperty()
  data2 = db.FloatProperty()
  ...

(他们有其他不同的数据,这就是他们在不同数据集中的原因。)

我想在两个数据集中找到所有重叠 start_time 和 end_time 的数据存储 ID,最好不要从一个数据集中提取结果并在另一个数据集中迭代第一个结果。

初始数据集的一个很好的可视化来自here(它也有在SQL中解决的问题):

1     |-----| 
2        |-----| 
3                 |--| 
4                       |-----| 
5                          |-----| 
6                                  |---| 
7                                        |---|  
8                           |---| 
9                                       |-----|

我需要的最终结果是(来自同一个example):

+----+---------------------+----+---------------------+ 
| id | start               | id | end                 | 
+----+---------------------+----+---------------------+ 
|  2 | 2008-09-01 15:02:00 |  1 | 2008-09-01 15:04:00 | 
|  5 | 2008-09-01 16:19:00 |  4 | 2008-09-01 16:23:00 | 
|  8 | 2008-09-01 16:20:00 |  4 | 2008-09-01 16:22:00 | 
|  8 | 2008-09-01 16:20:00 |  5 | 2008-09-01 16:22:00 | 
|  7 | 2008-09-01 18:18:00 |  9 | 2008-09-01 18:22:00 | 
+----+---------------------+----+---------------------+ 

以下示例中描述了 SQL 解决方案,但由于缺少 JOIN,我无法在数据存储中执行此操作:

SELECT v1.id, v1.start, v2.id, LEAST(v1.end,v2.end) AS end 
FROM visits v1 
JOIN visits v2 ON v1.id <> v2.id and v1.start >= v2.start and v1.start < v2.end  
ORDER BY v1.start;

我知道使用 ListProperty() 的一对多版本相当简单(来自this question)。

谁能想到找到重叠时间的解决方案(最好是在 Python 中)?

【问题讨论】:

    标签: python google-app-engine python-2.7 datastore date-range


    【解决方案1】:

    查看Marzullo's algorithm,它的时间效率为 O(n log n)。
    还有 many question on Stackoverflow 覆盖重叠区间,可用于解决您在 AppEngine 上的问题。

    【讨论】:

    • 甜,这实际上比我想象的更具可扩展性/可管理性,因为它适用于任意数量的数据集。谢谢,我正在使用这种方法发布我快速而肮脏的答案。
    【解决方案2】:

    感谢 Shay 的指导,发布我的解决方案而不使用 JOIN。应该能够在任意数量的数据集上找到重叠部分,只需稍作修改(至少理论上是这样)。

    我的 Python 不是很好,但下面应该给出这个想法:

    from operator import itemgetter
    
    class Find_Overlaps(webapp2.RequestHandler):
        def get(self):
            all_dates = []
            first_dates = db.GqlQuery("SELECT * FROM First_Set")
            for date in first_dates:
                row = {'dataset':'First_Set', 'dbkey':date.key(), 'offset':date.start_time, 'type': -1}
                all_dates.append(row)
                row = {'dataset':'First_Set', 'dbkey':date.key(), 'offset':date.end_time, 'type': 1}
                all_dates.append(row)
    
            second_dates = db.GqlQuery("SELECT * FROM Second_Set")
            for date in second_dates:
                row = {'dataset':'Second_Set', 'dbkey':date.key(), 'offset':date.start_time, 'type': -1}
                all_dates.append(row)
                row = {'dataset':'Second_Set', 'dbkey':date.key(), 'offset':date.end_time, 'type': 1}
                all_dates.append(row)
    
            newlist = sorted(all_dates, key=itemgetter('offset','type'))
            number_datasets = 2 #goal is to find overlaps in all sets not only the best overlaps, that's why this is needed
            loopcnt = 0
            update_bestend = 0
            overlaps = []
            for row in newlist: #Below is mostly from Marzullo's alghorithm
                loopcnt = loopcnt - row['type']#this is to keep track of overall tally
                if update_bestend == 1:
                    if loopcnt == (number_datasets - 1):
                        bestend = row['offset']
                        end_set = row['dataset']
                        end_key = row['dbkey']
                        overlaps.append({'start':beststart,'start_set':start_set,'start_key':start_key,'end':bestend,'end_set':end_set,'end_key':end_key})
                        update_bestend = 0
                if loopcnt == number_datasets:
                    beststart = row['offset']
                    start_set = row['dataset']
                    start_key = row['dbkey']
                    update_bestend = 1
    
            for overlap in overlaps: #just to see what the outcome is
                self.response.out.write('start: %s, start_set: %s, end: %s, end_set: %s<br>' % (overlap['start'], overlap['start_set'], overlap['end'], overlap['end_set']))
    

    【讨论】:

    • 对于大型数据集,您可能会在应用引擎中遇到此问题。每个查询将返回最多 1000 个结果,因此如果您的集合中有超过 1000 个条目,您将遇到问题。您可以执行多个查询以获取完整集,但如果该过程需要很长时间,您也可能会遇到请求超时。在这种情况下,您可能需要使用后端或 mapreduce 来处理操作。
    猜你喜欢
    • 1970-01-01
    • 2011-10-03
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 1970-01-01
    • 2013-05-17
    相关资源
    最近更新 更多