StreamlitでCSV・Excel結合アプリを作る：縦横結合＆転置対応

# **user**:

VSCode と Streamlit を使って、次の要求を満たすアプリを作ってください。

・1つあるいは複数のファイルを入力する。入力ファイルはcsvファイルまたはエクセルファイルである。
・エクセルファイルを読み込んだ場合は、そこにある1つあるいは複数のシートをすべて読み込む。
・読み込んだ複数の表形式データの概略を表示する。
・読み込まれた複数の表形式データから、ユーザが複数選択すると、選ばれた表形式データを結合する。
・結合の際に、ユーザは表データを転置（列と行を入れ替え）することもできる。
・結合した結果をエクセルファイルとしてダウンロードできる。

# **assistant**:

以下は、Streamlit と VSCode を使って実装できる、あなたの要件を満たすアプリのコードです。

---

### ✅ 必要なライブラリのインストール（VSCode のターミナルで）
```bash
pip install streamlit pandas openpyxl
```

---

### 📄 `app.py`（Streamlit アプリのコード）

```python
import streamlit as st
import pandas as pd
import io

st.title("CSV / Excel 結合アプリ")

uploaded_files = st.file_uploader(
    "CSV または Excel ファイルをアップロードしてください（複数可）",
    type=["csv", "xls", "xlsx"],
    accept_multiple_files=True
)

# ファイル名と対応する DataFrame のリスト
dataframes = {}
sheet_selector = {}

def load_file(file):
    if file.name.endswith(".csv"):
        df = pd.read_csv(file)
        dataframes[file.name] = {"原本": df}
    elif file.name.endswith((".xls", ".xlsx")):
        xls = pd.ExcelFile(file)
        for sheet in xls.sheet_names:
            df = xls.parse(sheet)
            key = f"{file.name} - {sheet}"
            dataframes.setdefault(file.name, {})[sheet] = df

# アップロードされたファイルを読み込み
if uploaded_files:
    for file in uploaded_files:
        load_file(file)

    st.subheader("読み込まれた表データの概要")

    for fname, sheets in dataframes.items():
        st.markdown(f"### 📁 {fname}")
        for sheet_name, df in sheets.items():
            st.write(f"▶️ **{sheet_name}**")
            st.dataframe(df.head())

    st.subheader("結合するデータを選択")

    all_keys = []
    for fname, sheets in dataframes.items():
        for sheet_name in sheets.keys():
            all_keys.append((fname, sheet_name))

    selected_keys = st.multiselect(
        "結合する表データを選んでください",
        options=all_keys,
        format_func=lambda x: f"{x[0]} - {x[1]}"
    )

    transpose = st.checkbox("転置（行と列を入れ替える）")

    if selected_keys:
        dfs_to_concat = []
        for fname, sheet_name in selected_keys:
            df = dataframes[fname][sheet_name]
            if transpose:
                df = df.transpose()
            dfs_to_concat.append(df)

        result = pd.concat(dfs_to_concat, ignore_index=True)

        st.subheader("🔗 結合結果プレビュー")
        st.dataframe(result)

        # Excel ファイルとして出力
        buffer = io.BytesIO()
        with pd.ExcelWriter(buffer, engine='openpyxl') as writer:
            result.to_excel(writer, index=False, sheet_name="結合結果")

        st.download_button(
            label="📥 結合結果をダウンロード",
            data=buffer.getvalue(),
            file_name="merged_result.xlsx",
            mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        )
```

---

### 🚀 実行方法
ターミナルで以下のコマンドを実行：
```bash
streamlit run app.py
```

---

このアプリでは、複数ファイル＋複数シートを処理し、転置オプションを選んで結合し、Excelファイルとしてダウンロードできます。必要に応じて、結合の方法（縦方向や横方向など）を選べる機能を追加することも可能です！

何か機能を追加したい部分やカスタマイズしたい点があれば教えてください！

# **user**:

ありがとうございます。ありがとうございます。ほとんど完璧なんですが、２つほど修正お願いします。

・転置は、出力ファイルではなくて、入力ファイル1個1個に対して、転置を行うか否か選択できませんか？
・今のコードだと縦に結合するようになっていますね。行ラベルを見て横に結合するのをデフォルトにしてください。

# **assistant**:

バッチリ理解しました！

### 🔧 修正点の概要：
1. **転置オプションをファイル（シート）ごとに設定**できるようにする。
2. **デフォルトの結合方法を「横方向（列方向）に結合」**し、行ラベル（インデックス）を基準に結合する。

---

以下が修正済みの完全なコードです👇

---

### 📄 `app.py`（修正版）

```python
import streamlit as st
import pandas as pd
import io

st.title("CSV / Excel 表データ結合アプリ")

uploaded_files = st.file_uploader(
    "CSV または Excel ファイルをアップロード（複数可）",
    type=["csv", "xls", "xlsx"],
    accept_multiple_files=True
)

# ファイル名と対応する DataFrame を保存する辞書
dataframes = {}
transpose_flags = {}

def load_file(file):
    if file.name.endswith(".csv"):
        df = pd.read_csv(file, index_col=0)
        key = (file.name, "原本")
        dataframes[key] = df
    elif file.name.endswith((".xls", ".xlsx")):
        xls = pd.ExcelFile(file)
        for sheet in xls.sheet_names:
            df = xls.parse(sheet, index_col=0)
            key = (file.name, sheet)
            dataframes[key] = df

# アップロードされたファイルを読み込む
if uploaded_files:
    for file in uploaded_files:
        load_file(file)

    st.subheader("読み込まれた表データの概要と転置設定")

    # 転置設定 UI の作成
    for (fname, sheet_name), df in dataframes.items():
        key_label = f"{fname} - {sheet_name}"
        st.markdown(f"#### 📄 {key_label}")
        st.dataframe(df.head())
        transpose_flags[(fname, sheet_name)] = st.checkbox(f"⬆️ {key_label} を転置", key=key_label)

    st.subheader("結合するデータを選択")

    selected_keys = st.multiselect(
        "結合する表データを選んでください（横方向に結合されます）",
        options=list(dataframes.keys()),
        format_func=lambda x: f"{x[0]} - {x[1]}"
    )

    if selected_keys:
        dfs_to_concat = []

        for key in selected_keys:
            df = dataframes[key]
            if transpose_flags.get(key, False):
                df = df.transpose()
            dfs_to_concat.append(df)

        # 横方向に結合（indexを使って）
        result = pd.concat(dfs_to_concat, axis=1)

        st.subheader("🔗 結合結果プレビュー")
        st.dataframe(result)

        # Excel 出力処理
        buffer = io.BytesIO()
        with pd.ExcelWriter(buffer, engine='openpyxl') as writer:
            result.to_excel(writer, sheet_name="結合結果")

        st.download_button(
            label="📥 結合結果をダウンロード",
            data=buffer.getvalue(),
            file_name="merged_result.xlsx",
            mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        )
```

---

### ✅ 修正のポイント

- 各シートごとに個別の `checkbox` を設けて「転置」するかを選べます。
- `pd.concat(..., axis=1)` を使って、**横方向に結合（行ラベルを揃えて結合）**します。
- `index_col=0` にして、行ラベルを活かした結合を実現しています。

---

お好みに応じて、**「縦方向に結合するモード」も選べるような切り替え機能**も追加できます。必要ならすぐご用意します！

他にも追加したい機能やデザイン調整などあれば、気軽にどうぞ！

# **user**:

ありがとうございます。次のようなエラーが出たので、結合時に列名が重複する場合は重複しない処理を加えるようにしてください。

```
ValueError: Duplicate column names found: ['Value', 'Value']
Traceback:
File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/exec_code.py", line 121, in exec_func_with_error_handling
    result = func()
File "/Users/maskot/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 640, in code_to_exec
    exec(code, module.__dict__)
File "/Users/maskot/streamlit-app/app.py", line 64, in <module>
    st.dataframe(result)
File "/Users/maskot/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/metrics_util.py", line 410, in wrapped_func
    result = non_optional_func(*args, **kwargs)
File "/Users/maskot/streamlit-app/venv/lib/python3.10/site-packages/streamlit/elements/arrow.py", line 607, in dataframe
    proto.data = dataframe_util.convert_pandas_df_to_arrow_bytes(data_df)
File "/Users/maskot/streamlit-app/venv/lib/python3.10/site-packages/streamlit/dataframe_util.py", line 822, in convert_pandas_df_to_arrow_bytes
    table = pa.Table.from_pandas(df)
File "pyarrow/table.pxi", line 4751, in pyarrow.lib.Table.from_pandas
File "/Users/maskot/streamlit-app/venv/lib/python3.10/site-packages/pyarrow/pandas_compat.py", line 595, in dataframe_to_arrays
    convert_fields) = _get_columns_to_convert(df, schema, preserve_index,
File "/Users/maskot/streamlit-app/venv/lib/python3.10/site-packages/pyarrow/pandas_compat.py", line 375, in _get_columns_to_convert
    raise ValueError(
```

# **assistant**:

ありがとうございます！エラー内容を拝見すると、**複数の DataFrame を横方向（列方向）に結合**する際に、**同名の列（たとえば `Value`）が重複しているために PyArrow がエラー**を出しているようですね。

---

### ✅ 解決方法
結合時に**列名の重複があれば自動的にリネーム**して、重複を避けるようにします。

以下の修正を `app.py` に加えました：

---

### 🛠 修正後のコード（重複列名の回避付き）

```python
import streamlit as st
import pandas as pd
import io

st.title("CSV / Excel 表データ結合アプリ")

uploaded_files = st.file_uploader(
    "CSV または Excel ファイルをアップロード（複数可）",
    type=["csv", "xls", "xlsx"],
    accept_multiple_files=True
)

# ファイル名と対応する DataFrame を保存する辞書
dataframes = {}
transpose_flags = {}

def load_file(file):
    if file.name.endswith(".csv"):
        df = pd.read_csv(file, index_col=0)
        key = (file.name, "原本")
        dataframes[key] = df
    elif file.name.endswith((".xls", ".xlsx")):
        xls = pd.ExcelFile(file)
        for sheet in xls.sheet_names:
            df = xls.parse(sheet, index_col=0)
            key = (file.name, sheet)
            dataframes[key] = df

# アップロードされたファイルを読み込む
if uploaded_files:
    for file in uploaded_files:
        load_file(file)

    st.subheader("読み込まれた表データの概要と転置設定")

    # 転置設定 UI の作成
    for (fname, sheet_name), df in dataframes.items():
        key_label = f"{fname} - {sheet_name}"
        st.markdown(f"#### 📄 {key_label}")
        st.dataframe(df.head())
        transpose_flags[(fname, sheet_name)] = st.checkbox(f"⬆️ {key_label} を転置", key=key_label)

    st.subheader("結合するデータを選択")

    selected_keys = st.multiselect(
        "結合する表データを選んでください（横方向に結合されます）",
        options=list(dataframes.keys()),
        format_func=lambda x: f"{x[0]} - {x[1]}"
    )

    if selected_keys:
        dfs_to_concat = []

        for i, key in enumerate(selected_keys):
            df = dataframes[key]
            if transpose_flags.get(key, False):
                df = df.transpose()

            # 列名が重複しないように prefix を追加
            df.columns = [f"{col}_{i+1}" if col in df.columns.duplicated(keep=False) else col for col in df.columns]
            dfs_to_concat.append(df)

        # 横方向に結合（indexを使って）
        result = pd.concat(dfs_to_concat, axis=1)

        st.subheader("🔗 結合結果プレビュー")
        st.dataframe(result)

        # Excel 出力処理
        buffer = io.BytesIO()
        with pd.ExcelWriter(buffer, engine='openpyxl') as writer:
            result.to_excel(writer, sheet_name="結合結果")

        st.download_button(
            label="📥 結合結果をダウンロード",
            data=buffer.getvalue(),
            file_name="merged_result.xlsx",
            mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        )
```

---

### 🔍 主な変更点
```python
df.columns = [f"{col}_{i+1}" if col in df.columns.duplicated(keep=False) else col for col in df.columns]
```

この行で、**同じ名前の列があれば自動的に `_1`, `_2`, ... をつけて回避**するようにしています。  
例：`Value` → `Value_1`, `Value_2`

---

ご希望に合わせて、列名のプレフィックスを「ファイル名+シート名」にするなどもできます！  
必要あればすぐ対応しますので、お気軽にどうぞ。

# **user**:

ありがとうございます。

・列名のプレフィックスを「ファイル名_シート名」にする。
・「縦方向に結合するモード」も選べるような切り替え機能も追加

以上の変更を加えて、全コードを書いてください。

# **assistant**:

完璧です！以下のコードでは、あなたのリクエストをすべて反映しています：

---

### ✅ 主な修正点
1. **列名に `ファイル名_シート名` をプレフィックス**として追加（転置にも対応）。
2. **結合方法（縦・横）を選択できる UI** を追加。

---

### 📄 `app.py`（完全版）

```python
import streamlit as st
import pandas as pd
import io

st.title("CSV / Excel 表データ結合アプリ")

uploaded_files = st.file_uploader(
    "CSV または Excel ファイルをアップロード（複数可）",
    type=["csv", "xls", "xlsx"],
    accept_multiple_files=True
)

# ファイル名と対応する DataFrame を保存する辞書
dataframes = {}
transpose_flags = {}

def load_file(file):
    if file.name.endswith(".csv"):
        df = pd.read_csv(file, index_col=0)
        key = (file.name, "原本")
        dataframes[key] = df
    elif file.name.endswith((".xls", ".xlsx")):
        xls = pd.ExcelFile(file)
        for sheet in xls.sheet_names:
            df = xls.parse(sheet, index_col=0)
            key = (file.name, sheet)
            dataframes[key] = df

# アップロードされたファイルを読み込む
if uploaded_files:
    for file in uploaded_files:
        load_file(file)

    st.subheader("読み込まれた表データの概要と転置設定")

    # 転置設定 UI の作成
    for (fname, sheet_name), df in dataframes.items():
        key_label = f"{fname} - {sheet_name}"
        st.markdown(f"#### 📄 {key_label}")
        st.dataframe(df.head())
        transpose_flags[(fname, sheet_name)] = st.checkbox(f"⬆️ {key_label} を転置", key=key_label)

    st.subheader("結合するデータを選択")

    selected_keys = st.multiselect(
        "結合する表データを選んでください",
        options=list(dataframes.keys()),
        format_func=lambda x: f"{x[0]} - {x[1]}"
    )

    concat_axis = st.radio(
        "データの結合方向を選択してください",
        options=["横方向（列を結合）", "縦方向（行を結合）"],
        index=0
    )

    if selected_keys:
        dfs_to_concat = []

        for key in selected_keys:
            fname, sheet_name = key
            df = dataframes[key]
            if transpose_flags.get(key, False):
                df = df.transpose()

            # 列名 or インデックスにプレフィックスをつけて重複を避ける
            prefix = f"{fname}_{sheet_name}".replace(" ", "_").replace(".", "_")

            if concat_axis == "横方向（列を結合）":
                df.columns = [f"{prefix}_{col}" for col in df.columns]
                dfs_to_concat.append(df)
            else:  # 縦方向
                df["__source__"] = prefix  # 区別用の列
                dfs_to_concat.append(df.reset_index())

        if concat_axis == "横方向（列を結合）":
            result = pd.concat(dfs_to_concat, axis=1)
        else:
            result = pd.concat(dfs_to_concat, axis=0, ignore_index=True)

        st.subheader("🔗 結合結果プレビュー")
        st.dataframe(result)

        # Excel 出力処理
        buffer = io.BytesIO()
        with pd.ExcelWriter(buffer, engine='openpyxl') as writer:
            result.to_excel(writer, index=False, sheet_name="結合結果")

        st.download_button(
            label="📥 結合結果をダウンロード",
            data=buffer.getvalue(),
            file_name="merged_result.xlsx",
            mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        )
```

---

### 📝 補足ポイント
- **横結合**のとき：行インデックスを基準に、**列名に `ファイル名_シート名_元の列名`** をつけて重複を避けています。
- **縦結合**のとき：元のインデックスを列に変換し、ソースを識別できるように `__source__` カラムを追加。
- **`concat_axis`** ラジオボタンで結合方向を切り替え可能。

---

さらに見やすくしたい、たとえば色付けや結合前後の比較など、必要に応じて機能追加できます！お気軽にどうぞ。

# **user**:

ありがとうございました。今回の内容をブログに書きたいので、タイトル案を複数提案してください。

# **assistant**:

こちらこそ、楽しい開発でした！ブログタイトル案、以下にいくつか提案しますね。目的や読者層に応じて調整できますので、お気軽にフィードバックください。

---

### 💻 実用寄り・検索されやすい系
1. **「StreamlitでCSV・Excel結合アプリを作る：縦横結合＆転置対応」**
2. **「Python×Streamlitで簡単データ結合ツールを自作してみた」**
3. **「複数のCSV・Excelファイルをまとめて結合！Streamlitアプリで効率化」**
4. **「転置＆横結合にも対応！Streamlitで作る表データ統合アプリ」**
5. **「ノーコード感覚でデータ加工！Streamlitでファイル結合アプリを構築」**

---

### ✨ キャッチー系・ストーリー仕立て
6. **「重複列名？転置？Streamlitと格闘して作ったデータ結合アプリ」**
7. **「ChatGPTに助けられながら作った、最強のCSV・Excel結合ツール」**
8. **「初心者でもできた！Streamlitで表データの“縦横無尽”アプリ」**
9. **「複数ファイルを“サクッと結合”！Streamlitで自作アプリ作ってみた」**
10. **「VSCode × Streamlitでここまでできる！Excelデータ結合アプリ開発記」**

---

どれか気になるものはありましたか？  
読者層（たとえば初心者向け、業務効率化、教育目的など）に合わせて、もう少し絞り込むこともできます！

# **user**:

ありがとうございます。「StreamlitでCSV・Excel結合アプリを作る：縦横結合＆転置対応」で行こうと思います。今回のまとめを詳細に書いてください。

# **assistant**:

了解しました！  
それでは、ブログ記事用の**詳細なまとめ原稿**を以下にご用意しました。文章はブログ形式で、読みやすさを意識して構成しています。

---

## StreamlitでCSV・Excel結合アプリを作る：縦横結合＆転置対応

業務や研究、教育の現場で「複数のCSVやExcelファイルを結合して1つの表にまとめたい！」というシーン、ありますよね。  
しかも、シートごとに転置したり、縦・横どちらで結合したいか選べる柔軟性も欲しい…。

今回は、そんなニーズを満たすために、**PythonとStreamlitを使って、誰でも使える表データ結合アプリ**を作ってみました！

---

### 🎯 このアプリでできること

| 機能 | 説明 |
|------|------|
| ✅ CSV・Excelファイルのアップロード（複数可） | Excelはすべてのシートを自動的に読み込みます。 |
| ✅ 各表に対して「転置」の設定が可能 | 結合前に行と列を入れ替えることができます。 |
| ✅ 複数の表を選択して結合 | ユーザが選んだデータだけをまとめます。 |
| ✅ 結合方向の選択（縦 or 横） | インデックス基準で「横に結合」または「行追加（縦結合）」を選べます。 |
| ✅ 重複列名の自動リネーム | 結合時のエラーを防止。 |
| ✅ 結果をExcelでダウンロード | ワンクリックで出力できます。 |

---

### 🧩 アプリの画面構成

1. **ファイルアップロード**  
　CSV / Excelファイルをドラッグ＆ドロップまたはファイル選択。

2. **読み込み確認 & 転置設定**  
　読み込んだデータの先頭数行が表示され、「転置するか？」をチェックで選択可能。

3. **結合対象の選択**  
　任意の表を複数選択可能。

4. **結合方法の選択（横 or 縦）**  
　ラジオボタンで直感的に切り替えできます。

5. **結果のプレビュー & ダウンロード**  
　結合されたデータを確認し、そのままExcelファイルとして保存。

---

### 🛠 技術的ポイント

- **Streamlitの使いやすさ**  
　UIの構築が非常に直感的で、フォーム入力やチェックボックス、ダウンロードボタンなどが簡単に組み込めます。

- **Pandasでデータ処理**  
　ファイルの読み込み、転置、結合、列名のリネームなどはすべて `pandas` で処理。

- **重複列のリネーム**  
　結合時に `ファイル名_シート名_列名` の形式にすることで、同名の列が衝突するのを回避しています。

---

### 💡 使い方のアイデア

- 複数の月次レポートを1つにまとめる
- 複数のアンケート集計データを比較
- 縦横の構造を自由に変えながらデータ統合
- 教育目的で表操作のデモ用アプリとして活用

---

### 🧪 エラーとの格闘（裏話）

開発中、「横に結合しようとしたら、`ValueError: Duplicate column names found:`」というエラーが発生。  
原因は、結合対象の表に同じ列名があったこと。

```text
ValueError: Duplicate column names found: ['Value', 'Value']
```

これを回避するために、**結合前に各列にプレフィックス（ファイル名＋シート名）を自動追加**する処理を加えました。  
細かいことですが、こうしたエラー回避の仕組みが安心して使えるアプリに繋がります。

---

### ✏️ コードの全体像

実際のコードは約100行ほど。  
フルコードはこちらに掲載しています 👉（GitHubなどへのリンクがあれば追記）

---

### ✨ おわりに

Streamlitは、「ちょっとした業務効率化ツールをすぐ形にしたい」というニーズにピッタリのフレームワークです。

今回のような「ファイル読み込み → データ操作 → 出力」系の処理に特に向いているので、ぜひ自分なりのバージョンを作ってみてください！

---

ご希望があれば、**コードの整形済みブロック**や、**画像用の図解**も追加できますので、お気軽にどうぞ！