データアナリストのメモ帳

データアナリストのメモ帳

IT企業で働くデータアナリストのブログ

【SQL】BigQueryでモンテカルロ法を用いて円周率の近似計算をする

BigQueryで2つの乱数を生成して円周率の近似値を求める方法です。 実用性は無いと思いますが、練習問題にはなるかもしれないですね。

WITH RandomValue AS (
SELECT
  num,
  RAND() AS rand1,
  RAND() AS rand2,
FROM UNNEST(GENERATE_ARRAY(1, 1000000)) AS num)

SELECT
  (pi / num) * 4 AS pi
FROM(
SELECT
  SUM(IF(POW(POW(rand1, 2) + POW(rand2, 2), 0.5) < 1, 1, 0))AS pi,
  COUNT(1) AS num
FROM
  RandomValue)

【SQL】BigQueryで素数の列を生成してみる

暇だったのでBigQueryで1000以下の素数の列を作りました。
素数の列がほしい時に使ってください。 NOT EXISTを使うのがポイントです!

WITH Numbers AS (
SELECT
  num 
FROM UNNEST(GENERATE_ARRAY(1, 1000)) AS num 
ORDER BY num)

SELECT 
  N1.num AS primeNumber
FROM 
  Numbers AS N1
WHERE
  N1.num > 1
  AND
    NOT EXISTS(
      SELECT 
        *
      FROM 
        Numbers AS N2
      WHERE 
        N1.num >= N2.num * N2.num
        AND N2.num > 1
        AND MOD(N1.num, N2.num) = 0);

【R言語】dplyrを使えばExcelの集計作業を代替できてしかも速い!

今回はR言語のdplyrの紹介です。
表計算をするなら使いたいパッケージ、それがdplyrです。

  • 項目ごとの集計(Excelで言うsumif的なもの)
  • ユニークカウント
  • Excelで言うVLOOKUPをLEFT JOINで行う

などなど・・・dplyrを身につければExcel要らず!になれるはずです。

では早速やってみましょう。
サンプルデータに台湾行政区人口順位表を使います。

ダウンロード
(クリックするとCSVファイルがダウンロードされます。)

CSVファイルの読み込み

まずはダウンロードしたファイルを読み込みます。
Rstudioで以下を実行してください。

taiwan_cities <- read.csv("taiwan_cities.csv", header=TRUE, fileEncoding="UTF-8-BOM")

このファイル、 fileEncoding="UTF-8 とすると読み込めなかったのですが、 fileEncoding="UTF-8-BOM こうすると読み込めました。なんでかはよくわからないです・・・

▼Rstudioでの読み込み結果がこちら
dplyr

次に、面積、人口、人口密度のクラスを変更します。試しに以下のように入力すると、

class(taiwan_cities$人口)
# [1] "factor"

と factor が返ってきます。数値に変換しないといけませんね。

CSV取り込みでよくやる前処理なのですが、ここではカンマを取ってから数値に直す、という処理をしないといけません。面倒ですがやってみましょう。

taiwan_cities$人口 <- gsub(",", "", taiwan_cities$人口)

こうすると、カンマ(",")をスペース("")で置換できます。面積と、人口密度にも同じ処理をしましょう。
さらに、as.numeric()でクラスを数値に変換します。小数点がある場合は、round()で整数にしてしまいましょう。

taiwan_cities$面積 <- round(as.numeric(gsub(",", "", taiwan_cities$面積)), digits = 0)

dplyrで集計をする

分類の列を見ると、直轄市、県、市という値が入っていることがわかります。
この分類ごとに、面積と人口を合計し、さらに人口密度を算出してみましょう。

library(dplyr)
taiwan_cities_bunrui <- taiwan_cities %>% dplyr::group_by(分類) 
%>% summarise(各分類の数 = n(), 
        面積の合計 = sum(面積), 
        人口の合計 = sum(人口), 
        人口密度 = round(人口の合計 / 面積の合計, digits = 0))

結果、こうなります。
dplyr

ここで、直轄市、県、市のそれぞれの数を知りたかったので、各分類の数 = n()として、個数を数えています。

さらにこれを人口で降順に並べ替えたいなら、arrange()とdesc()を用いて、

taiwan_cities_bunrui <- taiwan_cities_bunrui %>% dplyr::arrange(desc(人口の合計))

とすれば、降順で表示できます。

LEFT JOINで2つのテーブルをくっつける(VLOOKUP的な処理)

次に、各行政区に自分が行ったことがある/ないのテーブルを読み込みましょう。
まずは、CSVをダウンロード。

ダウンロード
(クリックするとCSVファイルがダウンロードされます。)

read.csv()で読み込みましょう。

taiwan_exp <- read.csv("taiwan_exp.csv", header=TRUE, fileEncoding="UTF-8-BOM")

こんなファイルです。

dplyr

ここには行ったことがある行政区しか載せていないため、最初のファイル(taiwan_cities)とは行の数が合いません。

さて、最初のテーブル(taiwan_cities)と今読み込んだテーブル(taiwan_exp)をLEFT JOINでくっつけてみましょう!
ExcelのVLOOKUPと同じようなことができます。

taiwan_cities_exp <- dplyr::left_join(taiwan_cities, taiwan_exp, by = "行政区")

dplyr

こんなふうになります!元のテーブルの右側に、列が加わりました。

left_joinの説明をすると、

dplyr::left_join(左側に置きたいテーブル, 右側に置きたいテーブル, by = "キー")

となります。

左側のテーブルを基準としているため、左側のテーブルはすべての行が残りますが、右側のテーブルは必ずしもすべてが残るわけではありません。(今回はたまたま残っていますが。)

また、左側にあって右側にない場合は、NAとなります。(NULLとなります。)

以上の要領でdplyrを駆使していけばマジでExcelいらずになります!
処理も速いし最高ですね。もうExcelには戻れませんよ・・・

【Python】フォルダ内のCSVファイルを一括で読み込み縦につなげる方法

Pythonディレクトリ内のCSVファイルを一気に読み込んで、かつそれらを連結させる方法です。

CSVでログが吐かれている場合など、この方法を使えば一発ですね。
Excelで消耗している方は是非トライしてみてください。流れはこんな感じです。

  1. 読み込みたいファイルがあるフォルダを指定
  2. glob.glob("*文字列*")で指定した文字列含むをファイルをリスト化する
  3. pd.read_csv()でCSVファイルを読み込む
  4. pd.concat()でDataFrameを縦に連結する
# import module
import pandas as pd
import glob
import os

# ディレクトリ変更
os.chdir('/Users/username/directory')

# 空のDataFrameを定義
df = pd.DataFrame()

# .csvを含むファイルをpd.read_csv()で読み込む
for i in glob.glob("*.csv*"):
    tmp_df = pd.read_csv(i)
  # DataFrameを連結する
    df = pd.concat([df, tmp_df])

繰り返し文(for文)を使えば指定した文字列を含むファイルをすべて読み込めます。

ただし、列名が揃っていないとうまく結合できないです。
その場合は、以下のようにして列名を変更してください。

# 列名の変更
df = df.rename(columns = {'col1': 'renamed_col1', 'col2': 'renamed_col2'})

【SQL】BigQueryで日付の列を生成する方法

指定した日付から本日までの日付の列を生成する方法です。
日付が歯抜けになっているテーブルとJOINしたいときや、日付のマスタを作る際に使えそうです。

▼Query

-- 指定した日付から本日までの日付の列を生成する
WITH DateRange AS(
  SELECT
    jpDate
  FROM 
    UNNEST(GENERATE_DATE_ARRAY(DATE('2015-04-01'), CURRENT_DATE('Asia/Tokyo'))) AS jpDate 
  ORDER BY jpDate)
SELECT * FROM DateRange

▼このようなアウトプットが得られます。

【Python】kaggleで使えるDataFrameのメモリを減らす関数

pandasで重たいデータを扱う際にマストな関数です!
初参加したkaggleのコンペ「ASHRAE - Great Energy Predictor III」の公開カーネルで使用されていたので、自分もkaggleや仕事で積極的に使っています。 特に、ASHRAEは扱うデータが大きかったので、重宝しましたね~

ガンガン使ってメモリ削減していきましょう!

# Function to reduce the DF size
import pandas as pd
import numpy as np

def reduce_mem_usage(df, verbose=True):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    start_mem = df.memory_usage().sum() / 1024**2    
    for col in df.columns:
        col_type = df[col].dtypes
        if col_type in numerics:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min &gt; np.iinfo(np.int8).min and c_max &lt; np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min &gt; np.iinfo(np.int16).min and c_max &lt; np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min &gt; np.iinfo(np.int32).min and c_max &lt; np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min &gt; np.iinfo(np.int64).min and c_max &lt; np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min &gt; np.finfo(np.float16).min and c_max &lt; np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min &gt; np.finfo(np.float32).min and c_max &lt; np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)    
    end_mem = df.memory_usage().sum() / 1024**2
    if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))
    return df

CSVを読み込む際に使うのが良いと思います。

# CSVを読み込む際に使うと良い
df = reduce_mem_usage(pd.read_csv('train.csv'))

出典:
https://www.kaggle.com/gemartin/load-data-reduce-memory-usage

【Python】SpotifyのAPIを使ってaikoの人気曲とアルバムを取得する

SpotifyのAPIを使ってaikoの人気曲とアルバムを取得する

SpotifyAPIを使うと、色々と楽しそうなことができるみたいなので試してみます。
この記事では、以下の2つを解説します。

  • アーティストの人気曲の取得
  • アーティストのアルバムの取得

とりあえず、自分の好きなaikoPUFFYMayday(台湾のアーティスト)の楽曲をSpotify APIで探してみたいと思います。

Spotify APIを使うための準備

まず、Spotifyのアカウントを持っていない場合は以下のリンクからアカウントを作りましょう。
https://www.spotify.com/jp/

次に、client_idとclient_secretが必要になるので、以下のリンクから取得してください。
https://developer.spotify.com/dashboard/

Pythonの開発環境がある方は、以下のコマンドでSpotifyAPIを使えるようにします。

pip install spotipy

spotifyではなくspotipyなんですね〜

簡単ですが、準備はこれでOKです。
リファレンスは次のリンクから。わからないことがあれば適宜参照してください。
https://spotipy.readthedocs.io/en/2.9.0/

Spotify APIでアーティストのTop Tracksを取得する

ここからが本題!好きなアーティストのTop Tracks(人気の曲)を取得してみます。
まず、先程取得したclient_idとclient_secretを入力して、認証をします。

# import modules
import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials

client_id = 'XXXXXXXXXXXXXXXX'
client_secret = 'XXXXXXXXXXXXXXXX'
client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials(client_id, client_secret)
spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

次にアーティストのIDを取得します。
APIを使ってアーティストを検索することもできるのですが、ここではSpotifyのWebページから取得します。

Spotifyのページからアーティストを検索し、アーティストページのURLの末尾の文字列をコピーします。
このページのURLですね。

SpotifyのAPIを使ってaikoの人気曲とアルバムを取得する https://open.spotify.com/artist/6TDMbiQCWeMClsMr9ORLRK

# アーティストのID
artist = 'spotify:artist:6TDMbiQCWeMClsMr9ORLRK' # aiko

このIDを使って、色々やってみます。
まずはTop Tracksを取得してみましょう。

spotify.artist_top_tracks(artist)

とすると、指定したアーティストの人気の10曲を取得できるようです。
曲の情報はかなり詳細に記されているのですが、そのうちの一部を取り出しましょう。

  • 曲名
  • リリース日
  • 曲の長さ
  • 人気度(popularity)
  • プレビューのURL
  • 収録されているアルバムの画像のURL
  • これらを取得してDataFrameにします。

    # Top Tracksを取得して、曲名、リリース日、人気度(popularity)などをDataFrameとして表示する
    results = spotify.artist_top_tracks(artist)
    
    name = []
    release_date = []
    duration_ms = []
    popularity = []
    preview_url = []
    images_url = []
    
    for track in results['tracks']:
        name.append(track['name'])
        release_date.append(track['album']['release_date'])
        duration_ms.append(track['duration_ms'])
        popularity.append(track['popularity'])
        preview_url.append(track['preview_url'])
        images_url.append(track['album']['images'][0]['url'])
        
    df_tracks = pd.DataFrame()
    df_tracks['name'] = name
    df_tracks['release_date'] = release_date
    df_tracks['duration_ms'] = duration_ms
    df_tracks['popularity'] = popularity
    df_tracks['preview_url'] = preview_url
    df_tracks['images_url'] = images_url
    

    すると、以下のようにDataFrameを作ることができます。
    カブトムシ、キラキラ、花火などaikoの代表的な曲が並んでいるのがわかります。
    popularityが一番大きいのは青空ですね。

    SpotifyのAPIを使ってaikoの人気曲とアルバムを取得する

    リリース日が年だったり年月日だったりすのが気になりますが・・・まあいいでしょう。

    アーティストのアルバム情報を取得する

    次に、アルバム情報を取得しましょう!
    アルバムに関しても、かなり詳細な情報が取得できるようですが、ここでは以下を取りだしてみます。

  • アルバム名
  • リリース日
  • 含まれている曲数
  • SpotifyのURL
  • ここで、アルバムを利用可能な地域"available_markets"を['JP']に指定しておきます。
    こうしておくことで、日本語名のアルバムのみを取り出せます。
    ※外国のアーティストなら指定しなくてもいいかも。詳しくは実際に触ってみてください。

    # アルバム情報を取得して、アルバム名、リリース日、URLなどをDataFrameとして表示する
    results = spotify.artist_albums(artist)
    
    name = []
    release_date = []
    total_tracks = []
    external_urls = []
    
    for item in results['items']:
        # 海外の楽曲なら不要
        if item['available_markets'] == ['JP']:
            name.append(item['name'])
            release_date.append(item['release_date'])
            total_tracks.append(item['total_tracks'])
            external_urls.append(item['external_urls']['spotify'])
            
    df_aibums = pd.DataFrame()
    df_aibums['name'] = name
    df_aibums['release_date'] = release_date
    df_aibums['total_tracks'] = total_tracks
    df_aibums['external_urls'] = external_urls
    

    こちらを実行すると、画像のようなDataFrameになると思います。
    全部で10曲のアルバムがありますね〜

    SpotifyのAPIを使ってaikoの人気曲とアルバムを取得する

    以上、SpotifyAPIで遊んでみました!
    もっと色んな使い方があるようなので、また更新します〜

    【Python】pandas.date_rangeで連続した日付の列を取得する

    任意の期間の日付の列を生成したいときに使います。 1月1日から丸一年分の日付が欲しいときや、ある1週間の日付が欲しいときに重宝します。

    # 連続した日付の列を取得
    pd.date_range('2020-01-01', periods=7, freq='D')
    

    periodsで期間の長さを指定、freqでどの単位(頻度)で取得するのかを指定します。 "D"なら日毎、"H"なら1時間ごと、"Q"なら四半期ごと、というように指定できます。

    【Python】リスト内包表記でDataFrameの列名を一括変更する

    DataFrameの列名を一括で変更する方法です。

    df.rename(columns = {'col_name1': 'col_name2'})
    

    とするよりも簡単で便利かもしれません。

    例えば、以下の画像のような、dayと数字の間にあるアンダースコア"_"を取り除きたい場合など。 DataFrame列名一括変更

    リスト内包表記を使えば一括で変更できます。

    # 列名を変更
    df.columns = [col.replace('_', '') for col in df.columns]
    

    【R言語】decomposeで時系列データの要素を分解してみる

    Rで時系列データを手軽に分析したいなってときにdecomposeをよく使うのですが、 その方法を簡単に説明します。

    decompose関数は、時系列データを

    ・tread ・seasonal ・random

    の3つに分解してくれます。 トレンドと季節性を分解してくれるので、例えば季節の影響なく売上が伸びているのか(減っているのか)を調べる時に使えそうだなと。

    ただ、このdecomposeが中でどういう処理をしているのかはよく知らないので、 ちゃんと勉強しないとですね・・・

    データはAirPassengersを用いました。

    ダウンロードはこちら: https://www.kaggle.com/rakannimer/air-passengers/data

    data <- read.csv('airpassengers.csv')
    ts.d <- ts(data$X.Passengers, frequency = 12)
    decomposed.ts.d <- decompose(ts.d)
    plot(decomposed.ts.d)
    

    1年単位で周期しているだろうと思いfrequency = 12と指定しました(月単位でデータが格納されているので)。

    たった4行で書けるので、手軽!

    ▼plotした結果 R decompose

    けれど、Random項がランダムじゃないっぽい波形をしているので、このモデルでは不十分そうなことがわかります。

    もっと詳しくやるなら、やはりこの本からですかね。 ARモデルが詳しく解説されています。