我有以下问题。我有一个数据集,其具有军事时间格式的旅行的开始(STRTTIME)和结束时间(ENDTIME)。我想弄清楚每15分钟时间增量的行程次数。我的目标是确定从0000到2359(96个时间片)的每个15分钟时间段内发生的旅行次数。我可以在excel中编写96个虚拟变量并执行它但我宁愿在R或Python中使用一些代码(我正在学习这两个,所以我的知识很简陋)。我可以放一个计数器,然后增加,但我不知道如何处理两个时间变量,发现自己打了一个deadend。我的例子如下。 Here is some sample data(CSV格式)。
非常感谢任何执行此操作的代码。
答案 0 :(得分:4)
让我尝试以您所代表的确切方式呈现解决方案
首先让我们定义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']
让我们定义一个计时器,一个与时间集长度相同但初始化为零的列表
timekeeper=[0]*len(timeset)
为了简单起见,我将使用与您提供的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')]
以下功能是主处理器。我使用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
最后,以下两个班轮将循环通过提供的计数器数据和每个数据序列的Call TimeCounter来更新计时器
for c in counter:
TimeCounter(timekeeper,timeset,c)
最终的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)]
最后如果您不想以所描述的格式打印数据,可以使用此代码
for i in xrange(0,len(timeset)-1):
print '-'.join([timeset[i],timeset[i+1],str(timekeeper[i+1])])
这是最终显示语句的样本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分钟之一。
如果您需要更多信息,只需要增加数量,也许您可以澄清您对我的最终需求。