山pの楽しいお勉強生活

勉強の成果を垂れ流していきます

ユーザ認証でGoogleドライブ上のファイルをPythonで操作する

はじめに

  • コードはドキュメント通りだが、認証に1日かかったためメモ
  • 本記事ではユーザ認証を使用する
    • バッチ処理などで使用するためにはサービスアカウントを使用する
  • キャプチャはないが、参考リンクは大目に貼っているので、解決しない場合でもリンクは見てみることを推奨

前提

  • ブラウザ上でファイルの取得は可能であることを確認
  • privateのファイル
    • 認証あり。インターネットに大公開しているファイルではない
  • 認証関連の設定が終わった後のコードは↓の通り
  • GCPへのアクセスが可能であること
    • プロジェクトは作成済み
  • gcloudコマンドを使用可能であること

手順

memo

  • gcloud auth application-default login で作成される認証情報の格納PATH
    • Windows: %APPDATA%\gcloud\application_default_credentials.json
    • Mac(未確認): ~/.config/gcloud/application_default_credentials.json

参考

npm install と npm ci をどのように使い分けるか

※タイトルの npm install は引数なしを想定しています。

一言

  • 引数なしの npm install は使わない
  • 環境構築時には npm ci を使用する

詳細

  • npm install
    • 引数なしの場合、package.json に沿った形で依存ライブラリをインストールする
      • package-lock.jsonは見ない
      • 依存ライブラリの依存ライブラリは最新版がインストールされる
      • 依存ライブラリの指定の範囲での最新版
      • package-lock.jsonを更新する
    • 用途
      • 引数なしは使わない
      • 新規ライブラリの追加、既にpackage.jsonに記載があるライブラリのバージョンアップを行う時
  • npm ci
    • package-lock.jsonに沿った形で依存ライブラリをインストールする
      • 依存ライブラリの依存ライブラリは package-locl.jsonに記載があるバージョンでインストールされる
      • package-lock.json は更新されない
    • 用途
      • そのリポジトリで想定されている環境を作る場合
      • 開発環境、CI環境、本番環境等
    • その他
      • npm install よりもかなり早い
  • 依存ライブラリの更新を定期的に行う場合、CIで npm install をして更新があればpackage-lock.jsonをPRさせるのが良さそう。
  • package.json自体の更新はdependabotを使うと良い。

きっかけ

  • 引数なしの npm install は package-lock.json を更新しないと思っていたが更新された(単純に記憶違い)
  • 各種環境構築スクリプトnpm install が使われていたが、package-lock.json が更新されるため、差分が発生する
  • 社内で聞いたら教えてくれた↑↑の通り教えてくれた

参考URL


追記(2022/05/28)

id:efcl から以下のブコメをもらったので追記。(よくわかっていないのでそのうちもう少し調べる)

npm install と npm ci をどのように使い分けるか - 山pの楽しいお勉強生活

引数なしの`npm install`がpackage-lock.jsonを更新するかは、npmのバージョンで動作が異なる感じ。 <a href="https://github.com/azu/npm-install-update-package-lock/actions/runs/2399517030" target="_blank" rel="noopener nofollow">https://github.com/azu/npm-install-update-package-lock/actions/runs/2399517030</a> <a href="https://github.com/npm/rfcs/issues/415#issuecomment-938279066" target="_blank" rel="noopener nofollow">https://github.com/npm/rfcs/issues/415#issuecomment-938279066</a>

2022/05/28 10:44
b.hatena.ne.jp

  • azuさんのGitHub Actionsの結果を見ると、npm6とnpm8で npm install をした結果、npm6はlockファイル内の依存ライブラリのバージョンが変更されているが、npm8は変わっていなかった。
  • 2つ目のリンクの内容はよくわからない
  • 少し調べたところ、npm7からlockファイルのバージョンが上がり、 npm install を行った際にバージョン2に書き換わるらしいのだが関係ある?

PySparkでDataFrame.cacheはMEMORY_AND_DISKレベルキャッシュされる

概要

※2021/12/17時点の最新版であるPySpark3.2.0の情報です

詳細

Xiaomi Pad 5(MIUI12)を複数アカウント(ユーザー)で使用する

はじめに

  • Xiaomi Pad5を購入したものの、デフォルトでは複数ユーザーで使用ができない?
    • 端末というより、OSであるMIUIの仕様?
  • とりあえず複数ユーザーで使用できるようになったので手順をメモする
    • ただし、色々制限はかかっているの注意。詳細は下記参照
  • 他にも良い手順があったらコメントでもTwitterでも良いので教えて下さい

わかる人向けの手順概要

※よくわからない人は下記にキャプチャ付きの詳細手順あります。

  1. 設定 → デバイス情報
  2. MIUIバージョンを10回タップ
  3. 開発者向けオプションを有効
  4. 追加設定 → 開発者向けオプション
  5. デフォルト値にリセット
    • ※全然関係ない項目のように見えるがここが一番のハマりポイント
  6. 最下部のMIUI最適化をオフ
  7. 複数ユーザー → 追加
  8. 切り替え
  9. Google関連の設定はOFFにして初期設定をする

よくわかっていないこと(困っていること)

  • ログイン時に追加したマルチユーザーでログインできない
    • 所有者(管理者)でログイン後に、設定からユーザを切り替える必要がある
    • Activity Launcher というアプリでユーザの切り替えのショートカットを作ることでとりあえずは対応
  • 追加したユーザーの初期設定でインターネットに繋がらない
    • Google関連の設定をONにして初期設定をするを行うと永遠に終わらない

詳細手順

  1. 設定 → デバイス情報 を表示
  2. MIUIバージョンを10回タップ
    • MIUIバージョンを複数回タップ
    • 何回かタップしていると「デベロッパーになるまであとXステップです。」と表示される。
    • デベロッパーになるまであと1ステップです。
  3. 規定回タップすると「これでデベロッパーになりました!」と表示される。
    • これでデベロッパーになりました!
  4. 追加設定内に「開発者向けオプション」が表示されているのでタップ
    • 追加設定の開発者向けオプション
  5. 開発者向けオプション内のデフォルト値にリセットを複数回タップする
    • ここからが本記事のキモ。私の環境だと「MIUIの最適化をオンにする」が表示されなかったので以下の操作を行う。
    • デフォルト値にリセットを複数回タップ
    • 「自動入力の開発者向けオプションをリセットしました」と表示される。
      • この設定は自動入力の設定なのでこの表示は正しい
    • 自動入力の開発者向けオプションをリセットしました
  6. 何回かタップしていると「MIUIの最適化をオンにする」が表示されるのでオフに切り替える
    • MIUIの最適化をオンにする
  7. 重要な警告を熟読してから同意
    • 重要な警告
  8. 複数ユーザーという項目が追加されているので選択
  9. オンに切り替え
  10. ユーザーまたはプロファイルを追加 → ユーザを追加する。
    • 複数ユーザー、ON、ユーザーまたはプロファイルを追加
  11. 追加されたユーザをタップして切り替える
    • yamapに切り替える
  12. ユーザの初期設定で「 Google関連の設定はOFF」にする

参考

GitHub ActionsでビルドしたドキュメントをGitHub Pagesで表示する

まとめ

はじめに

GitHub Pagesの存在は知っていてもprivateリポジトリで使用できないと思っている方は多いと思いますが、2021/01/21より「GitHub Enterprise Cloud 」であればprivateリポジトリで使用できるようになっています。(正確にはアクセス制御ができるようになった)

github.blog

変更方法などは以下のドキュメントを参照

docs.github.com

とは言え、GitHub Pagesの管理はそれなりに面倒です。 そこで、GitHub Actionsを併用する事でGitHubのイベントをトリガーにしてGitHub Pagesを簡単に更新する事ができたのでまとめておきます。

よくある問題

  • CIでドキュメントをビルドしているが有効活用されない
  • ビルドしたドキュメントを置く場所がない
  • ビルドしたドキュメントにアクセスするのが面倒

手順

具体的なサンプル

注意点

参考

  • 公式ドキュメントはかなり充実しています

PySparkではDataFrameのjoinでorderは維持されない

概要

  • PySparkのDataFrameではjoinした際にorderは維持されない
    • 正確にはshuffleが行われる
  • orderは出力直前に行うのが鉄則

再現コード

from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
df1 = spark.createDataFrame(
    [
        ['user_01'],
        ['user_02'],
        ['user_03'],
        ['user_04'],
        ['user_05'],
    ],
    ['id'],
)
df2 = spark.createDataFrame(
    [
        ['user_01', '1'],
        ['user_02', '2'],
        ['user_03', '3'],
        ['user_04', '4'],
        ['user_05', '5'],
    ],
    ['id', 'c1']
)
print('order byされているdf1')
df1 = df1.orderBy('id')
df1.show()

print('order byされているdf2')
df2 = df2.orderBy('id')
df2.show()

print('join後')
df1.join(df2, 'id').show()
order byされているdf1
+-------+
|     id|
+-------+
|user_01|
|user_02|
|user_03|
|user_04|
|user_05|
+-------+

order byされているdf2
+-------+---+
|     id| c1|
+-------+---+
|user_01|  1|
|user_02|  2|
|user_03|  3|
|user_04|  4|
|user_05|  5|
+-------+---+

join後
+-------+---+
|     id| c1|
+-------+---+
|user_03|  3|
|user_02|  2|
|user_01|  1|
|user_04|  4|
|user_05|  5|
+-------+---+

参考

PySparkのDataFrameでは同名のカラムが許容される

概要

  • PySparkのDataFrameでは同名のカラムが許容される
  • select などカラム名を指定する処理時に例外が発生する
  • カラム名を再定義、別名を付ける事で回避が可能

再現コード

作成時にカラム名が重複

from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
df99 = spark.createDataFrame(
    [
        [1, 'a', 'b'],
        [2, 'aa', 'bb'],
    ],
    ['id', 'c1', 'c1'],
)
df99.show()
+---+---+---+
| id| c1| c1|
+---+---+---+
|  1|  a|  b|
|  2| aa| bb|
+---+---+---+

※このパターンはあまりない気がする

joinした結果カラム目が重複

from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
df1 = spark.createDataFrame(
    [
        [1, 'a', 'b'],
        [2, 'aa', 'bb'],
    ],
    ['id', 'c1', 'c2'],
)
df2 = spark.createDataFrame(
    [
        [1, 'x', 'y'],
        [2, 'xx', 'yy'],
    ],
    ['id', 'c1', 'c2'],
)
merged_df = df1.join(df2, 'id')

# 重複カラムの存在は可能
merged_df.show()

# 重複していないカラムのselectは可能
merged_df.select('id').show()

# 重複しているカラムのselectは例外発生
merged_df.select('id', 'c1')
+---+---+---+---+---+
| id| c1| c2| c1| c2|
+---+---+---+---+---+
|  1|  a|  b|  x|  y|
|  2| aa| bb| xx| yy|
+---+---+---+---+---+

+---+
| id|
+---+
|  1|
|  2|
+---+

---------------------------------------------------------------------------
AnalysisException                         Traceback (most recent call last)
<ipython-input-20-69f4fa3aa508> in <module>
     24 
     25 # 重複しているカラムのselectは例外発生
---> 26 merged_df.select('id', 'c1')

/spark/python/pyspark/sql/dataframe.py in select(self, *cols)
   1419         [Row(name=u'Alice', age=12), Row(name=u'Bob', age=15)]
   1420         """
-> 1421         jdf = self._jdf.select(self._jcols(*cols))
   1422         return DataFrame(jdf, self.sql_ctx)
   1423 

/usr/local/lib/python3.7/site-packages/py4j/java_gateway.py in __call__(self, *args)
   1303         answer = self.gateway_client.send_command(command)
   1304         return_value = get_return_value(
-> 1305             answer, self.gateway_client, self.target_id, self.name)
   1306 
   1307         for temp_arg in temp_args:

/spark/python/pyspark/sql/utils.py in deco(*a, **kw)
    135                 # Hide where the exception came from that shows a non-Pythonic
    136                 # JVM exception message.
--> 137                 raise_from(converted)
    138             else:
    139                 raise

/spark/python/pyspark/sql/utils.py in raise_from(e)

AnalysisException: Reference 'c1' is ambiguous, could be: c1, c1.;

対応方法

toDFでカラムを指定して新しくDataFrameを作る

from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
df1 = spark.createDataFrame(
    [
        [1, 'a', 'b'],
        [2, 'aa', 'bb'],
    ],
    ['id', 'c1', 'c2'],
)
df2 = spark.createDataFrame(
    [
        [1, 'x', 'y'],
        [2, 'xx', 'yy'],
    ],
    ['id', 'c1', 'c2'],
)
merged_df = df1.join(df2, 'id')

# カラムを指定して新しくDataFrameを作る
df88 = merged_df.toDF('id', 'df1_c1', 'df1_c2', 'df2_c1', 'df2_c2')
df88.show()
+---+------+------+------+------+
| id|df1_c1|df1_c2|df2_c1|df2_c2|
+---+------+------+------+------+
|  1|     a|     b|     x|     y|
|  2|    aa|    bb|    xx|    yy|
+---+------+------+------+------+

https://spark.apache.org/docs/3.0.0/api/python/pyspark.sql.html#pyspark.sql.DataFrame.toDF

aliasをつけてselectする

from pyspark.sql import SparkSession
from pyspark.sql import functions as F
spark = SparkSession.builder.getOrCreate()
df1 = spark.createDataFrame(
    [
        [1, 'a', 'b'],
        [2, 'aa', 'bb'],
    ],
    ['id', 'c1', 'c2'],
)
df2 = spark.createDataFrame(
    [
        [1, 'x', 'y'],
        [2, 'xx', 'yy'],
    ],
    ['id', 'c1', 'c2'],
)
df1 = df1.alias('df1')
df2 = df2.alias('df2')
merged_df = df1.join(df2, 'id')

# showの見た目は変わらない
merged_df.show()

# alias付きで指定すると取得が可能
merged_df.select('id', 'df1.c1', 'df1.c2', 'df2.c1', 'df2.c2').show()
+---+---+---+---+---+
| id| c1| c2| c1| c2|
+---+---+---+---+---+
|  1|  a|  b|  x|  y|
|  2| aa| bb| xx| yy|
+---+---+---+---+---+

+---+---+---+---+---+
| id| c1| c2| c1| c2|
+---+---+---+---+---+
|  1|  a|  b|  x|  y|
|  2| aa| bb| xx| yy|
+---+---+---+---+---+

https://spark.apache.org/docs/3.0.0/api/python/pyspark.sql.html#pyspark.sql.Column.alias