・Raspberry Pi Zero WHを使って、格安で温湿度データロガーを作る。(2/2)【Pythonスクリプトの作成】
詳解! Google Apps Script完全入門 [第3版]
by SimpleImageLink
今回はとりあえず10月から2月途中までのデータですが、1万行を超えているので手作業ではしんどいですね。あと、データが飛んでいたり重複したりというところが多少あったので、その部分の修復も自動化してみました。
備忘録がてら、以下作り方です。
***
まずは、15分毎の温度・湿度データを1時間平均に変換します。
スプレッドシートIDは適宜。
シートは元データが入ったものが「raw_data」、1時間平均に直したものを入れるのが「data」になります。
それぞれ、A列が「日時」、B列が「温度」、C列が「湿度」、1行目が見出しという構成です。
//15分毎のデータを1時間平均に変換 function setHourAve() { const spreadsheet = SpreadsheetApp.openById("スプレッドシートID"); const rdSheet = spreadsheet.getSheetByName("raw_data"); const dSheet = spreadsheet.getSheetByName("data"); let lastRow = rdSheet.getLastRow(); let dt_raw = rdSheet.getRange(2, 1, lastRow, 1).getValues(); let tm_raw = rdSheet.getRange(2, 2, lastRow, 1).getValues(); let hu_raw = rdSheet.getRange(2, 3, lastRow, 1).getValues(); let data = []; let dt_h = ""; let tm_h = ""; let hu_h = ""; let count_h = 1; let count_row = 0; for(let x = 0; x < lastRow - 1; x++) { dt_h = Utilities.formatDate(new Date(dt_raw[x]), "JST", "MM/dd HH"); tm_h = Number(tm_raw[x]); hu_h = Number(hu_raw[x]); for(let y = 1; y < 5; y++) { if(dt_h == Utilities.formatDate(new Date(dt_raw[x + y]), "JST", "MM/dd HH")) { tm_h = tm_h + Number(tm_raw[x + y]); hu_h = hu_h + Number(hu_raw[x + y]); count_h++; } } data.push([Utilities.formatDate(new Date(dt_raw[x]), "JST", "yyyy/MM/dd HH:00:00"), tm_h / count_h, hu_h / count_h]); x = x + count_h - 1; count_h = 1; count_row++; } dSheet.getRange(2, 1, count_row, 3).setValues(data); }
※ コードを一部修正(2022-3-5)
15分毎にデータを取得しているので、通常は1時間あたり4回ほど温度・湿度が記録されるのですが、たまーに5回取れてしまうことがあったようです。そうすると、同じ日時の行が重複してしまいます。その対策として、23行目のfor文は5回ループさせています。
次に、1時間平均に変換したデータに欠損値等がないかをチェックします。あった場合には修復します。
//欠損値を探索・修復 function srchMissValu() { const spreadsheet = SpreadsheetApp.openById("スプレッドシートID"); const dSheet = spreadsheet.getSheetByName("data"); let lastRow = dSheet.getLastRow(); let dt_data = dSheet.getRange(3, 1, lastRow, 1).getValues(); let dt = ""; let dt_next = ""; let diff = 0; let x = 0; let y = 0; for(let i = 0; i < lastRow - 2 + x; i++) { dt = new Date(dt_data[i]); dt_next = new Date(dt_data[i + 1]); if((dt_next - dt) / 1000 > 3600) { //欠損データ(日時)の追加 diff = (dt_next - dt) / 1000 / 3600; for(let j = 0; j < diff - 1; j++) { dSheet.insertRowAfter(2 + i + x); dSheet.getRange(3 + i + x, 1).setValue(Utilities.formatDate(new Date(dt.setHours(dt.getHours() + 1)), "JST", "yyyy/MM/dd HH:00:00")); Logger.log("追加 " + (3 + i + j) + "行目:" + Utilities.formatDate(new Date(dt), "JST", "yyyy/MM/dd HH:00:00")); x++; } } else if (dt_next - dt == 0) { //重複データの削除 dSheet.deleteRow(3 + i + x); Logger.log("削除 " + (3 + i + x) + "行目:" + Utilities.formatDate(dt, "JST", "yyyy/MM/dd HH:00:00")); x--; } y = i + 3; } Logger.log("データを" + y + "行目までチェックしました。"); }
※ コードを一部修正(2022-3-5)
今回、欠損値というは、1時間平均が「2021/10/01 16:00:00」から「2021/10/01 18:00:00」に飛んでしまっているようなパターンです。時間が飛んでいると気象庁の過去の気象データと比較する際に行がずれてしまうため、「2021/10/01 17:00:00」を追記する必要があります。
また、元データの方で1時間に6回以上値が入っていると「2021/10/01 16:00:00」の次の行に再度「2021/10/01 16:00:00」が入ってしまうので、その場合は重複したデータの行を削除します。(5回ループのfor文のおかげで、今回は該当ありませんでした。)
***
ということで、無事に処理完了。こういうのは分かりやすく便利ですね。
今後、いろいろ使いまわせそうな気がします。
ちなみに、ちょっと混乱したのですが、Utilities.formatDateで年を取得するときは小文字(yyyy)だそうです。
大文字(YYYY)ですと、翌年の1月1日と同じ週が翌年として表示されてしまうようです。例えば2021年の場合、12月26日〜31日が2022年12月26日〜31日と表示されてしまいます。(参考 :「SimpleDateFormatで年を表すときはyyyy」)
過去に作ったスクリプト内でもYYYYになっているものがあったので修正しました。
GAS、日付や配列の操作にだいぶ慣れてきました。Pythonの方も頑張っていきましょう。
詳解! Google Apps Script完全入門 [第3版]
by SimpleImageLink
雪だるま きな粉
Twitter(@nkkmd)日々更新中です。