这是一个关于为一组并行数据生成图像或任何其他表示的问题。不是关于绘图或 GUI 编程,而是计算位置。
首先,我将解释一下我现在所处的位置,第二张图片和示例显示了我的问题。
当前状态
exampleOne-Easy http://www.wargsang.de/text3935.png
我有一个一维的对象,但它们是通过将它们放在平行的“线”上来对齐的。让我们称这种一维对象为“事件”,它以“持续时间”为时间单位。
这些事件有一个变体,什么都不发生,对象没有数据但有持续时间;一个“间隙”对象。
所以我们得到了一个由事件和间隙组成的模拟对象的时间表,作为三个对象列表很容易处理。
可视化也很简单:遍历列表并根据其持续时间绘制每个对象。
class Event():
def __init__(self, duration, displacement = 0): #displacement is explained in the second example and the core problem of this question
self.duration = duration
self.displacement = displacement
#additional data
def self.draw(self, start_coordinate):
"""draw duration * 10 pixels in black"""
#drawing code using start_coordinate to place the drawn object. see example graphic
return duration * 10
class Gap():
def __init__(self, duration, displacement = 0):
self.duration = duration
self.displacement = displacement
#no data
def self.draw(self, start_coordinate):
"""draw duration * 10 pixels in transparent"""
#drawing code using start_coordinate to place the drawn object. see example graphic
return duration * 10
row_one = [Event(1), Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(2)]
row_two = [Event(1), Gap(2), Event(1), Event(1), Gap(1), Event(1), Gap(1), ]
row_thr = [Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(3),]
timetable = [row_one, row_two, row_thr]
for row in timetable:
pixelcounter = 0 # the current position.
for item in row:
ret = item.draw(pixelcounter) #draw on the current position. Get how width the item was
pixelcounter += ret #save width for the next iteration
#instructions to move the "drawing cursor" down a few pixels so the next row does not overlap.
问题
现在到了问题。有些对象需要图形空间但持续时间为零。
我称之为“位移”。
exampleTwo-Problematic http://www.wargsang.de/text4120.png
或者我们需要有持续时间但也有位移的对象。
当我们只有一行时,这仍然不是问题,但同步行更复杂,我没有解决方案。
在上图中,红色块的持续时间为零并被置换。蓝色方块有持续时间,也会被置换。
示例 :
*想象一个 session 时间表,每小时有不同的演讲者席位(我们的持续时间席位)。每行代表一个不同的 session 室。
任务是找到一种方法来从上面的函数计算像素计数器,以便它单独对每一行都是正确的,而且一行中的位移会影响所有其他行并在那里创建额外的空间。
目标是每行中的持续时间是固定的和对齐的。任何应该从(例如,单位计数 4)开始的事件或间隙都应该从相同的绝对位置开始。
这意味着任何零持续时间/位移对象都从真实的时间点/持续时间开始,但不消耗任何时间/持续时间本身,因此所有后续项目都以完全相同的持续时间开始,直到包括下一个实际持续时间事件。从另一个角度来看,这也意味着零持续时间的项目总是在具有持续时间的事件之前开始。
在图片中,我们可以在第 2 列中看到一个相当简单的情况,这也意味着这开始了第二个持续时间槽。尽管该列中有三个真实事件向右移动,因为那里有一个置换项。
第 4 列有一个也有位移的持续时间项目。同样,从插槽 5 开始的所有项目都向右移动。
Colum 6 是最有趣也是我真正的问题,我在这里找不到解决方案。同样,第 6 列中的所有真实事件都向右移动并仍然同时开始。但是这里我们有 a) 两行的位移对象和两个 b) 两个彼此紧随其后的对象。
因此,对于真实事件来说,知道完整的位移很重要,但对于第三行中的第二个对象来说,知道在它之前还有一个位移项也很重要。
警告 :图形表示可能建议基于表格的方法,其中每列都有单独的宽度。但这就是这个例子结束的地方。实际应用处理每个事件 300-10,000 的常见持续时间,但持续时间为 1 不太可能,但技术上是可行的。所以表格的列宽为一个持续时间。考虑到我们进入了数十万个完整的持续时间(乘以行数),这可能会拖累性能。
这张图片的数据看起来像这样。如何使用此数据绘制第二张图像?或者需要改变什么,我愿意接受所有建议。
非常感谢您的时间和兴趣。如果您不知道解决方案,请不要犹豫,向我提问或向我展示我的概念的缺陷,这也会帮助我思考。
row_one = [ Event(1), #1
Event(0,1), Event(1), #2
Gap(1), #3
Event(1), #4
Gap(1), #5
Event(0,1), Event(1), #6
Event(1), #7
]
row_two = [ Event(1), #1
Event(1), #2
Gap(1), #3
Event(1, 0.5), #4, 0,5 is not important. we can also simply to just ints. The important bit is that it has both values.
Event(1), #5
Event(1), #6
Event(1), #7
]
row_thr = [ Event(1), #1
Event(1), #2
Event(1), #3
Event(1), #4
Event(1), #5
Event(0,1), Event(0,1), Event(1), #6 #Please pay attention to this case.
Event(1), #7
]
最佳答案
我不完全确定,但我认为你想要的是:
如果您希望结束时间同步,您必须解释如何;但我看不出明显的方式。
然后,您将获得原始问题的以下解决方案(大写 = 事件,小写字母 = 位移,点 = 间隙,空格 =“等待同步”,数字是事件的开始时间):
0 123 4 567
AbC.D .e FG
A B.CcD EF
A BCD EfgHI
您可以在以下示例中看到结束时间未同步:
0 12
AAa
Aa B
AaaB
还有一个更大的随机示例:
11 11 1 1 1 1 1 1 22 22 2 2 22 22 33 333 3 3 3 3 3 4 44444 4 4 4 45
01 2 34 5678 9 01 23 4 5 6 7 8 9 01 23 4 5 67 89 01 234 5 6 7 8 9 0 12345 6 7 8 90
AAAA BB CCC dd.. EEe Fff.. GGGGGg .... ... HHH .... IIii JJJ ... KKK LLLLl
abbbCCC DDDDDdd .. EEEEE Fff GGG HHH IIIii JJJjj KKKK LLLl Mno. PPP qR SSSSSs TT uuuVV
... AAAAA BBB CC DDDD ... EE FFFF GHhhIIII JJ. K Lll.m.... NNNO ....
...... AAAA .. .... BBB CCCCCc DDDDDd Ee FFFFff G hhhIIIII JJJ KLLLLLll M
.. AAA BBBCcc DD EE .. FFF gH IIIIIi J KKk LL MMMMM NNNNNn OOo PPQQQQ rrr...
AAAAa . BBBBbb CCCCC DDDDDd eeeFFFFF GG HH ..... IIIII JJ K LM.NNNNN .
AAAaaBBB CCCcc DDDDDdd EeFF ... GGgHHHH III JJJJ KKK llMMMm nnnOOOO PPPp ... Q
AAAAA BBBBB CCCC ..... DDD EEEEE FFFff .... GGGG HHHHhh II.... j . .
AAAaa.. BBBBbb CccDDDDD .... EEE .F GgghhhII Jj KKKK ... ... LLll ... MMMM N OooP
.... Aa ..BCCC ..... DDD EEEe FFf ..... GGGG HIIIIIii . JJ .... KKk LL
AAAAAa bbC..... DDDDD .... eeFFFFff GGGGG ... hh IIJJJ KKK L MMMMMmmNNNN
..aBBB CCCCc ..... ..... ... D. E FFFFFff ggHHhiiiJKKKk LLLLL mmmNNNOP Q RRR
AA BbCCCC DD Ee FFFFFff GGGGG HH IIIi JjjK.. LLLll MMMMmm .... . NNNOOOOOoo P
AB CCCCC ..... ddEEEE fffGgg HHHHHhh II jjKKKK LLLL MMMM nn.. OO PPPPPpp QQQQQqq
AAA BBB CCCC DDdd EE FFF gggHh IIIii JJJJ K LLLLl MMm NNOOOO . PP .QQQRRRRR
现在是代码(抱歉,它太长了,有趣的部分请看
Timetable.__init__
,其余大部分是 pretty-print )。from heapq import merge
from itertools import groupby, cycle, chain
from collections import defaultdict
from operator import attrgetter
from string import ascii_uppercase
# events are processed in this order:
# increasing start time, displacements (duration=0) first, and grouped by row_id
ev_sort_attrs = attrgetter("time", "duration", "row_id")
class Event:
def __init__(self, duration, displacement=0, visible=True, time=None, row_id=None):
self.duration = duration
self.displacement = displacement
self.visible = visible
self.time = time
self.row_id = row_id
self.pos = None
def draw(self, char):
return char * self.duration + char.lower() * self.displacement
def __lt__(self, other):
return ev_sort_attrs(self) < ev_sort_attrs(other)
def Gap(duration):
return Event(duration, visible=False)
class Timetable(list):
def __init__(self, *args):
""" compute positions for a list of rows of events """
list.__init__(self, *args)
# compute times for the events, and give them row_ids
for i, row in enumerate(self):
t = 0
for ev in row:
ev.time = t
t += ev.duration
ev.row_id = i
# map times to position for displacements and event
t2pos_disp = defaultdict(int) # maps times to position of synchronized start of displacements
t2pos_ev = defaultdict(int) # maps times to position of synchronized start of events and gaps
# the real work is done in the following loop
t_prev = 0
for t, g in groupby(merge(*self), key=attrgetter("time")):
# different times should have a minimum distance corresponding to their difference
t2pos_ev[t] = t2pos_disp[t] = max(t2pos_ev[t], t2pos_ev[t_prev] + t - t_prev)
t_prev = t
for (duration, row_id), g_row in groupby(g, key=attrgetter("duration", "row_id")): # process all displacements first, then the events
pos_ev = t2pos_ev[t] if duration > 0 else t2pos_disp[t] # events and displacements start at different
for ev in g_row:
ev.pos = pos_ev
pos_ev += ev.duration + ev.displacement
t2pos_ev[t + ev.duration] = max(t2pos_ev[t + ev.duration], pos_ev)
# keep our results...
self.t2pos_ev = t2pos_ev
self.t2pos_disp = t2pos_disp
@staticmethod
def str_row(row):
""" draw row, uppercase letters for real events, lower case letters for
displacements, dots for gaps"""
ev_chars = cycle(ascii_uppercase)
out = []
l = 0
for ev in row:
if ev.pos > l:
out.append(" " * (ev.pos - l))
out.append(ev.draw(next(ev_chars) if ev.visible else "."))
l = ev.pos + len(out[-1])
return "".join(out)
def __str__(self):
max_t, max_p = max(self.t2pos_ev.items())
w = len(str(max_t))
header_temp = [" " * w] * (max_p + 1)
for t, p in self.t2pos_ev.items():
header_temp[p] = "%*d" % (w, t)
headers = ("".join(header) for header in zip(*header_temp))
rows = (self.str_row(row) for row in self)
return "\n".join(chain(headers, rows))
if __name__ == "__main__":
# original example
row_one = [Event(1), Event(0,1), Event(1), Gap(1), Event(1), Gap(1), Event(0,1), Event(1), Event(1)]
row_two = [Event(1), Event(1), Gap(1), Event(1, 1), Event(1), Event(1), Event(1)]
row_thr = [Event(1), Event(1), Event(1), Event(1), Event(1), Event(0,1), Event(0,1), Event(1), Event(1)]
timetable = Timetable([row_one, row_two, row_thr])
print(timetable)
print("-" * 80)
# short example, shows ending times are not synchronized
print(Timetable([[Event(2, 1)], [Event(1, 1), Event(1)], [Event(1, 2), Event(1)]]))
print("-" * 80)
# larger random example
def random_row(l):
import random
res = []
t = 0
while t < l:
x = random.random()
if x < 0.1: res.append(Event(0, random.randint(1, 3)))
elif x < 0.8: res.append(Event(min(random.randint(1, 5), l - t), random.randint(0, 1) * random.randint(0, 2)))
else: res.append(Gap(min(random.randint(1, 5), l - t)))
t += res[-1].duration
return res
print(Timetable([random_row(50) for _ in range(15)]))
关于python - 从消耗时间的项目和不消耗时间但仍需要绘制空间的项目生成基于时间线的线性表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8439763/