【VBA】データの行と列を入れ替える(転置する)方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク
  1. この記事でわかること
  2. どんな場面で使う?
  3. 完成イメージ(Before / After)
  4. 実行前の準備
    1. バックアップを取る
    2. Excelをマクロ有効ブック(.xlsm)で保存する
    3. シート構成を確認する
  5. 手順(コピペ → 実行まで約5分)
    1. VBE(コードを書く画面)を開く
    2. 標準モジュールを挿入する
    3. コードを貼り付けて実行する
  6. コード(基本版)– PasteSpecialで転置貼り付け
    1. 書き換えポイント
  7. コード(応用版)– WorksheetFunction.Transposeで転置
    1. 書き換えポイント
  8. コード(実務版)– 月別横持ちデータを縦持ちに変換
    1. 書き換えポイント
  9. よくある落とし穴5選
    1. 1. CutCopyMode の解除忘れで点線枠が残る
    2. 2. WorksheetFunction.Transpose で「関数の引数が正しくありません」エラー
    3. 3. 転置先の範囲指定で行数と列数を逆にし忘れた
    4. 4. 同じシートで転置したら元データが壊れた
    5. 5. 1行 or 1列のデータで WorksheetFunction.Transpose の結果がおかしい
    6. VBAのTranspose関数でエラーが出るときの対処法
    7. VBAで行列入れ替え時に大量データで処理が失敗するときの対処法
  10. FAQ
    1. Q1: 書式も含めて転置したい
    2. Q2: 値だけ転置したい(書式はいらない)
    3. Q3: 65,536行を超えるデータを転置したい
    4. Q4: 横持ちデータを縦持ちに変換したい
    5. Q5: 転置後に元データを消したい
  11. まとめ
    1. 関連記事
  12. 次にやりたくなること
  13. もっとカスタマイズしたい場合

この記事でわかること

  • VBAでデータの行と列を入れ替え(転置)できる
  • PasteSpecial・Transpose関数・配列ループの3方式を使い分けられる
  • 月別の横持ちデータを縦持ちに変換できる(ピボットテーブル用のデータ整形)

対象: Excel 2016以降 / Microsoft 365、Windows 10/11

どんな場面で使う?

行列の入れ替え(転置)が必要になるのは、たとえばこんな場面だ。

  • 月別売上の横持ちデータをピボットテーブルに入れたい — 各拠点が行、月が列に並んだ横持ちデータは見やすいが、ピボットテーブルや集計関数で扱うには「拠点・月・金額」の3列構成(縦持ち)にする必要がある
  • アンケート結果の行列を入れ替えてグラフを作りたい — 質問が列、回答者が行に並んだ集計表を、質問ごとの棒グラフにしたい場合、行と列を入れ替えると項目軸が整う
  • 他システムから出力されたCSVの向きが逆 — 基幹システムやBIツールから出力されたデータが横持ちになっていて、自社フォーマット(縦持ち)に合わせる必要がある
  • 複数シートのデータを統合する前に向きを揃えたい — シートAは行方向、シートBは列方向にデータが並んでいて、統合前にどちらかを転置して形を揃える場面
  • 予算テンプレートの行列を入れ替えて報告書に貼りたい — 部門が行・四半期が列のテンプレートを、報告書用に四半期が行・部門が列のレイアウトに変換するケース

どれも手作業でやると行列の数に比例して時間がかかるし、コピペ先を間違えるリスクが高い作業ばかりだ。VBAで自動化すればデータ量に関係なく一瞬で終わる。

完成イメージ(Before / After)

Before(横持ちデータ):

拠点 4月 5月 6月 3月
東京 100 120 110 130
大阪 80 90 85 95

After(縦持ちデータに変換):

拠点 売上
東京 4月 100
東京 5月 120
東京 6月 110
大阪 4月 80
大阪 5月 90

自分も毎月、月別に横に並んだ売上データを集計用に縦持ちへ手作業で並べ替えていた。12か月×30拠点で360セル分を1つずつコピペしていて、途中でどこまでやったか分からなくなることがあった。VBAで転置を自動化してからは、360セルの並べ替えが3秒で終わるようになった。コピペミスもゼロ。同じように横持ちデータの変換に苦労している人に、この記事で自動化を体験してほしい。

行と列の入れ替えは手作業だと量が増えるほどミスが出る。VBAに任せれば速くて正確。

なお、行列の入れ替えではなく単純にセルの値を別シートにコピーしたい場合は セルの転記を自動化する方法 を参照。

実行前の準備

バックアップを取る

転置先に既存データがある場合、上書きされる可能性がある。 必ずファイルのコピーを別フォルダに保存してから実行する。

Excelをマクロ有効ブック(.xlsm)で保存する

  1. 「ファイル」→「名前を付けて保存」
  2. ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  3. 保存

シート構成を確認する

このコードは以下のシート構成を前提としている:

  • データシート: シート名「データ」— 転置元のデータがある
  • 結果シート: シート名「結果」— 転置先

自分のシート名が異なる場合は、コード内のシート名を書き換える。

手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

  1. Excelで Alt + F11 を押す

標準モジュールを挿入する

  1. VBEのメニュー →「挿入」→「標準モジュール」

コードを貼り付けて実行する

  1. コードウィンドウに、下のコードをそのままコピペする
  2. Alt + F8 → マクロ名を選んで「実行」

ボタンに割り当てれば毎回Alt+F8を押さなくて済む。方法は マクロをボタン1つで実行する方法 を参照。

コード(基本版)– PasteSpecialで転置貼り付け

書式も含めてそのまま行列を入れ替えたい場合はこの方法が最もシンプル。


'============================================================
' ■ PasteSpecialで行列を入れ替え(基本版)
'   → データシートの指定範囲を転置して結果シートに貼り付け
'============================================================
Sub TransposeByPasteSpecial()

    '--- ★書き換えポイント ---
    Dim srcSheet As String
    srcSheet = "データ"           '← 転置元のシート名

    Dim destSheet As String
    destSheet = "結果"            '← 転置先のシート名

    Dim srcRange As String
    srcRange = "A1:D3"            '← 転置する範囲

    Dim destCell As String
    destCell = "A1"               '← 転置先の開始セル
    '--- ★ここまで ---

    '--- 転置元をコピー
    ThisWorkbook.Worksheets(srcSheet).Range(srcRange).Copy

    '--- 転置貼り付け(Transpose:=True がポイント)
    ThisWorkbook.Worksheets(destSheet).Range(destCell).PasteSpecial _
        Paste:=xlPasteAll, _
        Operation:=xlNone, _
        SkipBlanks:=False, _
        Transpose:=True

    '--- コピー状態を解除(点線枠を消す)
    Application.CutCopyMode = False

    MsgBox "転置が完了しました。", vbInformation

End Sub

書き換えポイント

変数 説明 初期値
srcSheet 転置元のシート名 "データ"
destSheet 転置先のシート名 "結果"
srcRange 転置する範囲 "A1:D3"
destCell 転置先の開始セル "A1"

ポイント: Paste:=xlPasteAll は書式も含めて全てを貼り付ける。値だけにしたい場合は Paste:=xlPasteValues に変更する。

コードの処理の流れ:

このコードは3つのステップで動いている。まず Range.Copy で転置元のセル範囲をクリップボードにコピーする。次に PasteSpecial メソッドで転置先に貼り付けるが、ここで Transpose:=True を指定することで行と列が入れ替わった状態で貼り付けられる。PasteSpecialはExcelの「形式を選択して貼り付け」と同じ機能をVBAから呼び出しているだけなので、手作業でやっている人にはイメージしやすいはずだ。最後に Application.CutCopyMode = False でコピー状態を解除する。これを入れないと、セルの周囲に点線枠(コピー中を示すマーキー)が残り続けて見た目が悪い。

コード(応用版)– WorksheetFunction.Transposeで転置

配列として値だけを転置したい場合はこちら。PasteSpecialと違い、コピー状態(点線枠)が発生しない。


'============================================================
' ■ WorksheetFunction.Transposeで行列を入れ替え(応用版)
'   → 値のみ転置(書式はコピーしない)
'============================================================
Sub TransposeByFunction()

    '--- ★書き換えポイント ---
    Dim srcSheet As String
    srcSheet = "データ"           '← 転置元のシート名

    Dim destSheet As String
    destSheet = "結果"            '← 転置先のシート名

    Dim srcRange As String
    srcRange = "A1:D3"            '← 転置する範囲

    Dim destCell As String
    destCell = "A1"               '← 転置先の開始セル
    '--- ★ここまで ---

    Dim wsSrc As Worksheet
    Set wsSrc = ThisWorkbook.Worksheets(srcSheet)

    Dim wsDest As Worksheet
    Set wsDest = ThisWorkbook.Worksheets(destSheet)

    '--- 転置元のデータを配列に読み込み
    Dim srcData As Variant
    srcData = wsSrc.Range(srcRange).Value

    '--- WorksheetFunction.Transpose で転置
    Dim result As Variant
    result = WorksheetFunction.Transpose(srcData)

    '--- 転置結果を書き込み(行数と列数が入れ替わる)
    Dim srcRows As Long
    srcRows = wsSrc.Range(srcRange).Rows.Count

    Dim srcCols As Long
    srcCols = wsSrc.Range(srcRange).Columns.Count

    wsDest.Range(destCell).Resize(srcCols, srcRows).Value = result

    MsgBox "転置が完了しました(値のみ)。", vbInformation

End Sub

書き換えポイント

変数 説明 初期値
srcSheet 転置元のシート名 "データ"
destSheet 転置先のシート名 "結果"
srcRange 転置する範囲 "A1:D3"
destCell 転置先の開始セル "A1"

コードの処理の流れ:

このコードではまず Range.Value でセルの値を一括で配列に読み込んでいる。セルを1つずつ読むのと比べて、配列に一括で読み込む方がはるかに速い。次に WorksheetFunction.Transpose で行と列を入れ替えた新しい配列を生成する。この関数はExcelワークシート上の TRANSPOSE 関数と同じ機能をVBAから呼び出している。最後に Resize で転置後のサイズ(行数と列数が入れ替わるので Resize(srcCols, srcRows) になる)を指定して、配列の値を一括で書き込む。PasteSpecial方式と違い、クリップボードを経由しないため CutCopyMode の解除が不要で、コードがすっきりする。

注意: WorksheetFunction.Transpose には 65,536行/列 の上限がある。これを超えるデータではエラーになる。大量データの場合は配列ループ(FAQ Q3を参照)を使う。

大量データの高速処理については 配列を使ってVBAの処理速度を10倍にする方法 も参考になる。

コード(実務版)– 月別横持ちデータを縦持ちに変換

実務で最も多いのが「月別に横に並んだデータを縦持ちに変換する」パターン。ピボットテーブルでの集計や、データベースへの取り込みには縦持ちデータが必要になる。

横持ち→縦持ち変換を覚えてからは、ピボットテーブルに入れやすい形にデータを整形できるようになった。集計作業全体が楽になった。


'============================================================
' ■ 横持ちデータを縦持ちに変換(実務版)
'   → 元データ: 行=拠点、列=月 の横持ち
'   → 変換後:  拠点・月・値 の縦持ち(3列)
'============================================================
Sub ConvertWideToLong()

    '--- ★書き換えポイント ---
    Dim srcSheet As String
    srcSheet = "データ"           '← 元データのシート名

    Dim destSheet As String
    destSheet = "結果"            '← 変換結果のシート名

    Dim headerRow As Long
    headerRow = 1                 '← ヘッダー行(月名が並ぶ行)

    Dim dataStartRow As Long
    dataStartRow = 2              '← データの開始行

    Dim labelCol As Long
    labelCol = 1                  '← ラベル列(A列=拠点名)

    Dim dataStartCol As Long
    dataStartCol = 2              '← データ開始列(B列=4月〜)

    Dim destHeaders As Variant
    destHeaders = Array("拠点", "月", "売上")  '← 変換後のヘッダー
    '--- ★ここまで ---

    Dim wsSrc As Worksheet
    Set wsSrc = ThisWorkbook.Worksheets(srcSheet)

    Dim wsDest As Worksheet
    Set wsDest = ThisWorkbook.Worksheets(destSheet)

    '--- 元データの範囲を取得
    Dim lastRow As Long
    lastRow = wsSrc.Cells(wsSrc.Rows.Count, labelCol).End(xlUp).Row

    Dim lastCol As Long
    lastCol = wsSrc.Cells(headerRow, wsSrc.Columns.Count).End(xlToLeft).Column

    '--- データがあるか確認
    If lastRow < dataStartRow Then
        MsgBox "変換するデータがありません。", vbExclamation
        Exit Sub
    End If

    '--- 転置先をクリア
    wsDest.Cells.ClearContents

    '--- ヘッダーを書き込み
    Dim h As Long
    For h = 0 To UBound(destHeaders)
        wsDest.Cells(1, h + 1).Value = destHeaders(h)
    Next h

    '--- 横持ち → 縦持ち変換
    Dim outRow As Long
    outRow = 2                    '← 書き込み開始行

    Dim r As Long, c As Long
    For r = dataStartRow To lastRow
        For c = dataStartCol To lastCol
            '--- 拠点名
            wsDest.Cells(outRow, 1).Value = wsSrc.Cells(r, labelCol).Value
            '--- 月名(ヘッダー行から取得)
            wsDest.Cells(outRow, 2).Value = wsSrc.Cells(headerRow, c).Value
            '--- 値
            wsDest.Cells(outRow, 3).Value = wsSrc.Cells(r, c).Value
            outRow = outRow + 1
        Next c
    Next r

    MsgBox (outRow - 2) & " 行の縦持ちデータを作成しました。", vbInformation

End Sub

書き換えポイント

変数 説明 初期値
srcSheet 元データのシート名 "データ"
destSheet 変換結果のシート名 "結果"
headerRow 月名が並ぶヘッダー行 1
dataStartRow データの開始行 2
labelCol ラベル列(拠点名など) 1(A列)
dataStartCol データの開始列 2(B列)
destHeaders 変換後のヘッダー名 "拠点", "月", "売上"

コードの処理の流れ:

この実務版コードは、横持ちデータを縦持ちに「展開」する処理を行っている。単純な行列入れ替えとは違い、1行のデータが複数行に展開される点が特徴だ。外側の For r ループでデータ行(拠点)を、内側の For c ループでデータ列(月)を回している。ループの中では、拠点名はヘッダー行ではなくラベル列(A列)から取得し、月名はヘッダー行から取得している。こうすることで、元データの構造に依存せず柔軟に変換できる。outRow という変数で書き込み先の行番号を管理しており、1セル書くごとに1ずつ増やしている。最終行の取得には End(xlUp) を使っているので、データ件数が変わっても自動で対応する。最終行取得の詳しい仕組みは データの最終行・最終列を正確に取得する方法 を参照。

行列の入れ替えではなく、単純にセルの値を別シートにコピーしたい場合は セルの転記を自動化する方法 を参照。

よくある落とし穴5選

1. CutCopyMode の解除忘れで点線枠が残る

原因: PasteSpecial方式でコピー後に Application.CutCopyMode = False を書き忘れた。

対策: PasteSpecialの後には必ず Application.CutCopyMode = False を入れる。基本版コードには最初から入っている。

2. WorksheetFunction.Transpose で「関数の引数が正しくありません」エラー

自分もこれで引っかかった。WorksheetFunction.Transposeで65,536行を超えるデータを転置しようとしてエラーになった。上限があることを知らなかった。

対策: 65,536行/列を超える場合は配列ループ方式を使う(FAQ Q3を参照)。

3. 転置先の範囲指定で行数と列数を逆にし忘れた

原因: 元データが3行×4列なら、転置後は4行×3列になる。Resize の引数を元データと同じにするとサイズが合わない。

対策: Resize(srcCols, srcRows) のように行と列を入れ替えて指定する。

4. 同じシートで転置したら元データが壊れた

原因: 転置元と転置先の範囲が重なっていると、コピー中にデータが書き換わる。

対策: 転置先は別シートにするか、元データと重ならない位置を指定する。

5. 1行 or 1列のデータで WorksheetFunction.Transpose の結果がおかしい

原因: 1行だけのデータを WorksheetFunction.Transpose にかけると、結果が2次元配列ではなく1次元配列になる場合がある。

対策: 複数行×複数列のデータでは問題ない。1行/1列の場合はPasteSpecial方式を使う。

VBAのTranspose関数でエラーが出るときの対処法

「WorksheetFunction.Transposeを実行すると『関数の引数が正しくありません』というエラーが出る」という場合、原因はデータの行数または列数が65,536を超えていることだ。Transpose関数にはExcelの配列サイズの上限があり、大量データでは使えない。自分も10万行のデータを転置しようとしてこのエラーに遭遇した。対処法は、FAQ Q3で紹介している配列ループ方式に切り替えること。ReDim result(1 To cols, 1 To rows) で結果配列を用意し、ネストしたForループで result(c, r) = src(r, c) と値を入れ替えれば、データ量の制限なく転置できる。

VBAで行列入れ替え時に大量データで処理が失敗するときの対処法

「数千行のデータを転置しようとしたら、処理が途中で止まる・Excelが固まる」という場合、原因は画面更新と自動計算がONのまま大量のセル書き込みを行っていることだ。転置処理はセルの読み書き回数が多いため、画面更新のオーバーヘッドが大きい。対処法は、処理の前に Application.ScreenUpdating = FalseApplication.Calculation = xlCalculationManual を入れ、処理後に元に戻すこと。さらに、セルを1つずつ読み書きするのではなく配列に一括で読み込んでから処理すると、体感で数十倍速くなる。配列を使ってVBAの処理速度を10倍にする方法も参考になる。

FAQ

Q1: 書式も含めて転置したい

基本版(PasteSpecial方式)を使う。Paste:=xlPasteAll で書式ごと転置される。

Q2: 値だけ転置したい(書式はいらない)

応用版(WorksheetFunction.Transpose方式)を使う。または基本版で Paste:=xlPasteValues に変更する。

Q3: 65,536行を超えるデータを転置したい

配列ループで転置する:


Dim src As Variant
src = Range("A1:D100000").Value

Dim rows As Long, cols As Long
rows = UBound(src, 1)
cols = UBound(src, 2)

Dim result() As Variant
ReDim result(1 To cols, 1 To rows)

Dim r As Long, c As Long
For r = 1 To rows
    For c = 1 To cols
        result(c, r) = src(r, c)
    Next c
Next r

Range("F1").Resize(cols, rows).Value = result

Q4: 横持ちデータを縦持ちに変換したい

実務版コードを参照。月別データの横→縦変換に対応している。変換後のデータはピボットテーブルのソースデータとしてそのまま使える。

Q5: 転置後に元データを消したい

転置処理の後に元データをクリアする。誤消去を防ぐため確認ダイアログを挟む:


Dim ans As VbMsgBoxResult
ans = MsgBox("元データを消去しますか?", vbYesNo + vbQuestion)
If ans = vbYes Then
    wsSrc.Range(srcRange).ClearContents
End If

まとめ

  • PasteSpecial方式: 書式も含めて転置。Transpose:=True を指定するだけ
  • WorksheetFunction.Transpose: 値だけ転置。ただし65,536行/列の上限あり
  • 配列ループ: 上限なし。大量データやカスタム処理向き
  • 横持ち→縦持ち変換: 実務版コードでピボットテーブル用のデータ整形ができる

関連記事

次にやりたくなること

転置の自動化ができたら、次はこのあたりに挑戦してみてほしい。

もっとカスタマイズしたい場合

「横持ち→縦持ちの変換ルールが複雑(複数のラベル列がある)」「複数シートのデータを一括で転置したい」「転置後に自動で集計・グラフ作成もしたい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できる。

相談時に伝えると話が早い情報:

  • Excel のバージョン / OS
  • 元データの構成(行がどの項目で、列がどの項目か)
  • データの行数・列数の目安
  • 変換後にどう使うか(ピボットテーブル・データベース取り込みなど)

コメント

タイトルとURLをコピーしました