かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Python データにコンマ , を含む CSV をいい感じに扱いたい

スプレッドシートで JINNER JOIN, OUTER JOIN をしたかったけど VLOOKUP 関数だと1つのセルしか取ることができなかったので、面倒くささが爆発して PythonスプレッドシートCSV でダウンロードして加工していました。
雑に file open して改行とコンマ , で配列にしていたら、JSONデータとか , を含むデータがあるとセルが壊れてしまったので、PythonCSVをいい感じに扱うのメモ。

sample.csv

id, name, data
1, 星宮 いちご, "{""type"": ""cute"", ""unit"": [""soleil"", ""STAR☆ANIS"", ""2wingS"", ""コスモス"", ""ラッキーストロベリー""]}"
2, 霧矢 あおい, "{""type"": ""cool"", ""unit"": [""soleil"", ""STAR☆ANIS""]}"
3, 紫吹 蘭, "{""type"": ""sexy"", ""unit"": [""soleil"", ""STAR☆ANIS""]}"

CSV が壊れてしまうダメな例

def readCSV(filePath):
  try:
    with open(filePath, "r", encoding="utf-8") as f:
      data = f.read().split("\n")
      return {
        "header": data[0].split(","),
        "data": [row.split(",") for row in data[1:]]
      }
  except Exception as err:
    print(err)
    return None

csvData = readCSV("sample.csv")
print(csvData)

↓ 出力結果

{
  'header': ['id', 'name', 'data'],
  'data': [
    ['1', '星宮 いちご', '"{""type"": ""cute""', ' ""unit"": [""soleil""', ' ""STAR☆ANIS""', ' ""2wingS""', ' ""コスモス""', ' ""ラッキーストロベリー""]}"'],
    ['2', '霧矢 あおい', '"{""type"": ""cool""', ' ""unit"": [""soleil""', ' ""STAR☆ANIS""]}"'],
    ['3', '紫吹 蘭', '"{""type"": ""sexy""', ' ""unit"": [""soleil""', ' ""STAR☆ANIS""]}"']
  ]
}

, を含むデータ部分で分割されてしまう
(ᐡ o̴̶̷̤ ﻌ o̴̶̷̤ ᐡ) ぴえぴえ

, を含む CSV データを扱う Good な方法

csv 標準ライブラリを使用する

csv.reader(csvfile, dialect='excel', **fmtparams)
与えられた csvfile 内の行を反復処理するような reader オブジェクトを返します。
cf. csv --- CSV ファイルの読み書き — Python 3.9.2 ドキュメント

import csv

def readCSV(filePath):
  try:
    with open(filePath, "r", encoding="utf-8") as f:
      reader = csv.reader(f)
      csvData = [row for row in reader]
      header, *data = csvData

      return {
        "header": header,
        "data": data
      }
  except Exception as err:
    print(err)
    return None

csvData = readCSV("sample.csv")
print(csvData)

↓ 出力結果

{
  'header': ['id', 'name', 'data'],
  'data': [
    ['1', '星宮 いちご', '{"type": "cute", "unit": ["soleil", "STAR☆ANIS", "2wingS", "コスモス", "ラッキーストロベリー"]}'],
    ['2', '霧矢 あおい', '{"type": "cool", "unit": ["soleil", "STAR☆ANIS"]}'],
    ['3', '紫吹 蘭', '{"type": "sexy", "unit": ["soleil", "STAR☆ANIS"]}']
  ]
}

いい感じのCSVがいい感じです!
₍ ᐢ. ̫ .ᐢ ₎ YATTANE!!!

ポエム

ふつーに考えたら split(",") なんてしたらデータ中にコンマ , 含まれてたらデータ壊れるわ。って考えがすっぽり抜け落ちてしまっていたのでお脳の劣化がやゔぁい…
3月から join した会社での稼働時間が毎日長すぎてコードかけないからドンドン忘れていっています!
若い人が多いのに MTG ばかりして、就業時間後くらいにしか作業ができない & 土日も働くのが平気みたいな文化で非常に辛い。全社MTGで新入社員紹介に付き合った人の遍歴言わせるとかパワハラ体質な感じもあって、最近の若い子は一周回って昭和的な文化なのか、営業的な考えの人が上層部にいるとそういう文化になってしまう環境起因のか、人間の性質形成の例として大変興味深くなっています。次はちゃんとしたテックカンパニーを探します…
コード書ける環境に身を置きたいので各位よろしくおねがいします!!


[参考]

いい感じのお皿がいい感じです!な まちカドまぞく 6巻読みたいけどどこに売ってるの〜?

GAS SpreadSheet IMPORTRANGE で 外部シートを読み込もうとして 循環依存が検出されました。なエラーにハマる

PM っぽいことをしてて SpreadSheet を触るばかりの日々を過ごしています。 お久しぶりに SpreadSheet で他のシートからの参照をしててエラーになってしまったのでメモ。

IMPORTRANGE で外部のシートを読み込もうとしたら 循環依存が検出されました。 というエラーになった

=IMPORTRANGE('SpreadSheet_KEY', シート!A:Z)

こんな風に書いたら 循環依存が検出されました。 なエラーになってしまった。

IMPORTRANGE の範囲指定は " で囲って文字列にする必要がある。

  • 範囲の文字列 - 読み込む範囲を指定する文字列で、"[シート名!]範囲" の形式で指定します(例: "シート1!A2:B6"、"A2:B6")。
    • 範囲の文字列のシート名の要素は省略可能です。デフォルトでは、IMPORTRANGE 関数は最初のシートの指定範囲から読み込みます。
    • 範囲の文字列の値は二重引用符で囲むか、適切なテキストを含むセルへの参照にする必要があります。

cf. IMPORTRANGE - ドキュメント エディタ ヘルプ

IMPORTRANGE を使っているシート名が シート と同じだったので、今見ているシートを範囲として取ろうとして 循環依存 のエラーになってしまっているっぽい。
範囲を " で囲って文字列にすればOK

=IMPORTRANGE('SpreadSheet_KEY', "シート!A:Z")

₍ ᐢ. ̫ .ᐢ ₎ok 触ってないとすぐ忘れますね。(コードが書きたい)

IMPORTRANGE なんだから、当然 key で指定してるシート無いから範囲参照するものだと思ってたら違ってた。仕様が謎〜
SpreadSheet 関連ググっても公式のドキュメントより他の使い方 blog ばかり検索上位になるのどうにかして欲しい。

関係ないけど、スプレッドシートスプシ みたいに社内でしか通じないような略し方が浸透してるのは悪い文化だと思ってるので辞めて〜


[参考]

GAS SpreadSheet 複数のシートのデータをまとめて表示したい

1日目, 2日目, 3日目のイベント情報のデータがあってそれぞれ別のシートにあるから全体を見れるように 1つのシートで表示したいみたいなケース

day1

A B C D
1 id name day action
2 1 星宮いちご day1 xxx
3 2 霧矢あおい day1 xxx
4 3 紫吹蘭 day1 xxx

day2

A B C D
1 id name day action
2 4 大空あかり day1 xxx
3 5 氷上すみれ day1 xxx
4 6 新条ひなき day1 xxx

day3

A B C D
1 id name day action
2 7 神崎美月 day1 xxx
3 8 夏樹みくる day1 xxx
4 9 紅林珠璃 day1 xxx

これを縦に連結した 1 つのシートを作成したい

QUERY 関数で複数シートを連結表示できる

データを縦に連結する場合は各範囲を {} 内に ; 区切りで指定すればOK

=QUERY({範囲A; 範囲B; 範囲C}, "select *")

上記の day1, day2, day3 シートのデータをまとめるなら

=QUERY({day1!A:D; day2!A:D; day3!A:D}, "select *")

と書けば OK そうなのですが、これでは day1 のデータしか表示されません。

各シートのデータが入っている範囲を指定しなければならない

=QUERY({day1!A:D; day2!A:D; day3!A:D}, "select *")

この書き方の場合 day1 シートの A:D の範囲から入力の有無に関わらず全行取ってこようとするので、行数の最大値まで day_1 のデータで埋まってしまっていた為に day1 のデータしか表示されないように見えていました。

範囲が固定なら day1!A:D100 のように範囲を指定してあげれば OK です

=QUERY({day1!A:D4; day2!A:D4; day3!A:D4}, "select *")

範囲が動的な場合は QUERY を使って入力がある行を判定させる

固定範囲にした場合、元データに変更があると集計データのコードも変更しなければならないので面倒です。
なので各シートの入力がある行だけを取ってくるように変更します。

=QUERY({
  QUERY(day1!A:D4, "SELECT * WHERE A is not null");
  QUERY(day2!A:D4, "SELECT * WHERE A is not null");
  QUERY(day3!A:D4, "SELECT * WHERE A is not null")
}, "select *")

QUERY 関数の {} 内は QUERY を入れ子に出来るので、各シートからデータを取ってくる段階で入力のない行を除外してやればOK

これで動的に各シートのデータを集計した表示ができるようになりました!
最初のシートのデータしか表示されなかった時に空白の行も取ってきている事になかなか気づけませんでした。
これは罠!


[参考]