Island Babies

いろんなことを書きます

論文読み Unsupervised Sentence Compression

www.slideshare.net

要約をするモデルを学習するには大雑把にいうと Unsupervised と Supervised の手法が考えられるわけですが、この論文ではUnsupervised な手法を提案。 外部変数として次の文を使っているのですが、(実際のプレゼンでも質問が来ましたが)これがどれくらい良いものになっているのかというのはよくわかりません。 ドキュメントレベルでみてどういう文章になるべきかみたいなことを表現できればいいのではないかと思ったりしました。まる。

AWSのストレージでちょっとハマった

AWSのストレージ周りでちょっとハマったことがあったのでメモ程度に残しておく。

概要

AWSインスタンスで、ある程度大きなインスタンスを選ぶとインスタンスにストレージがついてくる。
大きな容量のストレージを使いたくなったので、このストレージを使えるようにしたかった。(デフォルトだとおそらく使えるようにはなっていないので)
手順としては、ここを見て有効化した。
しかし、インスタンスにデフォルトで紐づいているボリュームはインスタンスを stop すると内容が消えてしまう。
しかもファイルシステムも初期化されてしまう。
当然だが stop しても内容が消えないようになって欲しいと考えているので、それに対処をする必要があった。

結論

インスタンスにデフォルトで紐づいているボリュームと、メニューから自分で作ったボリュームは異なっていて、インスタンスに紐づくボリュームはstopするとボリュームの内容は消えてしまうようだった。
解決策としては、インスタンスとは別でボリュームを作成して、インスタンスに紐づければよかった。
というか、インスタンスを作るときに生成されるボリュームとそれとは別に作ったボリュームは、僕が見た範囲ではボリュームの情報に特に違いはなかったように思うので、その辺りがわかりやすくなったらいいなという感じがある。

Rust の プロファイラ

概要

やっぱりpython はどこまでいっても速さを追求できるものではなくて, Cython などいろいろ使ってみても慣れていないとなかなか難しいところがあります. 処理が早い言語を書く必要に迫られたので, Rust を勉強していろいろとプログラムを書いてみています. その中で使ったものを紹介します. 軽くになってはしまいますが...

cargo-profiler

keens.github.io まずこちらを試したみました. cargo install してすぐ使えるお手軽さが魅力かと思います. しかし、これはかなり細かい部分の処理が出てくるので、パッと使うには少し難しいような気がしています. 慣れればなんでもないのかもしれないですが.

perf

次が perf です. これはおそらくLinux のパッケージだと思います. qiita.com これもかなり低レイヤーの話をみている気がしていて, パッと使うには難しいかなと思います.

flame

結局, 使ったのは flame という crate でした. https://github.com/llogiq/flameがレポジトリになりますが, かなり使いやすいです. イメージ的には, 測定したい部分にflame::hogehoge って感じで挿入してあげるとその部分の時間を測定するって感じです. さらに, 出力されるhtmlが入れ子になっていて関数のどの部分が関数のうち何%を占めているかが視覚的にわかりやすくなっています.

マルチプロセスを走らせる時のプログレスバーを表示する

あらすじ

プログレスバー、使ってますか. 僕は最近プログレスバーの素晴らしさに気づきどこでもかしこでもプログレスバーを表示しています.
tqdm というライブラリを使っています. おそらく一番有名なのではないかと思います. 今取り組んでいるプログラムの中で、マルチプロセス化することで動作をより高速にできる部分があったのでその部分をマルチプロセス化していました.
具体的には, multiprocessing.pool というライブラリを用いてマルチプロセス化を行いました. このマルチプロセス化した処理のプログレスバーの表示をパパッとは実装できなかったので、色々調べました.

調べ始める前に思っていたこと

プログレスバーを進めるということは複数のプロセスで一つのプログレスバーを共有しないといけないので、難しそう(こっちのアプローチでも実装できそうな気はしています)

調べた結果

そもそも、 Pool.map()で実行したい関数を渡して処理を行っていました. 幸いにもループの中で変わる引数が一つだけだったので, partial で部分適用した関数を食わせて処理を回していました. いろいろ調べていると、こちらのページimap_unordered が紹介されていて、これが自分の使い方では一番よい使い道なのではないかと思いました. 順序を気にせずイテレータを返されるので、それをそのまま tqdm に食わせることを考えました. 果たしてその通りで, この処理で上手くいったのでした.
上記のページ以外にも、こちらのページ も参考にしました. (imap_unorderd を発見したのはこの記事の中です)

最後に

そもそも論として並行処理・並列処理についてしっかりと勉強したことがない気がするので、その辺りもしっかり調べていきたいと思っている所存です.

EDA Kernel を読んでみる

はじめに

Kaggle に少し手をつけてみようと思い EDA kernel (データを並べてみて解析するカーネル?) を眺めてみたのですが何をしているのかよくわからない部分が 多かったので、ブログでまとめてみようと思います。
とはいえそれなりに量が多いので全てのコードは扱いませんし、一度に書き切れる気もしないのでちょくちょく更新しようと考えています。

参考にしたカーネルこちらです

%%time
root = '../input/ashrae-energy-prediction/'
train_df = pd.read_csv(root + 'train.csv')
train_df["timestamp"] = pd.to_datetime(train_df["timestamp"], format='%Y-%m-%d %H:%M:%S')

weather_train_df = pd.read_csv(root + 'weather_train.csv')
test_df = pd.read_csv(root + 'test.csv')
weather_test_df = pd.read_csv(root + 'weather_test.csv')
building_meta_df = pd.read_csv(root + 'building_metadata.csv')
sample_submission = pd.read_csv(root + 'sample_submission.csv')

root で入力csv のルートを指定して、 pd.read_csvcsvを読み込んでいるようですね。 pd.to_datetime でパースしているようですが、これは

note.nkmk.me

このサイトによると、

pandas.to_datetime()関数を使うと、日時(日付・時間)を表した文字列の列pandas.Seriesをdatetime64[ns]型に変換できる。 と言うことだそうですね。 Panda.Series はある一列のことのよう。

あとは同じような感じで色々なcsvを読み込んでいると。

## Function to reduce the DF size
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 > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < npfor.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < 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

どうやらpandas の各データは静的型付けでいうところの intlong みたいな属性を持っているようで、それを必要十分な容量を持つ型に変換することでメモリの使用量を抑える関数のようです。 for col in df.columns で各行を見て、各行の型、最大値最小値に合わせて適切な型に変換してやる、ということらしい.

## REducing memory
train_df = reduce_mem_usage(train_df)
test_df = reduce_mem_usage(test_df)

weather_train_df = reduce_mem_usage(weather_train_df)
weather_test_df = reduce_mem_usage(weather_test_df)
building_meta_df = reduce_mem_usage(building_meta_df)

この部分のログを見ればわかるように、50%くらいはメモリの使用量が抑えられるらしい。かなりやるべき操作のような気がしますね。

train_df.head()

train_df の頭の方のデータをパッと表示するようです。とりあえず見てみるって感じなのかな

train_df.columns.values

df の 行一覧、さらにその値の一覧のarray を計算する感じ

for key, d in train_df.groupby('meter_reading'):
    break
    d.head()
plt.figure(figsize = (20,5))
d['meter'].plot()

正直この部分はよくわからない。多分最後に見た d の["meter"]をプロットする処理になっているはずだが、多分そういう処理ではないと思うし、おそらくd["meter"].plot()が中に入ってくるんじゃないかなと思う

plt.figure(figsize=(8,6))
plt.scatter(range(train_df.shape[0]), np.sort(train_df['meter_reading'].values))
plt.xlabel('index', fontsize=12)
plt.ylabel('meter_reading', fontsize=12)
plt.title("Target Distribution", fontsize=14)
plt.show()

figsize=(8,6)でグラフのサイズを指定して、scatter で散布図をプロットしているみたいですね。ソートしてからプロットしているのでそれは散布図である必要があるのかな?という気はします

plt.figure(figsize = (15,5))
train_df['meter_reading'].plot()

'meter_reading' の値をプロットしてるみたいですね。
plt.figure() でプロットの内容をリセットできるのかな? 横軸は指定しない風なので多分indexが自動的に横軸になるのかなあと思います

train_df['meter_reading'].plot(kind='hist',
                            bins=25,
                            figsize=(15, 5),
                           title='Distribution of Target Variable (meter_reading)')
plt.show()

plot()でkind="hist" と指定するとヒストグラムが作れるらしくて、特にbins=25とか指定すると横に何個分割するかを指定できるようです。例えば階級値が1~1000まであって、bins=25 と指定してやると、40このクラスに分けてカウントするみたいな感じだと思っています

# Load data
train = train_df.set_index(['timestamp'])

# Plot missing values per building/meter
f,a=plt.subplots(1,4,figsize=(20,30))
for meter in np.arange(4):
    df = train[train.meter==meter].copy().reset_index()
    df['timestamp'] = pd.to_timedelta(df.timestamp).dt.total_seconds() / 3600
    df['timestamp'] = df.timestamp.astype(int)
    df.timestamp -= df.timestamp.min()
    missmap = np.empty((1449, df.timestamp.max()+1))
    missmap.fill(np.nan)
    for l in df.values:
        if l[2]!=meter:continue
        missmap[int(l[1]), int(l[0])] = 0 if l[3]==0 else 1
    a[meter].set_title(f'meter {meter:d}')
    sns.heatmap(missmap, cmap='Paired', ax=a[meter], cbar=False)

pd.to_timedelta がよく話からなったのですが、色々な文字列での表現での時間を、一律でtimedelta というオブジェクトに変換して (いい感じに整形して) 返してくれる関数みたいです。それでパースしたものの.dt.total_sconds()がよくわからないですけど、多分秒で表したら何秒か?っていうのを取得しているんだと思います. 60*60で割って時間に直しているみたいなので(total_hours() みたいなのはないのかな?)

missmap = np.empty((1449, df.timestamp.max()+1))
    missmap.fill(np.nan)

この辺でnan で埋めて、

    for l in df.values:
        if l[2]!=meter:continue
        missmap[int(l[1]), int(l[0])] = 0 if l[3]==0 else 1

この辺でbuilding_id, time_stamp を見ながら値が入ってたら1、そうでなければ0 とかで埋めてるって感じ

total = train_df.isnull().sum().sort_values(ascending = False)
percent = (train_df.isnull().sum()/train_df.isnull().count()*100).sort_values(ascending = False)
missing__train_data  = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing__train_data.head(4)

欠損しているデータの割合がどんな感じなのかを調べる。 .count()でTrue, False 関係なく数えるっていう感じなのだと思います

# Find correlations with the target and sort
correlations = train_df.corr()['meter_reading'].sort_values()

# Display correlations
print('Most Positive Correlations:\n', correlations.tail(15))
print('\nMost Negative Correlations:\n', correlations.head(15))

train_df.corr()で各カラム間の相関係数を計算してくれるようです. そのうちの'meter_reading'の部分をソートして表示しています.

corrs = train_df.corr()


plt.figure(figsize = (20, 8))

# Heatmap of correlations
sns.heatmap(corrs, cmap = plt.cm.RdYlBu_r, vmin = -0.25, annot = True, vmax = 0.6)
plt.title('Correlation Heatmap');

相関係数を計算した後、ヒートマップを表示しています。各引数の意味は、

  • cmap -> 値に対する色のマッピングを指定.
  • vmin, vmax -> ヒートマップの最小値、最大値を指定(使い道がよくわからない)
  • annot -> ヒートマップ上に数値を表示する

だそうです. 色々あって覚えるのが大変そうだなぁー

def plot_dist_col(column):
    '''plot dist curves for train and test weather data for the given column name'''
    fig, ax = plt.subplots(figsize=(10, 10))
    sns.distplot(weather_train_df[column].dropna(), color='green', ax=ax).set_title(column, fontsize=16)
    sns.distplot(weather_test_df[column].dropna(), color='purple', ax=ax).set_title(column, fontsize=16)
    plt.xlabel(column, fontsize=15)
    plt.legend(['train', 'test'])
    plt.show()

かなり重要そうだなという感じがする関数. sns.distplot で分布をプロットしてくれるようです。タイトル付きでプロットした後、プロットにタイトルつけて表示っていう感じかな

ts=train_df.groupby(["timestamp"])["meter_reading"].sum()
ts.astype('float')
plt.figure(figsize=(16,8))
plt.title('meter_reading')
plt.xlabel('timestamp')
plt.ylabel('meter_reading')
plt.plot(ts);

groupby であるカラムを見て同じ値を持つデータに対してまとめて処理を行うっていう感じらしいので、同じタイムスタンプに対して(多分色々なmeter が設定されていて)そのmeter_reading の合計をとるっていう処理になっている?

plt.figure(figsize=(16,6))
plt.plot(ts.rolling(window=12,center=False).mean(),label='Rolling Mean');
plt.plot(ts.rolling(window=12,center=False).std(),label='Rolling sd');
plt.legend();

rolling で、指定した値の数分だけお連続したデータを見て、それに対してmeanやらstdやらで統計量を計算する、という処理らしい。 center=True にすることで(デフォルトはFalseのようだが...) 、窓の真ん中に計算結果の値を置くようになるらしい(デフォルトでは最後に置く). 細かい挙動はよくわかっていないが、なんとなく使い道は想像できる.

import statsmodels.api as sm
# multiplicative
res = sm.tsa.seasonal_decompose(ts.values,freq=12,model="multiplicative")
fig = res.plot()

# Additive model
res = sm.tsa.seasonal_decompose(ts.values,freq=12,model="additive")
fig = res.plot()

statsmodels は統計モデルをパパッと試せるライブラリらしい? そして、seasonal_decomposeは、ある種の時系列データを、周期的なデータ(一週間周期とか)、全体的な傾向を表すデータ(直線的に上がるとか、下がるとかのイメージ?)、そしてランダムなデータに分解してくれるらしい。なんじゃその便利すぎる関数は!(ってかそんなことできるんやって感じだけど...) Additive とか multiplicative とかっていうのは、データを分割するときにその三つの積で表されると思ってやるか、和で表せると思ってやるか、ということの違いのよう。というか、これで説明できるとしたらかなり便利だよなーと思ったり(複雑な変化だと多分自分の目で見てもわからないと思う)

y_mean_time.rolling(window=10).std().plot(figsize=(20, 8))
ax = plt.axhline(y=0.009, color='red')

ローリングしながらstdを計算。plt.axhlineであるyの値で直線を引けるみたいです

```` train_df['meter'] = pd.Categorical(train_df['meter']).rename_categories({0: 'electricity', 1: 'chilledwater', 2: 'steam', 3: 'hotwater'}) daily_train = train_df.copy() daily_train['date'] = daily_train['timestamp'].dt.date daily_train = daily_train.groupby(['date', 'building_id', 'meter']).sum() daily_train

rename_categories で、カテゴリー変数?を変換しているみたいです (0 -> electricity に、みたいな感じ)
dt.date で日付までで日時の情報を丸めて、それを元に groupbyで指定した三つの情報が同じカラムについて合計を取っているよう。なるほどなーという感じですよねー

daily_train_agg = daily_train.groupby(['date', 'meter']).agg(['sum', 'mean', 'idxmax', 'max']) daily_train_agg = daily_train_agg.reset_index() level_0 = daily_train_agg.columns.droplevel(0) level_1 = daily_train_agg.columns.droplevel(1) level_0 = ['' if x == '' else '-' + x for x in level_0] daily_train_agg.columns = level_1 + level_0 daily_train_agg.rename_axis(None, axis=1) daily_train_agg.head()

groupby で date, meter が同じものをまとめて、それに対して sum, mean, idxmax, max を適用.  idx は範囲の中で最大の値を持つような列の名前を返すような関数. 
・・・なんですがちょっとこの部分の処理については理解しきれてないのでおいおい勉強していこうと思っています

fig_total = px.line(daily_train_agg, x='date', y='meter_reading-sum', color='meter', render_mode='svg') fig_total.update_layout(title='Total kWh per energy aspect') fig_total.show()

px.line で折れ線グラフを表示。daily_train_agg を元データとして、'meter'をカラーとして指定するのでそれがキーとして、"meter_reading-sum"の値についてグラフを描画、ということか.

で、どうやらある一つの建物が明らかにおかしなデータを出していることがグラフを見ていると分かるらしい(ほんまか?)。のでそれがどれかを調べていく。

daily_train_agg['building_id_max'] = [x[1] for x in daily_train_agg['meter_reading-idxmax']] daily_train_agg.head()

まず、各日、各メーターについて一番高くなっているidを計算しておく.

def show_building(building, energy_aspects=None): ...

一応定義はされてるけど使われてなかったのでパス

daily_train_electricity = daily_train_agg[daily_train_agg['meter']=='electricity'].copy() daily_train_electricity['building_id_max'] = pd.Categorical(daily_train_electricity['building_id_max']) fig_daily_electricity = px.scatter(daily_train_electricity, x='date', y='meter_reading-max', color='building_id_max', render_mode='svg') fig_daily_electricity.update_layout(title='Maximum consumption values for the day and energy aspect') fig_daily_electricity.show()

` daily_train_agg[daily_train_agg['meter']=='electricity'] ` で ['meter'] == 'electricity' のカラムのidを取ってきて、それを元にコピー.
pd.Categorical でカラムのデータ型をカテゴリー型に変換してくれるらしい. こっちの方がカテゴリーとして扱うときに色々便利なよう.
その次にox.scatter で散布図を作る. x, yで軸にするカラムを指定して, color で色分けに使うカラムを指定. 
可視化された結果を見ると、明らかに値がおかしなことになってるbuilding がいくつかあることがわかりますね.

このエラーが起きている理由は色々と考えられるようですが、何れにせよなんらかの対処をしなければいけない、ということのよう.

ここからが特徴エンジニアリングと、モデリングの部分らしい.

train_df['timestamp'] = pd.to_datetime(train_df['timestamp'])
test_df['timestamp'] = pd.to_datetime(test_df['timestamp'])
weather_train_df['timestamp'] = pd.to_datetime(weather_train_df['timestamp'])
weather_test_df['timestamp'] = pd.to_datetime(weather_test_df['timestamp'])
    
building_meta_df['primary_use'] = building_meta_df['primary_use'].astype('category')

timestamp をto_datetime でパース.

temp_df = train_df[['building_id']]
temp_df = temp_df.merge(building_meta_df, on=['building_id'], how='left')
del temp_df['building_id']
train_df = pd.concat([train_df, temp_df], axis=1)

temp_df = test_df[['building_id']]
temp_df = temp_df.merge(building_meta_df, on=['building_id'], how='left')

del temp_df['building_id']
test_df = pd.concat([test_df, temp_df], axis=1)
del temp_df, building_meta_df

temp_df = train_df[['site_id','timestamp']]
temp_df = temp_df.merge(weather_train_df, on=['site_id','timestamp'], how='left')

del temp_df['site_id'], temp_df['timestamp']
train_df = pd.concat([train_df, temp_df], axis=1)

temp_df = test_df[['site_id','timestamp']]
temp_df = temp_df.merge(weather_test_df, on=['site_id','timestamp'], how='left')

del temp_df['site_id'], temp_df['timestamp']
test_df = pd.concat([test_df, temp_df], axis=1)

del temp_df, weather_train_df, weather_test_df

building_idやらsite_id, timestamp でDataFrame をマージし、共通する部分を削除してから繋げることで、meta_df を繋げたDFを計算しています

train_df.to_pickle('train_df.pkl')
test_df.to_pickle('test_df.pkl')
   
del train_df, test_df
gc.collect()

pickle によってデータを一時ファイルとして保存することができている

train_df['age'] = train_df['year_built'].max() - train_df['year_built'] + 1
test_df['age'] = test_df['year_built'].max() - test_df['year_built'] + 1

建てられてから何年経っているか?をカラムとして追加する

le = LabelEncoder()
# train_df['primary_use'] = train_df['primary_use'].astype(str)
train_df['primary_use'] = le.fit_transform(train_df['primary_use']).astype(np.int8)

# test_df['primary_use'] = test_df['primary_use'].astype(str)
test_df['primary_use'] = le.fit_transform(test_df['primary_use']).astype(np.int8)

primary_use のカラムをラベル変数に変換する。 各ラベルに順序がある場合はその順序を考慮したラベル付け(大きい順とか) にした方が良いらしい.

ここからは、欠損値の扱いについて~~ 欠損値がなぜ欠損しているかを考えることはとても重要であるらしい.  その埋め方はカーネルに色々書いてありました. 今回のカーネルでは、たくさん値がなくなっているような4つの特徴について、-999で埋めるという手法を取っています

train_df['floor_count'] = train_df['floor_count'].fillna(-999).astype(np.int16)
test_df['floor_count'] = test_df['floor_count'].fillna(-999).astype(np.int16)

train_df['year_built'] = train_df['year_built'].fillna(-999).astype(np.int16)
test_df['year_built'] = test_df['year_built'].fillna(-999).astype(np.int16)

train_df['age'] = train_df['age'].fillna(-999).astype(np.int16)
test_df['age'] = test_df['age'].fillna(-999).astype(np.int16)

train_df['cloud_coverage'] = train_df['cloud_coverage'].fillna(-999).astype(np.int16)
test_df['cloud_coverage'] = test_df['cloud_coverage'].fillna(-999).astype(np.int16) 

その次に、たくさんtimestamp に関わる特徴量を計算しています.

drop_cols = ["precip_depth_1_hr", "sea_level_pressure", "wind_direction", "wind_speed","timestamp"]
target = np.log1p(train_df["meter_reading"])  
del train["meter_reading"]
train_df = train_df.drop(drop_cols, axis=1)

ログスケールに直したり、カラムを削除したりしているようです.

あとはlgbを使って学習を回して、っていう感じですね!

感想

とりあえず一通り読んではみましたが、まだまだ自分でかけるという感じではない気がするのでもっと精進していかないといけないですね!

os.path.exists で存在するはずのパスがFalseを返す

概要

タイトル通りですが、最近画像のデータセットについて、コピーするのではなくてシンボリックリンクを貼れば良いのでは?ということで元のデータセットからいい感じにデータをまとめるスクリプトを書いていたのですが、そこで起きたエラーが少しややこしかったという感じです.
具体的には、dataset_hogehoge/images,xmls という感じのディレクトリから対応する画像をシンボリックリンクで、xmlをコピーでそれぞれ新しいディレクトリに持ってくるということをしている時、一応コピーしてきた xml 中に書かれているファイル名の画像がちゃんと入っているかな?ということを確認するスクリプトを書いてみたところ・・・

なぜ

ほぼ全てのxml ファイルに対応する画像ファイルについて、os.path.exists が false を返すことがわかりました。なるほどなるほど、ファイル名を間違えていたのか、うっかりさんめ・・・と思ったのですが一応確認しておこうと思って ls hogehoge.png とかやってみると、ファイル自体は存在していることがわかりました.
・・・?
この時点で?が頭の上に3個くらい浮かびまして、色々調べていると次のようなページが見つかりました.

stackoverflow.com

なるほどー、空白記号とかが入っているといかんのだな、ということで.strip() して再度試してみましたが、うまくいかず・・・
その後も色々探してみると、ついに発見しました。

http://python.6.x6.nabble.com/os-path-exists-path-returns-false-when-the-path-actually-exists-td1701865.html

Return True if path refers to an existing path. Returns False for broken symbolic links. On some platforms, this function may return False if permission is not granted to execute os.stat() on the requested file, even if the path physically exists.

So the better question is, does is this file a broken symbolic link or can os.stat() be executed on it?

どうやらシンボリックリンクが綺麗に貼れていない場合、見かけ上はファイルがあるけど実体がない?ためにエラーが返されるようです。 シンボリックリンクを貼るコードをよくよく読んでいると、画像名の拡張子をつけるところで間違えていたことを発見しました! これで一件落着。 しかしファイルが存在しなくてもシンボリックリンクを貼る時点では特にエラーが出ないとは・・・なかなか難しいですな

初めまして itertools。

概要

二つのイテレータから全部の組み合わせ方を返してくれるような組み込み関数ってあったりしないのかなーとか調べてみたら itertools.product っていうデカルト積を返してくれる関数を発見しました. それ以外にもイテレータを扱う上で便利そうな関数がいくつかあったので少しまとめてみます。基本的には公式を参考にしています.

accumulate

累積和であったり、ある二項演算を初めから順に適用させた累積の要素を返すようなイテレータを返します。

# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
# accumulate([1,2,3,4,5], initial=100) --> 100 101 103 106 110 115
# accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120

これだけでめちゃ便利そうな感じがしますね。initialで指定すれば初期値を設定できて, 第二引数として二項演算を指定することができます!
累積和は特に使うことがよくあると思うので便利そう。

chain

複数のイテレータを受け取って、最初のイテレータを要素を順番に返し、その次に2番目のイテレータの要素を・・・というように順番に要素を返してくれる、イテレータを繋げるような関数です.

# chain('ABC', 'DEF') --> A B C D E F

使い道はありそうだけど、あんまり使うことはないのかなーって感じはしますね笑

combinations

イテレータの各要素の、引数で与えた長さの組み合わせの全パターンを返します。

# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123

オシャレだけどあんま使うことはなさそう.

dropwhile

述語として渡した関数の値が真の間の要素を頭から全て捨てていって、偽になった要素から後のものを全て返す関数です。

# dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1

これはばちばち使いやすそうな関数ですね

filterfalse

関数型言語でよくあるfilterとほぼ同じですが、疑になるような要素を返します。