0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローカルの画像ファイルをpandasで可視化する

Posted at

概要

以下の方法でローカルの画像ファイルを表示する

  • ローカルの画像ファイルをBase64形式に変換し、DataFrameをHTMLで表示する
  • ローカルの画像ファイルをエクセルに埋め込む

環境

jupyter-notebookで行います。

docker-compose.yml
version: '3.4'
services:
  notebook:
    image: jupyter/scipy-notebook:latest
    command:
    - start-notebook.sh
    - --NotebookApp.token='' # 認証を無効化
    - --NotebookApp.disable_check_xsrf=True # CSRFチェックを無効化
    ports:
    - 8888:8888
    volumes:
    - ./notebook:/home/jovyan/work

やり方

画像ファイルをBase64形式に変換する

PILを利用して画像ファイルを読み込み、base64に変換します。

import base64
from PIL import Image
from io import BytesIO

def get_thumbnail(path):
    img = Image.open(path)
    aspect = float(img.height) / img.width
    width = 300
    height = round(width * aspect)
    return img.resize((width, height))

def image_base64(img):
    if isinstance(img, str):
        img = get_thumbnail(img)
    with BytesIO() as buffer:
        img.save(buffer, 'png')
        return base64.b64encode(buffer.getvalue()).decode()

# image_base64("file_path")

HTMLで画像を表示する

import pandas as pd
from IPython.display import HTML
from os import path

def image_formatter(img):
    if img is None:
        return
    # base64形式で表示する
    return f'<img src="data:image/png;base64,{img}">'

def generate_image(file_path):
    return image_base64(file_path) if path.exists(file_path) else None

# 以下からは実際の環境に合わせて変更してください
def add_img(df):
    df['img'] = df.animal.map(lambda name: generate_image(f'/home/jovyan/work/{name}.png'))

df = pd.read_csv('/home/jovyan/work/animal.csv')
add_img(df)
# HTMLに変換した上で表示する。formatterを指定する。
HTML(df.to_html(formatters={'img': image_formatter}, escape=False))

HTML_dataframe.png

base64形式に変換しながら表示することになるので、量が多いと読み込みに時間がかかります。
このままcsvとして保存してもいいですが、ファイルサイズに気をつけてください。

エクセルで画像を埋め込む

import pandas as pd
from os import path

import openpyxl
from openpyxl.drawing.image import Image


def adjust_column_width(ws):
    for c in ws.columns:
        column_letter = c[0].column_letter
        # 画像に合わせて列幅を調整
        if column_letter == 'C':
            ws.column_dimensions[column_letter].width = 40

def to_image(cell, cell_name, idx):
    try:
        img = Image(str(cell.value))
        aspect = float(img.height) / img.width
        img.width = 300
        img.height = aspect * 300
        ws.add_image(img, cell_name)
        ws.row_dimensions[idx].height = img.height - 30
    except FileNotFoundError:
        pass
    finally:
        # 画像列にパスを埋め込んで変換するので、セルの文字列を削除
        cell.value = ""

def convert_image_column(worksheet):
    ws = worksheet

    row_number = len(list(ws.rows))
    # ヘッダー行は除外
    for idx in range(2, row_number + 1):
        # セルの行は1始まり
        img_cell_name = 'C' + str(idx)
        cell = ws[img_cell_name]
        to_image(cell, img_cell_name, idx)

# 以下からは実際の環境に合わせて変更してください
def add_img(df):
    df['img'] = df.animal.map(lambda name: f'/home/jovyan/work/{name}.png')

# Dataframeにパスを埋め込んでエクセルとして保存
df = pd.read_csv('/home/jovyan/work/animal.csv')
add_img(df)
excel_file_path = '/home/jovyan/work/animal.xlsx'
df.to_excel(excel_file_path)

# openpyxlで読み込んで整形
wb = openpyxl.load_workbook(excel_file_path)
ws = wb.active
adjust_column_width(ws)
convert_image_column(ws)
wb.save(excel_file_path)
wb.close()

excel_dataframe.png

簡単にまとめ

実際は他にもパラメータが存在していて、条件で絞り込んだ際に画像を確認したりする目的で使いました。
データ量が多くなると重くなったり、ファイルサイズが大きくなるので、先に画像を小さくしておくといいかもしれないです。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?