2

我正在尝试将使用的交互式绘图嵌入Altair到 google-site 中。在此图中,我想一次以交互方式显示存储在 google-drive 上的图像。当我尝试这样做时,mark_image默默地失败了,大概是因为它没有读取图像。这并不奇怪,因为 google-drive 图像是私有的。使用公开共享的图像,我不会遇到这个问题。出于此情节的目的,我想将图像保密。另外,总共有很多图像(~1K),所以我可能不应该将它们编码为数据/字节。我怀疑这可能会使我的 HTML 文件变得又大又慢。如果我对此有误,请纠正我。

我想知道是否mark_image可以从 google-drive 链接读取图像,可能使用某种“阅读器”(上游 JS 或 python 库),然后将读取的图像提供给mark_image. 如果有人有这方面的经验,解决方案/建议/解决方法将非常有帮助。

这是一个演示代码来测试这个:

案例 1:可公开访问的图像(没问题)。使用 显示mark_image,以 HTML 格式保存。

import altair as alt
import pandas as pd
path="https://vega.github.io/vega-datasets/data/gimp.png"
source = pd.DataFrame([{"x": 0, "y": 0, "img": path},])

chart=alt.Chart(source).mark_image(width=100,height=100,).encode(x='x',y='y',url='img')
chart.save('test.html')

在此处输入图像描述

然后我使用此选项将 HTML 嵌入到谷歌站点(私有,不向公众共享)中,在此处输入图像描述然后将 HTML 文件的内容粘贴到Embed code选项卡中。

案例 2:Google 驱动器上的图像(问题!)。存储在谷歌驱动器上的图像的情况(私人,不向公众共享)。

# Please use the code above with `path` variable generated like this:
file_id='' # google drive file id
path=f"https://drive.google.com/uc?export=view&id={file_id}"

在这种情况下,显然mark_image会默默地失败,并且图像不会显示在图上。​</p>

4

1 回答 1

0

在寻找最佳解决方案后,我决定依靠一种将图像编码为数据/字节的解决方法。这消除了从谷歌驱动器读取 URL 的问题,我找不到解决方案。正如我所怀疑的那样,以数据/字节编码图像会使 HTML 变大,然而,令人惊讶的是(对我来说)加载速度一点也不慢。我想这是我想做的最好的事情。

在下面的示例中,get_data函数获取图像的数据/字节。Altair我将它放入作为输入的数据框的列中。

def plot_(images_from):
    import altair as alt
    import pandas as pd
    import numpy as np

    np.random.seed(0)
    n_objects = 20
    n_times = 50
    # Create one (x, y) pair of metadata per object
    locations = pd.DataFrame({
        'id': range(n_objects),
        'x': np.random.randn(n_objects),
        'y': np.random.randn(n_objects)
    })
    def get_data(p):
        import base64
        with open(p, "rb") as f:
            return "data:image/jpeg;base64,"+base64.b64encode(f.read()).decode()
    import urllib.request
    if images_from=='url':
        l1=[f"https://vega.github.io/vega-datasets/data/{k}.png" for k in ['ffox','7zip','gimp']]
    elif images_from=='data':
        l1=[get_data(urllib.request.urlretrieve(f"https://vega.github.io/vega-datasets/data/{k}.png",f'/tmp/{k}.png')[0]) for k in ['ffox','7zip','gimp']]

    np.random.seed(0)
    locations['img']=np.random.choice(l1, size=len(locations))

    # Create a 50-element time-series for each object
    timeseries = pd.DataFrame(np.random.randn(n_times, n_objects).cumsum(0),
                            columns=locations['id'],
                            index=pd.RangeIndex(0, n_times, name='time'))

    # Melt the wide-form timeseries into a long-form view
    timeseries = timeseries.reset_index().melt('time')

    # Merge the (x, y) metadata into the long-form view
    timeseries['id'] = timeseries['id'].astype(int)  # make merge not complain
    data = pd.merge(timeseries, locations, on='id')

    # Data is prepared, now make a chart

    selector = alt.selection_single(empty='none', fields=['id'])

    base = alt.Chart(data).properties(
        width=250,
        height=250
    ).add_selection(selector)

    points = base.mark_point(filled=True, size=200).encode(
        x='mean(x)',
        y='mean(y)',
        color=alt.condition(selector, 'id:O', alt.value('lightgray'), legend=None),
    )

    timeseries = base.mark_line().encode(
        x='time',
        y=alt.Y('value', scale=alt.Scale(domain=(-15, 15))),
        color=alt.Color('id:O', legend=None)
    ).transform_filter(
        selector
    )

    images=base.mark_image(filled=True, size=200).encode(
        x='x',
        y='y',
        url='img',
    ).transform_filter(
        selector
    )

    chart=points | timeseries | images
    chart.save(f'test/chart_images_{images_from}.html')

# generate htmls
plot_(images_from='url') # generate the HTML using URLs
plot_(images_from='data') # generate the HTML using data/bytes

使用这些数据生成的 HTML 比使用 URL 生成的 HTML 大约大 78 倍(~12Mb vs ~0.16Kb),但没有明显变慢。

更新:我后来发现谷歌网站不允许嵌入超过 1Mb 大小的 HTML 文件。所以最后,对图像进行编码并没有真正的帮助。

在此处输入图像描述

于 2021-12-12T04:02:33.660 回答