สร้างแผนภูมิบรรยายสไตล์ xkcd


45

ในหนึ่งในแถบ xkcd ที่เป็นสัญลักษณ์มากขึ้น Randall Munroe แสดงให้เห็นถึงเส้นเวลาของภาพยนตร์หลายเรื่องในแผนภูมิบรรยาย:

ป้อนคำอธิบายรูปภาพที่นี่ (คลิกเพื่อดูรุ่นที่ใหญ่กว่า)

ที่มา: xkcd เลขที่ 657

กำหนดสเปคของไทม์ไลน์ของภาพยนตร์ (หรือเรื่องเล่าอื่น ๆ ) คุณจะต้องสร้างแผนภูมิดังกล่าว นี่คือการประกวดความนิยมดังนั้นคำตอบที่ได้คะแนนมากที่สุด (สุทธิ) จะเป็นผู้ชนะ

ความต้องการขั้นต่ำ

หากต้องการกระชับ spec เล็กน้อยนี่คือชุดคุณลักษณะขั้นต่ำที่ทุกคำตอบต้องนำไปปฏิบัติ:

  • ใช้เป็นรายการชื่อตัวละครตามด้วยรายการเหตุการณ์ แต่ละเหตุการณ์เป็นรายการของอักขระที่กำลังจะตายหรือรายชื่อกลุ่มของอักขระ (แสดงว่าอักขระใดอยู่ด้วยกันในปัจจุบัน) นี่คือตัวอย่างหนึ่งสำหรับการบรรยายเรื่อง Jurassic Park ที่สามารถเข้ารหัสได้:

    ["T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", "Sattler", "Gennaro",
     "Hammond", "Kids", "Muldoon", "Arnold", "Nedry", "Dilophosaurus"]
    [
      [[0],[1,2,3],[4],[5,6],[7,8,10,11,12],[9],[13]],
      [[0],[1,2,3],[4,7,5,6,8,9,10,11,12],[13]],
      [[0],[1,2,3],[4,7,5,6,8,9,10],[11,12],[13]],
      [[0],[1,2,3],[4,7,5,6,9],[8,10,11,12],[13]],
      [[0,4,7],[1,2,3],[5,9],[6,8,10,11],[12],[13]],
      [7],
      [[5,9],[0],[4,6,10],[1,2,3],[8,11],[12,13]],
      [12],
      [[0, 5, 9], [1, 2, 3], [4, 6, 10, 8, 11], [13]], 
      [[0], [5, 9], [1, 2], [3, 11], [4, 6, 10, 8], [13]], 
      [11], 
      [[0], [5, 9], [1, 2, 10], [3, 6], [4, 8], [13]], 
      [10], 
      [[0], [1, 2, 9], [5, 6], [3], [4, 8], [13]], 
      [[0], [1], [9, 5, 6], [3], [4, 8], [2], [13]], 
      [[0, 1, 9, 5, 6, 3], [4, 8], [2], [13]], 
      [1, 3], 
      [[0], [9, 5, 6, 3, 4, 8], [2], [13]]
    ]
    

    เช่นบรรทัดแรกหมายความว่าที่จุดเริ่มต้นของแผนภูมิ T-Rex เป็นหนึ่งเดียวแร็พเตอร์ทั้งสามอยู่ด้วยกันมิลล์ส์อยู่คนเดียวแกรนท์และ Sattler อยู่ด้วยกัน ฯลฯ เหตุการณ์ที่สองถึงครั้งสุดท้ายหมายความว่าแร็พเตอร์สองคนตาย .

    คุณคาดหวังว่าการป้อนข้อมูลจะขึ้นอยู่กับคุณตราบเท่าที่สามารถระบุข้อมูลประเภทนี้ได้ เช่นคุณอาจใช้รูปแบบรายการที่สะดวก นอกจากนี้คุณยังสามารถคาดหวังว่าตัวละครในเหตุการณ์จะเป็นชื่อเต็มของตัวละครอีกครั้งเป็นต้น

    คุณอาจ (แต่ไม่จำเป็นต้อง) สมมติว่าแต่ละกลุ่มมีตัวละครที่มีชีวิตในแต่ละกลุ่ม อย่างไรก็ตามคุณไม่ควรสันนิษฐานว่ากลุ่มหรือตัวละครในเหตุการณ์เดียวนั้นอยู่ในลำดับที่สะดวกมาก

  • เรนเดอร์ไปที่หน้าจอหรือไฟล์ (เป็นกราฟิกแบบเวกเตอร์หรือแรสเตอร์) ซึ่งมีหนึ่งบรรทัดสำหรับแต่ละอักขระ แต่ละบรรทัดจะต้องมีป้ายกำกับด้วยชื่ออักขระที่จุดเริ่มต้นของบรรทัด

  • สำหรับแต่ละเหตุการณ์ปกติจะต้องมีบางส่วนของแผนภูมิที่กลุ่มอักขระมีความคล้ายคลึงกันอย่างชัดเจนตามความใกล้เคียงของแต่ละบรรทัด
  • สำหรับแต่ละเหตุการณ์ความตายบรรทัดของตัวละครที่เกี่ยวข้องจะต้องยุติในหยดที่มองเห็นได้
  • คุณไม่ต้องทำซ้ำคุณสมบัติอื่น ๆ ของแปลงของ Randall และคุณไม่ต้องทำซ้ำรูปแบบการวาดของเขา เส้นตรงที่มีการเลี้ยวที่คมชัดทั้งหมดเป็นสีดำโดยไม่มีป้ายกำกับเพิ่มเติมและชื่อเรื่องนั้นสมบูรณ์แบบมากที่จะเข้าร่วมการแข่งขัน ไม่จำเป็นต้องใช้พื้นที่อย่างมีประสิทธิภาพเช่นคุณอาจทำให้อัลกอริทึมของคุณง่ายขึ้นโดยเพียงแค่ลากเส้นลงด้านล่างเพื่อพบกับตัวละครอื่น ๆ ตราบใดที่ยังมีทิศทางของเวลา

ฉันได้เพิ่มโซลูชันอ้างอิงซึ่งตอบสนองความต้องการขั้นต่ำเหล่านี้ทั้งหมด

ทำให้มันสวย

นี่คือการประกวดความนิยมดังนั้นคุณอาจใช้สิ่งที่คุณต้องการ การเพิ่มที่สำคัญที่สุดคืออัลกอริธึมเลย์เอาต์ที่เหมาะสมซึ่งทำให้แผนภูมิมีความชัดเจนมากขึ้น - เช่นทำให้โค้งงอในเส้นที่ง่ายต่อการติดตามและลดจำนวนการข้ามเส้นที่จำเป็น นี่เป็นปัญหาขั้นตอนวิธีหลักของความท้าทายนี้! คะแนนโหวตจะตัดสินว่าอัลกอริทึมของคุณทำงานได้ดีเพียงใดในการรักษาแผนภูมิให้เป็นระเบียบ

แต่ต่อไปนี้เป็นแนวคิดเพิ่มเติมส่วนใหญ่มาจากแผนภูมิของ Randall:

การตกแต่ง:

  • เส้นสี
  • หัวเรื่องสำหรับพล็อต
  • การติดฉลากสิ้นสุดบรรทัด
  • จัดเรียงบรรทัดใหม่โดยอัตโนมัติซึ่งผ่านส่วนที่ไม่ว่าง
  • สไตล์วาดด้วยมือ (หรืออื่น ๆ ตามที่ฉันได้กล่าวไว้ว่าไม่จำเป็นต้องสร้างสไตล์ของแรนดัลหากคุณมีแนวความคิดที่ดีกว่า) สำหรับบรรทัดและแบบอักษร
  • การวางแนวที่ปรับแต่งได้ของแกนเวลา

ความหมายเพิ่มเติม:

  • เหตุการณ์ที่มีชื่อ / กลุ่ม / ความตาย
  • เส้นหายไปและปรากฏขึ้นอีกครั้ง
  • ตัวละครเข้ามาสาย
  • ไฮไลท์ที่ระบุคุณสมบัติ (สามารถถ่ายโอนได้?) ของตัวละคร (เช่นดู ringbearer ในแผนภูมิ LotR)
  • การเข้ารหัสข้อมูลเพิ่มเติมในแกนการจัดกลุ่ม (เช่นข้อมูลทางภูมิศาสตร์เช่นในแผนภูมิ LotR)
  • เดินทางข้ามเวลา
  • ความเป็นจริงทางเลือก?
  • ตัวละครเปลี่ยนเป็นอีกเรื่องหนึ่งหรือไม่?
  • ตัวละครสองตัวผสาน? (แยกตัวละคร?)
  • 3D? (ถ้าคุณไปไกลขนาดนั้นจริงๆโปรดตรวจสอบให้แน่ใจว่าคุณใช้มิติเพิ่มเติมเพื่อดูภาพที่น่าจดจำจริง ๆ !)
  • คุณสมบัติอื่น ๆ ที่เกี่ยวข้องซึ่งอาจเป็นประโยชน์ในการแสดงภาพเล่าเรื่องของภาพยนตร์ (หรือหนังสือ ฯลฯ )

แน่นอนว่าสิ่งเหล่านี้จำนวนมากต้องการข้อมูลเพิ่มเติมและคุณสามารถเพิ่มรูปแบบการป้อนข้อมูลของคุณได้ตามต้องการ แต่โปรดบันทึกว่าจะป้อนข้อมูลอย่างไร

โปรดรวมหนึ่งหรือสองตัวอย่างเพื่ออวดคุณสมบัติที่คุณใช้

โซลูชันของคุณควรจะสามารถจัดการกับอินพุตที่ถูกต้องได้ แต่มันก็ใช้ได้จริง ๆ ถ้ามันเหมาะกับเรื่องเล่าบางประเภทดีกว่าเรื่องอื่น ๆ

เกณฑ์การลงคะแนน

ฉันไม่มีภาพลวงตาที่ฉันสามารถบอกคนอื่นว่าพวกเขาควรใช้คะแนนของพวกเขาอย่างไร แต่นี่คือแนวทางที่แนะนำบางอย่างตามลำดับความสำคัญ:

  • Downvote คำตอบที่ใช้ประโยชน์จากช่องโหว่มาตรฐานหรืออื่น ๆ หรือเข้ารหัสอย่างน้อยหนึ่งผลลัพธ์
  • อย่าถอนคำตอบที่ไม่เป็นไปตามข้อกำหนดขั้นต่ำ (ไม่ว่าแฟนซีจะเหลือแค่ไหน)
  • ก่อนอื่นอัลกอริทึมการเลย์เอาต์ที่ดีขึ้น ซึ่งรวมถึงคำตอบที่ไม่ได้ใช้พื้นที่แนวตั้งจำนวนมากในขณะที่ย่อเล็กสุดข้ามเส้นเพื่อให้กราฟชัดเจนหรือจัดการการเข้ารหัสข้อมูลเพิ่มเติมลงในแกนตั้ง การมองภาพการจัดกลุ่มโดยไม่ทำให้ยุ่งเหยิงมากควรเป็นจุดสนใจหลักของความท้าทายนี้เช่นนี้เป็นการประกวดการเขียนโปรแกรมที่มีปัญหาอัลกอริทึมที่น่าสนใจ
  • โหวตคุณสมบัติที่เป็นตัวเลือกซึ่งเพิ่มพลังการแสดงออก (เช่นการตกแต่งที่ไม่บริสุทธิ์)
  • สุดท้ายนี้ขอเสนอการนำเสนอที่ดี

7
เพราะ code-golf มี xkcd ไม่เพียงพอ
ภูมิใจ Haskeller

8
@proudhaskeller PPCG ไม่สามารถมี xkcd ได้เพียงพอ ;) แต่ฉันไม่คิดว่าเราได้พยายามที่จะท้าทายความสามารถของกราฟิกข้อมูล / วิชวลไลเซชันของเขาดังนั้นฉันหวังว่าฉันจะนำสิ่งใหม่ ๆ มาสู่โต๊ะด้วยสิ่งนี้ และฉันมั่นใจว่าคนอื่น ๆ บางคนจะสร้างความแตกต่างและความท้าทายที่น่าสนใจเช่นกัน
Martin Ender

ไม่เป็นไรถ้าวิธีแก้ปัญหาของฉันจัดการเฉพาะคนโกรธ 12 คน, Duel (Spielberg, 1971, คนขับรถยนต์ธรรมดากับคนขับรถบรรทุกที่บ้าคลั่ง) และเครื่องบินรถไฟและรถยนต์? ;-)
เลเวลริเวอร์เซนต์

4
ฉันสงสัยว่าการป้อนข้อมูลสำหรับไพรเมอร์จะมีลักษณะเช่น ...
โจชัว

1
@ ใช่ใช่นั่นเป็นความคิด หากกิจกรรมมีรายการเพิ่มเติมนั่นคือการจัดกลุ่มรายการ ดังนั้น[[x,y,z]]จะหมายความว่าตัวละครทั้งหมดอยู่ด้วยกันในปัจจุบัน แต่ถ้าเหตุการณ์ไม่ได้มีรายการ แต่มีเพียงตัวละครโดยตรงมันเป็นความตายดังนั้นในสถานการณ์เดียวกัน[x,y,z]หมายความว่าตัวละครทั้งสามนั้นตาย อย่าลังเลที่จะใช้รูปแบบอื่นโดยมีข้อบ่งชี้ชัดเจนว่ามีบางสิ่งบางอย่างที่เป็นความตายหรือเหตุการณ์การจัดกลุ่มหากสิ่งนั้นช่วยคุณได้ รูปแบบข้างต้นเป็นเพียงข้อเสนอแนะ ตราบใดที่รูปแบบการป้อนข้อมูลของคุณมีความหมายอย่างน้อยที่สุดคุณสามารถใช้อย่างอื่นได้
Martin Ender

คำตอบ:


18

Python3 ที่มี numpy, scipy และ matplotlib

จูราสสิคพาร์ค

แก้ไข :

  • ฉันพยายามที่จะให้กลุ่มอยู่ในตำแหน่งที่สัมพันธ์กันระหว่างเหตุการณ์ดังนั้นsorted_eventฟังก์ชั่น
  • ฟังก์ชั่นใหม่ในการคำนวณตำแหน่ง y ของตัวละคร ( coords)
  • เหตุการณ์ที่มีชีวิตทุกอันถูกพล็อตสองครั้งในตอนนี้
  • เพิ่มเลเบลคำอธิบายแผนภูมิและแกนนำออก
import math
import numpy as np
from scipy.interpolate import interp1d
from matplotlib import cm, pyplot as plt


def sorted_event(prev, event):
    """ Returns a new sorted event, where the order of the groups is
    similar to the order in the previous event. """
    similarity = lambda a, b: len(set(a) & set(b)) - len(set(a) ^ set(b))
    most_similar = lambda g: max(prev, key=lambda pg: similarity(g, pg))
    return sorted(event, key=lambda g: prev.index(most_similar(g)))


def parse_data(chars, events):
    """ Turns the input data into 3 "tables":
    - characters: {character_id: character_name}
    - timelines: {character_id: [y0, y1, y2, ...],
    - deaths: {character_id: (x, y)}
    where x and y are the coordinates of a point in the xkcd like plot.
    """
    characters = dict(enumerate(chars))
    deaths = {}
    timelines = {char: [] for char in characters}

    def coords(character, event):
        for gi, group in enumerate(event):
            if character in group:
                ci = group.index(character)
                return (gi + 0.5 * ci / len(group)) / len(event)
        return None

    t = 0
    previous = events[0]
    for event in events:
        if isinstance(event[0], list):
            previous = event = sorted_event(previous, event)
            for character in [c for c in characters if c not in deaths]:
                timelines[character] += [coords(character, event)] * 2
            t += 2
        else:
            for char in set(event) - set(deaths):
                deaths[char] = (t-1, timelines[char][-1])

    return characters, timelines, deaths


def plot_data(chars, timelines, deaths):
    """ Draws a nice xkcd like movie timeline """

    plt.xkcd()  # because python :)

    fig = plt.figure(figsize=(16,8))
    ax = fig.add_subplot(111)
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    ax.set_xlim([0, max(map(len, timelines.values()))])

    color_floats = np.linspace(0, 1, len(chars))
    color_of = lambda char_id: cm.Accent(color_floats[char_id])

    for char_id in sorted(chars):
        y = timelines[char_id]
        f = interp1d(np.linspace(0, len(y)-1, len(y)), y, kind=5)
        x = np.linspace(0, len(y)-1, len(y)*10)
        ax.plot(x, f(x), c=color_of(char_id))

    x, y = zip(*(deaths[char_id] for char_id in sorted(deaths)))
    ax.scatter(x, y, c=np.array(list(map(color_of, sorted(deaths)))), 
               zorder=99, s=40)

    ax.legend(list(map(chars.get, sorted(chars))), loc='best', ncol=4)
    fig.savefig('testplot.png')


if __name__ == '__main__':
    chars = [
        "T-Rex","Raptor","Raptor","Raptor","Malcolm","Grant","Sattler",
        "Gennaro","Hammond","Kids","Muldoon","Arnold","Nedry","Dilophosaurus"
    ]
    events = [
        [[0],[1,2,3],[4],[5,6],[7,8,10,11,12],[9],[13]],
        [[0],[1,2,3],[4,7,5,6,8,9,10,11,12],[13]],
        [[0],[1,2,3],[4,7,5,6,8,9,10],[11,12],[13]],
        [[0],[1,2,3],[4,7,5,6,9],[8,10,11,12],[13]],
        [[0,4,7],[1,2,3],[5,9],[6,8,10,11],[12],[13]],
        [7],
        [[5,9],[0],[4,6,10],[1,2,3],[8,11],[12,13]],
        [12],
        [[0,5,9],[1,2,3],[4,6,10,8,11],[13]],
        [[0],[5,9],[1,2],[3,11],[4,6,10,8],[13]],
        [11],
        [[0],[5,9],[1,2,10],[3,6],[4,8],[13]],
        [10],
        [[0],[1,2,9],[5,6],[3],[4,8],[13]],
        [[0],[1],[9,5,6],[3],[4,8],[2],[13]],
        [[0,1,9,5,6,3],[4,8],[2],[13]],
        [1,3],
        [[0],[9,5,6,3,4,8],[2],[13]]
    ]
    plot_data(*parse_data(chars, events))

ฮ่า ๆ , ดู xkcd ที่ดีมาก ๆ :) ... โอกาสใด ๆ ที่คุณจะสามารถติดฉลากได้?
Martin Ender

ติดป้ายเส้นต่าง ๆ มีความกว้างของเส้นต่าง ๆ (ด้วยการลด / เพิ่มระหว่างบางจุด) และในที่สุด ... ทำให้เส้นแนวนอนมากขึ้นเมื่อใกล้กับจุดยอดในขณะที่สอดแทรกอีกเช่นโค้งเบซิเยร์และนี่จะเป็นรายการ IMO ที่ดีที่สุด: )
เครื่องมือเพิ่มประสิทธิภาพ

1
ขอบคุณ แต่สไตล์ xkcd รวมอยู่ใน matplotlib ดังนั้นมันจึงเป็นเพียงการเรียกใช้ฟังก์ชั่น :) ดีฉันสร้างตำนาน แต่มันครอบครองเกือบหนึ่งในสามของภาพดังนั้นฉันจึงแสดงความคิดเห็นออกมา
pgy

ฉันแก้ไขคำตอบของฉันฉันคิดว่าตอนนี้ดูดีขึ้นแล้ว
pgy

6

T-SQL

ฉันไม่พอใจกับสิ่งนี้เป็นรายการ แต่ฉันคิดว่าคำถามนี้สมควรได้รับอย่างน้อยลองดู ฉันจะพยายามปรับปรุงในภายหลังการอนุญาตนี้ แต่การติดฉลากจะเป็นปัญหาใน SQL เสมอ โซลูชันต้องการ SQL 2012+ และทำงานใน SSMS (SQL Server Management Studio) ผลลัพธ์อยู่ในแท็บผลลัพธ์เชิงพื้นที่

-- Variables for the input
DECLARE @actors NVARCHAR(MAX) = '["T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", "Sattler", "Gennaro", "Hammond", "Kids", "Muldoon", "Arnold", "Nedry", "Dilophosaurus"]';
DECLARE @timeline NVARCHAR(MAX) = '
[
   [[1], [2, 3, 4], [5], [6, 7], [8, 9, 11, 12, 13], [10], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 9, 10, 11, 12, 13], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 9, 10, 11], [12, 13], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 10], [9, 11, 12, 13], [14]],
   [[1, 5, 8], [2, 3, 4], [6, 10], [7, 9, 11, 12], [13], [14]],
   [8],
   [[6, 10], [1], [5, 7, 11], [2, 3, 4], [9, 12], [13, 14]],
   [13],
   [[1, 6, 10], [2, 3, 4], [5, 7, 11, 9, 12], [14]],
   [[1], [6, 10], [2, 3], [4, 12], [5, 7, 11, 9], [14]],
   [12],
   [[1], [6, 10], [2, 3, 11], [4, 7], [5, 9], [14]],
   [11],
   [[1], [2, 3, 10], [6, 7], [4], [5, 9], [14]],
   [[1], [2], [10, 6, 7], [4], [5, 9], [3], [14]],
   [[1, 2, 10, 6, 7, 4], [5, 9], [3], [14]],
   [2, 4],
   [[1], [10, 6, 7, 5, 9], [3], [14]]
]
';

-- Populate Actor table
WITH actor(A) AS ( SELECT CAST(REPLACE(STUFF(REPLACE(REPLACE(@actors,', ',','),'","','</a><a>'),1,2,'<a>'),'"]','</a>') AS XML))
SELECT ROW_NUMBER() OVER (ORDER BY(SELECT \)) ActorID, a.n.value('.','varchar(50)') Name
INTO Actor
FROM actor CROSS APPLY A.nodes('/a') as a(n);

-- Populate Timeline Table
WITH Seq(L) AS (
    SELECT CAST(REPLACE(REPLACE(REPLACE(REPLACE(@timeline,'[','<e>'),']','</e>'),'</e>,<e>','</e><e>'),'</e>,','</e>') AS XML)
    ),
    TimeLine(N,Exerpt,Elem) AS (
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) N
        ,z.query('.')
        ,CAST(REPLACE(CAST(z.query('.') AS VARCHAR(MAX)),',','</e><e>') AS XML)
    FROM Seq 
        CROSS APPLY Seq.L.nodes('/e/e') AS Z(Z)
    ),
    Groups(N,G,Exerpt) AS (
    SELECT N, 
        ROW_NUMBER() OVER (PARTITION BY N ORDER BY CAST(SUBSTRING(node.value('.','varchar(50)'),1,ISNULL(NULLIF(CHARINDEX(',',node.value('.','varchar(50)')),0),99)-1) AS INT)), 
        CAST(REPLACE(CAST(node.query('.') AS VARCHAR(MAX)),',','</e><e>') AS XML) C
    FROM TimeLine 
        CROSS APPLY Exerpt.nodes('/e/e') as Z(node)
    WHERE Exerpt.exist('/e/e') = 1
    )
SELECT * 
INTO TimeLine
FROM (
    SELECT N, null G, null P, node.value('.','int') ActorID, 1 D 
    FROM TimeLine CROSS APPLY TimeLine.Elem.nodes('/e') AS E(node)
    WHERE Exerpt.exist('/e/e') = 0
    UNION ALL
    SELECT N, G, DENSE_RANK() OVER (PARTITION BY N, G ORDER BY node.value('.','int')), node.value('.','int') ActorID, 0
    FROM Groups CROSS APPLY Groups.Exerpt.nodes('/e') AS D(node)
    ) z;

-- Sort the entries again
WITH ReOrder AS (
            SELECT *, 
                ROW_NUMBER() OVER (PARTITION BY N,G ORDER BY PG, ActorID) PP, 
                COUNT(P) OVER (PARTITION BY N,G) CP, 
                MAX(G) OVER (PARTITION BY N) MG, 
                MAX(ActorID) OVER (ORDER BY (SELECT\)) MA
            FROM (
                SELECT *,
                    LAG(G,1) OVER (PARTITION BY ActorID ORDER BY N) PG,
                    LEAD(G,1) OVER (PARTITION BY ActorID ORDER BY N) NG
                FROM timeline
                ) rg
    )
SELECT * INTO Reordered
FROM ReOrder;
ALTER TABLE Reordered ADD PPP INT
GO
ALTER TABLE Reordered ADD LPP INT
GO
WITH U AS (SELECT N, P, LPP, LAG(PP,1) OVER (PARTITION BY ActorID ORDER BY N) X FROM Reordered)
UPDATE U SET LPP = X FROM U;
WITH U AS (SELECT N, ActorID, P, PG, LPP, PPP, DENSE_RANK() OVER (PARTITION BY N,G ORDER BY PG, LPP) X FROM Reordered)
UPDATE U SET PPP = X FROM U;
GO

SELECT Name, 
    Geometry::STGeomFromText(
        STUFF(LS,1,2,'LINESTRING (') + ')'
        ,0)
        .STBuffer(.1)
        .STUnion(
        Geometry::STGeomFromText('POINT (' + REVERSE(SUBSTRING(REVERSE(LS),1,CHARINDEX(',',REVERSE(LS))-1)) + ')',0).STBuffer(D*.4)
        )
FROM Actor a
    CROSS APPLY (
        SELECT CONCAT(', '
            ,((N*5)-1.2)
                ,' ',(G)+P
            ,', '
            ,((N*5)+1.2)
                ,' ',(G)+P 
            ) AS [text()]
        FROM (
            SELECT ActorID, N,
                CASE WHEN d = 1 THEN
                    ((MA+.0) / (LAG(MG,1) OVER (PARTITION BY ActorID ORDER BY N)+.0)) * 
                    PG * 1.2
                ELSE 
                    ((MA+.0) / (MG+.0)) * 
                    G * 1.2
                END G,
                CASE WHEN d = 1 THEN
                (LAG(PPP,1) OVER (PARTITION BY ActorID ORDER BY N) -((LAG(CP,1) OVER (PARTITION BY ActorID ORDER BY N)-1)/2)) * .2 
                ELSE
                (PPP-((CP-1)/2)) * .2 
                END P
                ,PG
                ,NG
            FROM Reordered
            ) t
        WHERE a.actorid = t.actorid
        ORDER BY N, G
        FOR XML PATH('')
        ) x(LS)
    CROSS APPLY (SELECT MAX(D) d FROM TimeLine dt WHERE dt.ActorID = a.ActorID) d
GO

DROP TABLE Actor;
DROP TABLE Timeline;
DROP TABLE Reordered;

ไทม์ไลน์ที่ได้จะมีลักษณะดังนี้ ป้อนคำอธิบายรูปภาพที่นี่


4

Mathematica โซลูชั่นอ้างอิง

สำหรับการอ้างอิงฉันมีสคริปต์ Mathematica ซึ่งตอบสนองความต้องการขั้นต่ำไม่มากไม่น้อยไปกว่านี้

คาดว่าตัวละครจะเป็นรายชื่อของรูปแบบในคำถามที่และเหตุการณ์ในcharsevents

n = Length@chars;
m = Max@Map[Length, events, {2}];
deaths = {};
Graphics[
 {
  PointSize@Large,
  (
     linePoints = If[Length@# == 3,
         lastPoint = {#[[1]], #[[2]] + #[[3]]/(m + 2)},
         AppendTo[deaths, Point@lastPoint]; lastPoint
         ] & /@ Position[events, #];
     {
      Line@linePoints,
      Text[chars[[#]], linePoints[[1]] - {.5, 0}]
      }
     ) & /@ Range@n,
  deaths
  }
 ]

เป็นตัวอย่างนี่คือตัวอย่าง Jurassic Park โดยใช้ชนิดรายการ Mathematica:

chars = {"T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", 
   "Sattler", "Gennaro", "Hammond", "Kids", "Muldoon", "Arnold", 
   "Nedry", "Dilophosaurus"};
events = {
   {{1}, {2, 3, 4}, {5}, {6, 7}, {8, 9, 11, 12, 13}, {10}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 9, 10, 11, 12, 13}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 9, 10, 11}, {12, 13}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 10}, {9, 11, 12, 13}, {14}},
   {{1, 5, 8}, {2, 3, 4}, {6, 10}, {7, 9, 11, 12}, {13}, {14}},
   {8},
   {{6, 10}, {1}, {5, 7, 11}, {2, 3, 4}, {9, 12}, {13, 14}},
   {13},
   {{1, 6, 10}, {2, 3, 4}, {5, 7, 11, 9, 12}, {14}},
   {{1}, {6, 10}, {2, 3}, {4, 12}, {5, 7, 11, 9}, {14}},
   {12},
   {{1}, {6, 10}, {2, 3, 11}, {4, 7}, {5, 9}, {14}},
   {11},
   {{1}, {2, 3, 10}, {6, 7}, {4}, {5, 9}, {14}},
   {{1}, {2}, {10, 6, 7}, {4}, {5, 9}, {3}, {14}},
   {{1, 2, 10, 6, 7, 4}, {5, 9}, {3}, {14}},
   {2, 4},
   {{1}, {10, 6, 7, 4, 5, 9}, {3}, {14}}
};

เราจะได้รับ:

ป้อนคำอธิบายรูปภาพที่นี่

(คลิกเพื่อดูรุ่นที่ใหญ่กว่า)

นั่นไม่ได้ดูแย่เกินไปแต่ส่วนใหญ่เป็นเพราะข้อมูลอินพุตสั่งมากหรือน้อย หากเราสลับกลุ่มและตัวละครในแต่ละเหตุการณ์ (ในขณะที่ยังคงโครงสร้างเดิม) สิ่งเช่นนี้สามารถเกิดขึ้นได้:

ป้อนคำอธิบายรูปภาพที่นี่

ซึ่งเป็นระเบียบเล็กน้อย

ดังที่ฉันได้กล่าวมาสิ่งนี้ตอบสนองความต้องการขั้นต่ำ มันไม่ได้พยายามที่จะหาเลย์เอาต์ที่ดีและมันก็ไม่สวย แต่นั่นคือสิ่งที่พวกคุณเข้ามา!


ฉันแค่คิดว่าคุณสามารถ 'prettify' โดยใช้เส้นโค้งกำลังสองหรือลูกบาศก์เพื่อลบมุมที่คม? (ฉันจะทำอย่างนั้นแทนเจนต์ที่จุดที่กำหนดเป็น 0 เสมอ)
ข้อผิดพลาด

@ flawr แน่นอนหรือฉันสามารถใช้เคล็ดลับเหล่านี้ได้บ้าง แต่นั่นไม่ใช่จุดประสงค์ของคำตอบนี้ ;) ฉันแค่อยากจะให้การอ้างอิงสำหรับขั้นต่ำที่แน่นอน
Martin Ender

3
ขอโทษด้วยที่ไม่ได้สังเกตว่านี่เป็นคำถามของคุณเอง = P
flawr
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.