确定15分钟周期数的代码

时间:2011-12-24 20:48:40

标签: python r

我有以下问题。我有一个数据集,其具有军事时间格式的旅行的开始(STRTTIME)和结束时间(ENDTIME)。我想弄清楚每15分钟时间增量的行程次数。我的目标是确定从0000到2359(96个时间片)的每个15分钟时间段内发生的旅行次数。我可以在excel中编写96个虚拟变量并执行它但我宁愿在R或Python中使用一些代码(我正在学习这两个,所以我的知识很简陋)。我可以放一个计数器,然后增加,但我不知道如何处理两个时间变量,发现自己打了一个deadend。我的例子如下。 Here is some sample data(CSV格式)。

  1. 假设行程从0805开始并在0840结束,那么每个15分钟的周期将具有以下值:
    • 0000-0015 - 0
    • 0015-0030 - 0
    • ....
    • 0800-0815 - 2/3
    • 0815-0830 - 1
    • 0830-0845 - 2/3
    • 0845-0900 - 0
    • ...
    • 2330-2345 - 0
    • 2345-2400 - 0
  2. 假设另一次旅行从0810开始,到0850结束,那么每个15分钟的时间段将具有以下值:
    • 0000-0015 - 0
    • 0015-0030 - 0
    • ....
    • 0800-0815 - 1/3
    • 0815-0830 - 1
    • 0830-0845 - 1
    • 0845-0900 - 1/3
    • ...
    • 2330-2345 - 0
    • 2345-2400 - 0
  3. 在处理这2个记录之后,15分钟周期虚拟字段中的值将如下所示(即它已将其增加了前一记录中字段的值):
    • 0000-0015 - 0
    • 0015-0030 - 0
    • ....
    • 0800-0815 - 1
    • 0815-0830 - 2
    • 0830-0845 - 5/3
    • 0845-0900 - 1/3
    • ...
    • 2330-2345 - 0
    • 2345-2400 - 0
  4. 非常感谢任何执行此操作的代码。

4 个答案:

答案 0 :(得分:4)

让我尝试以您所代表的确切方式呈现解决方案

  1. 首先让我们定义15分钟的时间范围。 Itertools.product用于在使用datetime strftime进行转换后创建使用time格式化的整个时间范围。

    timeset=[datetime.time(h,m).strftime("%H%M") for h,m in itertools.product(xrange(0,24),xrange(0,60,15))]+['2400']
    >>> timeset
    ['0000', '0015', '0030', '0045', '0100', '0115', '0130', '0145', '0200', '0215', '0230', '0245', '0300', '0315', '0330', '0345', '0400', '0415', '0430', '0445', '0500', '0515', '0530', '0545', '0600', '0615', '0630', '0645', '0700', '0715', '0730', '0745', '0800', '0815', '0830', '0845', '0900', '0915', '0930', '0945', '1000', '1015', '1030', '1045', '1100', '1115', '1130', '1145', '1200', '1215', '1230', '1245', '1300', '1315', '1330', '1345', '1400', '1415', '1430', '1445', '1500', '1515', '1530', '1545', '1600', '1615', '1630', '1645', '1700', '1715', '1730', '1745', '1800', '1815', '1830', '1845', '1900', '1915', '1930', '1945', '2000', '2015', '2030', '2045', '2100', '2115', '2130', '2145', '2200', '2215', '2230', '2245', '2300', '2315', '2330', '2345', '2400']
    
  2. 让我们定义一个计时器,一个与时间集长度相同但初始化为零的列表

    timekeeper=[0]*len(timeset)
    
  3. 为了简单起见,我将使用与您提供的XLS表相同的数据来定义一个元组,而不是从CSV中读取

    counter=[('1020','1050'),('0900','0930'),('1830','2000'),('2330','2350'),('1200','1202'),('1232','1234'),('1450','1635'),('1220','1440'),('0930','1205'),('1656','1730'),('1800','1850'),('1200','1210'),('1715','1727'),('1140','1215'),('1450','1500')]
    
  4. 以下功能是主处理器。我使用bisect来确定开始和结束时间序列。我还使用fraction来避免浮点并保持问题

    中描述的格式
    def TimeCounter(timekeeper,timeset,(sttime,entime)):
        st=bisect.bisect_left(timeset,sttime)
        en=bisect.bisect_left(timeset,entime)
        timekeeper[st]+=fractions.Fraction(int(timeset[st])-int(sttime),15)
        timekeeper[en]+=fractions.Fraction(int(entime)-int(timeset[en-1]),15)
        for i in xrange(st+1,en):
            timekeeper[i]+=1
    
  5. 最后,以下两个班轮将循环通过提供的计数器数据和每个数据序列的Call TimeCounter来更新计时器

    for c in counter:
        TimeCounter(timekeeper,timeset,c)
    
  6. 最终的o / p看起来像这样

    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Fraction(0, 1), 2, Fraction(2, 1), 2, 2, 2, Fraction(10, 3), 4, Fraction(8, 3), 2, 2, Fraction(8, 3), Fraction(4, 1), Fraction(64, 15), Fraction(4, 3), Fraction(64, 15), 2, 2, 2, 2, 2, 2, 2, Fraction(4, 3), Fraction(62, 3), 2, 2, 2, 2, 2, 2, Fraction(2, 3), Fraction(88, 15), Fraction(2, 1), Fraction(18, 5), 0, Fraction(0, 1), 2, Fraction(2, 1), 4, Fraction(8, 3), 2, 2, 2, Fraction(22, 3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Fraction(0, 1), 2, Fraction(2, 3)]
    
  7. 最后如果您不想以所描述的格式打印数据,可以使用此代码

    for i in xrange(0,len(timeset)-1):
        print '-'.join([timeset[i],timeset[i+1],str(timekeeper[i+1])])
    
  8. 这是最终显示语句的样本o / p

    1015-1030-10/3
    1030-1045-4
    1045-1100-8/3
    1100-1115-2
    1115-1130-2
    1130-1145-8/3
    1145-1200-4
    1200-1215-64/15
    1215-1230-4/3
    1230-1245-64/15
    

答案 1 :(得分:4)

由于R中还没有答案,我会为此添加一个。我觉得解决方案可能比python更优雅,但这是一个品味问题。

首先,我们必须阅读数据:

data <- read.csv('sample_data.csv')

然后,我想将时间转换为十进制格式。因此,我确实使用提供的小时和分钟而不是军事格式。但这不是问题,因为您总是可以使用简单的整数运算来转换值。

data <- cbind(data, start = data$STARTHR + data$STARTMIN/60, end= data$ENDHR + data$ENDMIN/60)

现在生成时间间隔(我们将通过它们的开始时间识别)

intervals <- seq(0, 23.75, by=0.25)

那部分有点棘手...... 首先,我们将检查哪些行程晚于我们的间隔结束。所有这些行程我们将分配1,在我们的间隔之前结束的行程我们将分配0.如果行程在间隔内结束,我们将使用指定0和1之间的相应分数。

endvalues <- (pmax(pmin(outer(data$end, intervals, FUN="-"), 0.25), 0) / 0.25)  

注意使用外部。这里,功能&#34; - &#34; (减法)用于结束时间和间隔矢量的所有组合。所有其他操作都是元素明智的。我建议您只是逐步测试操作,然后应该明白做了什么。

同样,我们将使用startintervals执行此操作,但现在我们将使用负号。

startvalues <- (pmax(pmin(-outer(data$start, intervals, FUN="-"), 0), -0.25) / 0.25)

这使我们能够生成一个矩阵,只要间隔完全包含在行程中,该矩阵就为1:

resultmatrix <- endvalues + startvalues

最后,我们可以总结所有行程并获得每个行程内的行程次数:

intervalcount <- apply(resultmatrix, 2, sum)

答案 2 :(得分:3)

由于您的目标是创建直方图,因此您有效地解决了“分箱数据”这一常见问题,但方式略有不同!

最简单的解决方案是首先创建一个从0到95的索引字典(如你所提到的96个切片)。每个代表15分钟的时间段。

单独处理每条记录,找到它们开始的索引,以及它们结束的索引。增加字典中这两个索引之间的每个值,以表示您在该时间点发生了一次旅行。

import csv
spamReader = csv.reader(open('sample_data.csv', 'rb'), delimiter=',')

histogram = dict()

def toMinutes(militaryTime):
    if type(militaryTime) != str:
        raise ValueError("requires string as arg")
    hours = int(militaryTime[:2])
    mins = int(militaryTime[2:])
    return 60*hours + mins

for record in spamReader:
    if record[0] == 'STRTTIME':
        continue #skip first record which contains headers
    startTime = toMinutes(record[0]) #must convert militarytime to minutes
    endTime = toMinutes(record[1])

    startIndex = int(int(startTime)/15.0) #int division in python 3.0 and 2.X
    endIndex = int(int(endTime)/15) #is handled different, this unifies the two

    for i in range(startIndex,endIndex+1):
        valAd = 1
        if i == startIndex:
           valAd = 1-((startTime-(15*i))/15.0)
        if i == endIndex:
           valAd = ((endTime-(15*i))/15.0) #opposite boundary condition
        histogram[i] = histogram.get(i,0) + valAd
for key,val in histogram.items():
    print key,val
'''
output from your example csv, in minutes, which can easily be converted to militaryTime
41 0.666666666667
42 1
43 0.333333333333
46 0.333333333333
47 1
48 1.8
49 0.666666666667
50 1.26666666667
51 1
52 1
53 1
54 1
55 1
56 1
57 1
58 0.666666666667
59 1.33333333333
60 1.0
61 1
62 1
63 1
64 1
65 1
66 0.333333333333
67 0.266666666667
68 1
69 1.8
70 0.0
72 1.0
73 1
74 2.0
75 1.33333333333
76 1
77 1
78 1
79 1
80 0.0
94 1.0
95 0.333333333333
360 1.0
361 1
362 1
363 1
364 1
365 1
366 1
367 1
368 1
369 1
370 1
371 1
372 0.0
'''

答案 3 :(得分:0)

我可能会误解问题的分数部分,因为我认为它是一种确定它是否应该计入整个"I want to figure out the number of 15 minute time increments the trip is taking place"的方法。如果这是你想要的,例如,10分钟以上并不算作时间增量,那么像这样的东西就可以用来做我刚刚引用的内容。

len([x for x in range(len(range(int('0000'), int('0215'), 15))) if x%7 < 4])
#outputs: 9

基本上,因为它是在军事时间,你可以把它作为一个int投射并生成一个15步的范围。这将生成一个列表,您可以在其中获取4个元素,丢弃3个元素,取4个元素,依此类推。因此,我采用长度并迭代一系列新对象以标准化为0,1,2,3,4等,并使用x%7&lt; 4做到这一点。

您可以创建一个传递两个字符串的函数。因此,在上文中,如果您将'0000'更改为'0010',则会返回8,因为您只需将10分钟缩短为15分钟之一。

如果您需要更多信息,只需要增加数量,也许您可​​以澄清您对我的最终需求。