Pythonバイオ? 
1846   2020-07-08 (水) 13:56:44

pandasティップス

行選択と列選択

行位置選択

行の値選択、列の値選択

セル選択

行演算と列演算

行間での足し算

import pandas as pd
df = pd.DataFrame([['A', 1, 3], ['B', 2, 4], ['C', 3, 5], ['D', 4, 6]], \
                  columns=['a', 'b', 'c'])
print(df)
#   a  b  c
#0  A  1  3
#1  B  2  4
#2  C  3  5
#3  D  4  6

print(df[df.index.isin([2, 3])].sum())  # リストにある行を足す
#a    CD
#b     7
#c    11
#dtype: object

print(df.loc[0:1].sum())  # 連続した行を足す
#a    AB
#b     3
#c     7
#dtype: object

print(df.loc[[0,2]].sum()) # (リストにある)とびとびの行でもOK
#a    AC
#b     4
#c     8
#dtype: object

おまけ、平均

print(df[df.index.isin([2, 3])].mean())
#b    3.5
#c    5.5
#dtype: float64

print(df.loc[0:1].mean())
#b    1.5
#c    3.5
#dtype: float64

pandas DataFrameで数値の桁数を丸めるには

2列間での演算

Pandasで複数の列を値をもとに、新しい列を任意の関数で定義する方法 | Shikoan's ML Blog

import pandas as pd
comike = pd.DataFrame({
    "block" : ["AX", "AY", "あZ", "あX", "イQ", "イR"],
    "number" : [1, 1, 10, 11, 12, 13],
    "side" : ["aX", "bX", "aY", "bZ", "aQ", "bR"]
})
print(comike)
#comike["space"] = comike.apply(lambda x: f"{x['block']}-{x['number']:02d}{x['side']}", axis=1)
comike = comike[comike.apply(lambda x: x['block'][1]==x['side'][1], axis=1) ]
print(comike)

値・行名/列名でソートする

pandas.DataFrame, Seriesをソートするsort_values, sort_index | note.nkmk.me

値でソートする

df.sort_values()
df.sort_values(ascending=False)

行名でソートする

df.sort_index()

列名でソートする

df.sort_index(axis=1)

ifのある内包

even_list = [i for i in range(10) if i % 2 == 0]
even_list = [i if i % 2 == 0 else "odd" for i in range(10)]

三項演算子(条件式)

Pythonの三項演算子(条件演算子)でif文を一行で書く | note.nkmk.me
6. 式 (expression) — Python 3.8.0 ドキュメント 6.12. 条件式 (Conditional Expressions)

result = a * 2 if a % 2 == 0 else a * 3

elifを含めたい時(推奨せず)

result = 'negative' if a < 0 else 'positive' if a > 0 else 'zero'

要素データの置き換え replace

pandas.DataFrame, Seriesの要素の値を置換するreplace | note.nkmk.me

df = df.replace(oldvalue, newvalue)

正規表現を使って置き換えを指定することができる。

print(df.replace('(.*)li(.*)', r'\1LI\2', regex=True))

のように、regex=Trueで指定する。(デフォルトはFalse)
正規表現の指定の仕方は、たとえばPythonの正規表現モジュールreの使い方(match、search、subなど) | note.nkmk.meを参照。

内包の2重ループ

Python のネストした内包表記 - Qiita

[(x, y) for x in [1, 2] for y in ['a', 'b', 'c']]

yが内側ループ、xが外側ループになることに注意。

もう1つの(条件付きの)要素データの置き換え where

条件付きの要素書換えは、whereで書くことができる。

pandasで条件に応じて値を代入(where, mask) | note.nkmk.me

whereを使って、第3引数に新しい値を指定することによって、書き換えることができる。

1列'A'を書き換える例

df['D'] = df['A'].where(df['C'] == 'a', 100)     # 定数を書込む
df['D'] = df['A'].where(df['C'] == 'a', df['B']) # df['B']から値を持ってくる

但し、「書換え」操作として見ると条件指定は逆(Trueだと書き換えない)になっているので注意。その理由は、whereが(第3引数を指定しないと)条件がTrueの要素だけを元の値を抜き出し、Falseの要素はNaNを返すような元々の機能だから。つまりTrueはそのままで、Falseだと何かするが、その「何か」が第3引数があればその値、第3引数が指定されていなければNaNとなる。

データフレーム全体で書き換える例

df2 = df.where(df < 0, 100)
df2 = df.where(df < 0, df * 2)

列名の付け替え rename

pandas.DataFrameの行名・列名の変更 | note.nkmk.me

df = df.rename(columns={旧名: 新名, 旧名2: 新名2, ...})

なお、indexに対する名前を付けるには

df.index.name = colname1

同様に、

df.columns.name = colname2

つまり

import pandas as pd
df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['A','B','C'])
print(df)
df.index.name='インデックス'
print(df)
df.columns.name = 'カラム'
print(df)

とすると、何もつけないときは

   A  B  C
0  1  2  3
1  4  5  6
2  7  8  9

indexに名前'インデックス'を付けると

        A  B  C
インデックス         
0       1  2  3
1       4  5  6
2       7  8  9

更に、columnに名前'カラム'を付けると

カラム     A  B  C
インデックス         
0       1  2  3
1       4  5  6
2       7  8  9

となる。

インデックスの付け替え drop_indexとset_index

[[pandas.DataFrame, Seriesのインデックスを振り直すreset_index | note.nkmk.me https://note.nkmk.me/python-pandas-reset-index/]]

df = df.drop_index()  # インデックスは新たに列として加えられる。もし列名が重複するとエラー
df = df.drop_index(drop=True)  # インデックスを完全に削除(列として残さない)
df = df.set_index('列名')  # 列名の列をインデックスとして使う。その列は消える
df = df.reset_index.set_index('列名') # 元のインデックスを列として残しつつ変更

列位置の入れ替え

df = df.loc[:, ["col3", "col1", "col2", "col0"]]

行・列のdrop

pandas.DataFrameの行・列を指定して削除するdrop | note.nkmk.me

df = df.drop('行名', axis=0)   # axis=0はデフォルト
df = df.drop(index='行名')
df = df.drop('列名', axis=1)
df = df.drop(column='行名')

複数行・列の場合

df = df.drop(index=['行名1', '行名2']

行番号で指定

df = df.drop(index=df.index[1, 3, 5]))

なお、値を条件にして行を削除/残すのは、

df = df[df['参照']>=3]
df = df[(df['参照1']>=3) and (df['参照2']!='')]

でできる。

列名が重複してしまった時の列の削除

import pandas as pd
df = pd.DataFrame([['A',1, 0.1], ['B', 2, 0.2], ['C', 3, 0.3], ['D', 4, 0.4]], 
columns=['ラベル', '値', '値'])
print(df)
#  ラベル  値    値
#0   A  1  0.1
#1   B  2  0.2
#2   C  3  0.3
#3   D  4  0.4

dfx = df[['ラベル', '値']]    # 値は2つ残る
print(dfx)
#  ラベル  値    値
#0   A  1  0.1
#1   B  2  0.2
#2   C  3  0.3
#3   D  4  0.4

dfx = df.drop('値', axis=1)   # 2つともdropする
print(dfx)
#  ラベル
#0   A
#1   B
#2   C
#3   D

dfx = df.drop(df.columns[[2]], axis=1)  # 位置2を指定しても2つともdropする
print(dfx)
#  ラベル
#0   A
#1   B
#2   C
#3   D

dfx = df.copy()   # 浅いコピーだと列名書換えはdfの方にも及ぶので注意
dfx.columns = ['ラベル', '値', '値2']   # すべて指定すれば列名はすべて書き変えられる
dfx = dfx[['ラベル', '値']]   # 値2をdropすればいい
print(dfx)
#  ラベル  値
#0   A  1
#1   B  2
#2   C  3
#3   D  4

dfx = df.rename(columns={'値': '値3'})  # 両方とも書き換えられる
print(dfx)
#  ラベル  値3   値3   <-- 両方とも'値3'になるのでダメ
#0   A   1  0.1
#1   B   2  0.2
#2   C   3  0.3
#3   D   4  0.4

結論として、列名を全面的に書換えするしかなさそうだ。

MultiIndexまわり

MultiIndexのDF/Seriesから要素を指定するには ⇒ xs()メソッドが見やすい

pandasのMultiIndexから任意の行・列を選択、抽出 | note.nkmk.me

print(df.xs(インデックス名, level='レベル名))

たとえば

                         val_1  val_2
level_1 level_2 level_3              
A0      B0      C0          98     90
                C1          44      9
        B1      C2          39     17
                C3          75     71
A1      B2      C0           1     89
                C1          54     60
        B3      C2          47      6
                C3          16      5
A2      B0      C0          75     22
                C1          19      4
        B1      C2          25     52
                C3          57     40

に対して、

print(df.xs('B1', level='level_2'))  # level_2が'B1'である行を選択

とすると

#                  val_1  val_2
# level_1 level_3              
# A0      C2          39     17
#         C3          75     71
# A2      C2          25     52
#         C3          57     40

pandasのMultiIndexから任意の行・列を選択、抽出 | note.nkmk.me

#multindex
import pandas as pd

# Create DF
columns = ['level_1','level_2','level_3','val_1','val_2']
l = \
[['A0',      'B0',      'C0',     98,     90],
 ['A0',      'B0',      'C1',     44,      9],
 ['A0',      'B1',      'C2',     39,     17],
 ['A0',      'B1',      'C3',     75,     71],
 ['A1',      'B2',      'C0',      1,     89],
 ['A1',      'B2',      'C1',     54,     60],
 ['A1',      'B3',      'C2',     47,      6],
 ['A1',      'B3',      'C3',     16,      5],
 ['A2',      'B0',      'C0',     75,     22],
 ['A2',      'B0',      'C1',     19,      4],
 ['A2',      'B1',      'C2',     25,     52],
 ['A2',      'B1',      'C3',     57,     40],
 ['A3',      'B2',      'C0',     64,     54],
 ['A3',      'B2',      'C1',     27,     96],
 ['A3',      'B3',      'C2',    100,     77],
 ['A3',      'B3',      'C3',     22,     50]]
df = pd.DataFrame(l, columns=columns)
print(df)
df_m = df.set_index(['level_1', 'level_2', 'level_3'])  # muyltiIndex化
print()
print(df_m)
print()
print(df_m.xs('B1', level='level_2'))  # xsによるアクセス
print()

df_T = df_m.T    # 転置でmultiColumn化
print(df_T)
print()
print(df_T.xs('B1', level='level_2', axis=1))  # xsはaxis=1でmultiColumnに対応

結果は

   level_1 level_2 level_3  val_1  val_2
0       A0      B0      C0     98     90
1       A0      B0      C1     44      9
2       A0      B1      C2     39     17
3       A0      B1      C3     75     71
4       A1      B2      C0      1     89
5       A1      B2      C1     54     60
6       A1      B3      C2     47      6
7       A1      B3      C3     16      5
8       A2      B0      C0     75     22
9       A2      B0      C1     19      4
10      A2      B1      C2     25     52
11      A2      B1      C3     57     40
12      A3      B2      C0     64     54
13      A3      B2      C1     27     96
14      A3      B3      C2    100     77
15      A3      B3      C3     22     50

                         val_1  val_2
level_1 level_2 level_3              
A0      B0      C0          98     90
                C1          44      9
        B1      C2          39     17
                C3          75     71
A1      B2      C0           1     89
                C1          54     60
        B3      C2          47      6
                C3          16      5
A2      B0      C0          75     22
                C1          19      4
        B1      C2          25     52
                C3          57     40
A3      B2      C0          64     54
                C1          27     96
        B3      C2         100     77
                C3          22     50


                  val_1  val_2
level_1 level_3              
A0      C2          39     17
        C3          75     71
A2      C2          25     52
        C3          57     40

level_1  A0              A1              A2              A3             
level_2  B0      B1      B2      B3      B0      B1      B2       B3    
level_3  C0  C1  C2  C3  C0  C1  C2  C3  C0  C1  C2  C3  C0  C1   C2  C3
val_1    98  44  39  75   1  54  47  16  75  19  25  57  64  27  100  22
val_2    90   9  17  71  89  60   6   5  22   4  52  40  54  96   77  50

level_1  A0      A2    
level_3  C2  C3  C2  C3
val_1    39  75  25  57
val_2    17  71  52  40

SeriesからDataFrameへ変換

s = pd.Series(['A','B','C'])
df = pd.DataFrame([s])
print(df)
#    0  1  2
# 0  A  B  C

そこで転置すると

df = pd.DataFrame([s]).T
print(df)
#    0
# 0  A
# 1  B
# 2  C

もう少し例を:

#multindex
import pandas as pd
# Create DF
columns  = ['level_1','level_2','level_3','val_1','val_2', 'val_3']

l = \
[[ 'X0',      'Y0',      'Z0',      'U',      'U',    'V'],
 [ 'X1',      'Y1',      'Z1',      'P',      'Q',    'P'],
 [ 'A0',      'B0',      'C0',     98,     90,    5],
 [ 'A0',      'B0',      'C1',     44,      9,    4],
 [ 'A0',      'B1',      'C2',     39,     17,    3],
 [ 'A0',      'B1',      'C3',     75,     71,    2],
 [ 'A1',      'B2',      'C0',      1,     89,    1],
 [ 'A1',      'B2',      'C1',     54,     60,    9],
 [ 'A1',      'B3',      'C2',     47,      6,    8],
 [ 'A1',      'B3',      'C3',     16,      5,    7],
 [ 'A2',      'B0',      'C0',     75,     22,    6],
 [ 'A2',      'B0',      'C1',     19,      4,    51],
 [ 'A2',      'B1',      'C2',     25,     52,    52],
 [ 'A2',      'B1',      'C3',     57,     40,    53],
 [ 'A3',      'B2',      'C0',     64,     54,    54],
 [ 'A3',      'B2',      'C1',     27,     96,    55],
 [ 'A3',      'B3',      'C2',    100,     77,    56],
 [ 'A3',      'B3',      'C3',     22,     50,    57]]
df = pd.DataFrame(l, columns=columns)
print(df)
df_m = df.set_index(['level_1', 'level_2', 'level_3'])  # multiIndex化
print()
print(df_m)
print()
print(df_m.xs('B1', level='level_2'))  # xsによるアクセス
print()
df_T = df_m.T    # 転置でmultiColumn化
print('df_T\n', df_T)
print()
#print(df_T.xs('B1', level='level_2', axis=1))  # xsはaxis=1でmultiColumnに対応

df_X = df_T.set_index([('X0', 'Y0', 'Z0'), ('X1', 'Y1', 'Z1')])
print(df_X.index)
index = pd.MultiIndex.from_tuples(df_X.index, names=['First', 'Second'])
df_X.index = index
print('df_X\n', df_X)
print()
df_Y = df_X.xs('B1', level='level_2', axis=1)  # xsによるアクセス
print('df_Y\n', df_Y)
print()
print(df_Y.index)
df_R = df_Y.reset_index(level=('X0', 'Y0', 'Z0'))
print('df_R\n', df_R)

この辺がよさそうだ。pd.MultiIndex.from_tuple()を使う

import pandas as pd
index = pd.MultiIndex.from_tuples([('bird', 'falcon'),
                                   ('bird', 'parrot'),
                                   ('mammal', 'lion'),
                                   ('mammal', 'monkey')],
                                   names=['class', 'name'])
columns = pd.MultiIndex.from_tuples([('speed', 'max'),
                                     ('species', 'type')])
df = pd.DataFrame([(389.0, 'fly'),
                   ( 24.0, 'fly'),
                   ( 80.5, 'run'),
                   (np.nan, 'jump')],
                  index=index,
                  columns=columns)
print(df)

#print(df.reset_index())
#print(df.reset_index(level='class'))

# column名変更?
columns = pd.MultiIndex.from_tuples([('P', 'Q'),
                                     ('R', 'S')])
df_R = df.copy()
df_R.columns = columns
print('df_R\n', df_R)
df_RR = df.copy()
df_RR[('P', 'Q')] = df[('speed', 'max')]
df_RR = df_RR.drop(('speed', 'max'), axis=1)
print('df_RR\n', df_RR)

print()
print('reset\n', df_RR.reset_index(level='name'))

辞書の反転

Pythonで辞書のキーと値を入れ替える | note.nkmk.me

d_swap = {v: k for k, v in d.items()}

DataFrameから辞書

pandas.DataFrame, Seriesを辞書に変換(to_dict) | note.nkmk.me

DataFrameの2つの列の間の関係を辞書にするためには、

pair = df[['キー側', 'バリュー側']]
ser = pair['バリュー側']
ser.index = pair['キー側']
dic = ser.to_dict()

Series/DataFrameからリストに変換するなら .values.tolist()

pandas.DataFrame, SeriesとPython標準のリストを相互に変換 | note.nkmk.me

Seriesの値部分だけなら

s = pd.Series([0, 1, 2])
l_1d = s.values.tolist()

DataFrameの値部分だけなら

df = pd.DataFrame([[0, 1, 2], [3, 4, 5]])
l_2d = df.values.tolist()
print(l_2d)
# [[0, 1, 2], [3, 4, 5]]

行ラベル(行名)ともリストに変換するには、結局reset_indexとかでインデックスを列に含める

l_1d_index = s_index.reset_index().values.tolist()
print(l_1d_index)
# [['row1', 0], ['row2', 1], ['row3', 2]]

列ラベル(列名)はreset_indexに相当する機能が無いので、1つの方法として転置してからreset_indexをかける。

l_2d_index_columns = df_index.reset_index().T.reset_index().T.values.tolist()
print(l_2d_index_columns)
# [['index', 'col1', 'col2', 'col3'], ['row1', 0, 1, 2], ['row2', 3, 4, 5]]

ラベルだけなら、df.index、df.columnsだが、リストにするためにtolist()しておく

df.index.tolist()
df.columns.tolist()

重複した行をdropする

duplicatedとdrop_duplicates

df = df.duplicated(keep='last', subset=['この列での重複']) # 重複ならTrue、ないならFalseの1列
df = df[~df.duplicated(keep='...', subset=['..', '..'])]  # 重複以外をひろう(=drop_duplicates)

df = df.drop_duplicates(keep='last')  # 重複した列を最後の1つだけ残して削除

文字列を含む行の抽出

pandasで特定の文字列を含む行を抽出(完全一致、部分一致) | note.nkmk.me

完全一致は == でよい。

dfx = df[df['state'] == 'CA']

部分一致は、in はダメらしい。str.contains() を使う。

dfx = df[df['name'].str.contains('li')

但し、デフォルトでは第1引数を正規表現と解釈する(要するにre.searchと同様)ので、 ピリオド等を含む場合には正しくない。回避するためには regex=False を指定するとよい。

dfx = df[df['name'].str.contains('li', regex=False)

また、要素が欠損値NaNである場合、デフォルトではNaNを返すので、行を抽出するとエラーになる。オプションとして na=True/False を指定する。

dfx = df[df['name'].str.contains('li', na=True)]   # NaNは抽出結果に含まれる
dfx = df[df['name'].str.contains('li', na=True)]   # NaNは抽出結果に含まない

なお、str.containsの他、 str.startswith や str.endswidth 更に str.match(〜re.matchと同様の動作) が使える。

要素が指定リストに含まれる行の抽出 (文字列ではなくてリスト)

[[pandas.DataFrameの行を条件で抽出するquery | note.nkmk.me: https://note.nkmk.me/python-pandas-query/]]

基本的に、isinメソッドを使うか、queryメソッドの中で in を使うか。

dfx = df[df['state'].isin(['NY', 'TX'])]

dfx = df.query('state in ["NY", "TX"]')

これも可:

dfx = df.query('state == ["NY", "TX"]'

Groupby

pandas.DataFrameをGroupByでグルーピングし統計量を算出 | note.nkmk.me

grouped = df.groupby('グループ化したい列名')

この結果に対して統計演算ができる

df = grouped.sum()

使えるのはsum(), mean(), min(), max(), std(), var()など

また、agg()を使って、関数を適用することができる。関数はSeriesを受け取って objectを返す関数でなければならない。

df = grouped.agg(lambda x: type(x))['sl'])
df = grouped.agg(min)

列ごとに異なる関数を適用することも可能。

df = grouped.agg({'列1': min}, {'列2': max})

複数の列をキーにしてグループ化できる。

gf = df.groupby(['列1', '列2']).mean()

as_index=Falseを指定すると(True時はキーが結果DFのインデックスになるが)、キーは結果DFの行のままで残りインデックスは元のまま

df = pd.DataFrame({
    'city': ['osaka', 'osaka', 'osaka', 'osaka', 'tokyo', 'tokyo', 'tokyo'],
    'food': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})

gb = df.groupby(['city', 'food']).mean()  # as_index指定なし(デフォルトはTrue)
print(gb)

に対して

               price  quantity
city  food                   
osaka apple   100.0       1.0
      banana  275.0       3.5
      orange  200.0       2.0
tokyo apple   175.0       5.5
      banana  400.0       7.0

だが、as_index=Falseにすると

gb_noindex = df.groupby(['city', 'food'], as_index=False).mean()
print(gb_noindex)

に対して

     city    food  price  quantity

0  osaka   apple  100.0       1.0
1  osaka  banana  275.0       3.5
2  osaka  orange  200.0       2.0
3  tokyo   apple  175.0       5.5
4  tokyo  banana  400.0       7.0

のようにcityとfoodの列が残る。

単に個数をカウントしたい場合、下記のsize()でもよいし、関数count()を使ってよい。
Pandasでデータの個数を数え上げるcount関数の使い方 - DeepAge

dfcount = df.groupby('city').count()
print(dfcount)
#       food  price  quantity
#city                        
#osaka     4      4         4
#tokyo     3      3         3

dfcount = df.groupby('city')[['food']].count().rename(columns={'food': 'count'})
print(dfcount)
#       count
#city        
#osaka      4
#tokyo      3

Groupbyのグループを取出す

Pandas の groupby の使い方 - Qiita

df.groupby('city').groups

{'osaka': Int64Index([0, 1, 2, 3], dtype='int64'),
 'tokyo': Int64Index([4, 5, 6], dtype='int64')}

df.groupby('city').get_group('osaka')

city	food	price	quantity
0	osaka	apple	100	1
1	osaka	orange	200	2
2	osaka	banana	250	3
3	osaka	banana	300	4

df.groupby('city').size()

city
osaka    4
tokyo    3
dtype: int64

df.groupby('city').size()['osaka']

4

map, apply

pandasで要素、行、列に関数を適用するmap, applymap, apply | note.nkmk.me

複数dfをjoin

reference

DFのリスト joinlist = [df1, df2, ..., dfn] を用意し、dfにjoinする。

dfout = df.join(joinlist)

このとき、キー指定("on"の指定)はできない。インデックスをキーにしてjoinする。

つまり、あらかじめインデックスを付け替えておく必要がある。

df1 = df1.set_index('column to use as key')

NaN扱い

NaNはnumpyのnan

DFでなければ

if np.isnan(np.nan):
    hogehoge

DFでのnan判定

Reference→API reference のgeneral functionにある

df = pd.DataFrame([[1,3],[2,np.nan]])
print(df.isnull())
print(df.notnull())

なお、isnull()はisna()のalias、notnull()はnotna()のalias

初めに空のDataFrameを作って後からデータ行を足す

df = pd.DataFrame( columns=['A', 'B'] )
for u in [0, 1, 2, 3]:
    temp_series = pd.Series( [i, i*i], index=df.columns )
    df = df.append( temp_series, ignore_index=True )

あとから列を足すのは、縦のデータの長さが同じなら簡単で、上記の場合縦が4なので

df['NEW'] = [3, 4, 5, 6]

長さが異なる場合、上記の方法はエラーになる。Seriesを作って同じように書きこめばOK.

## Test 〜 convert list (with different length) to DF column
import pandas as pd
import numpy as np

list1 = [1, 2, 3]
list2 = [4, 5, 6, 7, 8]
list3 = [10, 11]

# リストの長さがすべて同じならば、単純にdf['新列名']=listでOK
# 長さが異なるとエラー
df = pd.DataFrame(index=[0, 1, 2, 3, 4], columns=[])
#df['A'] = list1  <-- 長さが違うのでエラー

s = pd.Series(list1, index=[0,1,2])
df['A'] = s
print(df)  # これだと、不足した要素部分はNaN
#     A
#0  1.0
#1  2.0
#2  3.0
#3  NaN
#4  NaN

df.to_excel('mytest.xlsx')  # NaNの場所はExcel上では空欄になる

df = df.replace(np.nan, '')  # nanを空文字列に置換え
print(df)
#   A
#0  1
#1  2
#2  3
#3   
#4   

df.to_excel('mytest2.xlsx')  # 空文字列の場所はExcel上では空欄になる

collections.Counterの結果を加える

[Python] リストの要素の数を数える (collections.Counter) | Hibiki Programming Notes

c1 = collections.Counter('abbcabbbccca')
c1.update({'a':1, 'b':1, 'c':1})   # c1を書き換えることに注意('+'と異なる)

または

c1 = collections.Counter('abbcabbbccca')
c1.update('abc')

もOK

引く場合は、subtract

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)   # cを書き換えることに注意('-'と異なる)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

0やマイナスになり得る

また、8.3. collections --- コンテナデータ型 — Python 3.6.10rc1 ドキュメントでは

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d                       # add two counters together:  c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d                       # intersection:  min(c[x], d[x]) 
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})

引き算は0や負になると要素が消える(Countだから?)ので注意〜subtractと振る舞いが異なる。

基本的に、カウントが0の項目は削る方向に動く。従って、Counter同士を足したり引いたり した結果、値が0になればprintしても見えなくなる。

Counterはほぼ辞書と同じなので、次項のソートが使える。

辞書のソート

Pythonの辞書(dict)をソートする方法まとめ | HEADBOOST

d = {'b': 2, 'a': 3}
print(sorted(d)) # keyを取出してソート
print(sorted(d.keys())) # 上と同じこと
print(sorted(d.values()))
print(sorted(d.items())) # itemを取出してkey順でソート
print(sorted(d.items(), key = lambda x : x[1])) # itemを取出してvalue順でソート

これを使って、codons数を数えて出力をcodon名順にソート

import pandas as pd
import collections
# dict (codon count) をDFに書き込む
# d = {'B':1, 'C':2}
# df= pd.DataFrame(columns=['A', 'B', 'C'])
# 結果としてAは0, Bは1, Cは2と書いて欲しい

zerodic = collections.Counter([])  # これは不可 エントリーCが入らない
zerodic = collections.Counter({'A':0, 'B':0, 'C':0})
print(zerodic)  ## 意図は{'A', 0:, 'B': 0, 'C':0}
d = collections.Counter({'B':1, 'C':2})
print(zerodic+d)  # これは不可。カウントが0のエントリーCが入らない
#
zerodic.update({'B':1, 'C':2})  # updateだと元のzerodicがそのまま型紙になる
              # 但しupdateはin-placeで置き換えるので注意
print(zerodic)
# あとはキーでソートして値をリストにして、pd.Seriesに変換して、pd.DataFrameに追加
szero = sorted(zerodic.items(), key=lambda x: x[0])  # ペアでソート、出力はペアのリスト
szerolist = [u[1] for u in szero]  # 値だけのリストにする
print(szerolist)
ser = pd.Series(szerolist, index=df.columns)
df = df.append(ser, ignore_index=True)
print(df)

read_csvとto_csv

read_csv

pandasでcsv/tsvファイル読み込み(read_csv, read_table) | note.nkmk.me
pandas.read_csv — pandas 0.25.0 documentation
codecs — Codec registry and base classes — Python 3.7.4 documentation

import pandas as pd
pd.read_csv('filename', sep='\t', header=0, skiprows=[], encoding='cp932')

または

with open('filename', 'r', encoding='...') as fin:
    pd.read_csv(fin, ... )

なお、separatorが複数空白を含むとき、

pd.read_csv('filename', sep='\s+')

が使える。python - How to make separator in pandas read_csv more flexible wrt whitespace? - Stack Overflow

to_csv

df = ...
df.to_csv('filename', ...)

to_excelと複数シート書出し

pandas.DataFrame.to_excel — pandas 0.25.0 documentation

If you wish to write to more than one sheet in the workbook, it is necessary to specify an ExcelWriter object:

>>> df2 = df1.copy()
>>> with pd.ExcelWriter('output.xlsx') as writer:  # doctest: +SKIP
...     df1.to_excel(writer, sheet_name='Sheet_name_1')
...     df2.to_excel(writer, sheet_name='Sheet_name_2')

または、withを使わない方法として

writer = pd.ExcelWriter('output.xlsx')
...
df1.to_excel(writer, sheet_name='Sheet_name_1')
df2.to_excel(writer, sheet_name='Sheet_name_2')
...
writer.save()  # <-- これが必要
writer.close() # <-- これが必要

read_excelでパスワード付きのExcelファイルを読む場合

パスワード機能を直接pandas(やopenxylx, xlrd)に取り込む話は難しいようだ。

pip install msoffcrypto-tool
import tempfile
import msoffcrypto
import pandas as pd
from pathlib import WindowsPath
file_dir = WindowsPath(r"アンケートが入ったディレクトリのパス")
# 回答ファイルを順次確認
for file in file_dir.glob("*.xlsm"):
    # パスワード解除したテンポラリファイル作成
    with file.open("rb") as f, tempfile.TemporaryFile() as tf:
        office_file = msoffcrypto.OfficeFile(f)
        office_file.load_key(password="パスワード")
        office_file.decrypt(tf)
        # テンポラリファイルから回答をロード
        df = pd.read_excel(tf, header=None)

read_excelで階層カラムを読む場合

pandasで複数行ヘッダーを持つExcelファイルを読込みカラム(columns)を整形する - Qiita

階層カラムの例.png

df = pd.read_excel(filename, sheet_name='xxx', skiprows=y, header=[u, v, w])

これで、まず先頭y行分をスキップした後、headerで列名部分を読むが、(スキップした後の)u行目、v行目、w行目をそれぞれ第1階層・第2階層・第3階層の列名とする。

階層カラムをフラット化する

python - pandas DataFrameで階層列インデックスをフラット化するにはどうすればよいですか? - ITツールウェブ

   A     B   
   a  b  a  b
0  0  1  2  3
1  4  5  6  7

   Aa  Ab  Ba  Bb
0   0   1   2   3
1   4   5   6   7

のようにしたい、と言う場合。

列名を書き直すとすれば、

df.columns = df.columns.map(''.join)

万が一列名が文字列でない場合は、文字列に変換

df.columns = df.columns.map(lambda x: ''.join([*map(str, x)]))

をしてからjoinする必要がある。

to_excelで、セル内に改行を置きたい場合

デフォルトでは改行は不可(\nを入れても効果ない)

セルのスタイルプロパティで、wrap_text=Trueを設定する。

結局、to_excelでファイルを作ってしまってから、もう一度openpyxlでファイルをオープンして、 シート内のすべてのセルのプロパティを上記3番目で変更するのが簡単そう。

なお、変更の方法は、

cell.alignment =  cell.alignment.copy(wrapText=True)

はDepreciatedのwarningが出るので、

cell.alignment = Alignment(wrap_text=True)

にしてみた。

全体は、

# "to_Excel"でセル内改行アリが可能か?
import pandas as pd
df = pd.DataFrame([['5\n4' ,3], [2,6], [8,5]])   # データ中に\nを入れる
print(df)
df.to_excel("mytest2.xlsx")    # とにかくDFのデータからexcelファイルを作ってしまう

import openpyxl
from openpyxl.styles import Alignment
wb = openpyxl.load_workbook('mytest2.xlsx')
ws = wb['Sheet1']
for row in ws.iter_rows():
    for cell in row:      
        #cell.alignment =  cell.alignment.copy(wrapText=True) # Depreciated警告
        #cell.style.alignment.wrap_text=True    # エラー
        cell.alignment = Alignment(wrap_text=True)  # これなら動いた
wb.save('mytest2.xlsx')    # ファイルへ書き出し(書き戻し)

これによって実現できる。

matplotlib描画出力(figデータ)をイメージデータとして扱う方法

matplotlibで描いた図をイメージデータに作ることができる。あまり美しくはないが。

PythonのMatplotlibのグラフをNumPy行列に変換してOpenCVやPillowで使う — 某エンジニアのお仕事以外のメモ(分冊)

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image

x = np.linspace(0, 2*np.pi, 21)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = y1-y2
fig, ax = plt.subplots()
ax.plot(x, y1, 'b.-')
ax.plot(x, y2, 'g,-.')
ax.plot(x, y3, 'r,-.')

fig.canvas.draw()
#im = np.array(fig.canvas.renderer.buffer_rgba())
im = np.array(fig.canvas.renderer._renderer) # matplotlibが3.1より前の場合

img = Image.fromarray(im)
img.show() # これはjupyter notebook上で動いた PILのim.show()の出力はXwinへ行く

もう1つの例は(rendererの出力を文字にする) How to convert a matplotlib figure to a numpy array or a PIL image | ICARE Data and Services Center

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

# make an agg figure
fig, ax = plt.subplots()
ax.plot([1, 2, 3])
ax.set_title('a simple figure')

fig.canvas.draw()

# grab the pixel buffer and dump it into a numpy array
#X = np.array(fig.canvas.renderer.buffer_rgba()) # これはダメだった
w,h = fig.canvas.get_width_height()
buf = np.frombuffer( fig.canvas.tostring_argb(), dtype=np.uint8 )
buf.shape = ( w, h,4 )
# canvas.tostring_argb give pixmap in ARGB mode. Roll the ALPHA channel to have 
it in RGBA mode
buf = np.roll ( buf, 3, axis = 2 )

w, h, d = buf.shape
im = Image.frombytes( "RGBA", ( w ,h ), buf.tostring( ) )
im.show()

# これはjupyter notebook上で動いた PILのim.show()の出力はXwinへ行く

複数の図(subplot)を1枚の図にする時

subplotsを使って指定できる。

fig, ax = plt.subplots( ncols=len(xdata), nrows=len(ydata), figsize=(6*len(xdata), 6*len(ydata))
...
for i, x in xdata:
  for j, y in ydata:
    sns.kdeplot(data[i], data[j], ax=ax[j,i], shade=True)
plt.show()

subplotで図の間隔が狭いときは

subplots_adjustを使う

plt.subplots_adjust(wspace=0.4, hspace=0.6)

デフォルトは0.2, 0.2らしい。大きくすると広がる。枚数による。

ヒストグラム

散布図

散布図の各点に値を書く

matplotlibのannotateを使う

<Python, matplotlib> 散布図の各要素に文字を付ける。 - ねこゆきのメモ

Pandasで散布図を書くとき各要素のラベルを表示 - Qiita

annotateを使うのにaxが必要なので、たとえば、

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame([[1,3,5], [2,4,6]], index=['助教', '教授'], columns=['少ない', 'まあまあ', '多い'])
dfx = pd.DataFrame(columns=['職階', '評価', '発生数'])
for ix in df.index:
    for col in df.columns:
        line = [ix, col, df.loc[ix][col]]
        s = pd.Series(line, index=['職階', '評価', '発生数'])
        dfx = dfx.append(s, ignore_index=True)
print(df)
print('dfx\n', dfx)
ax = plt.subplot(1,1,1)  # <-- このようにしてaxを作る
plt.scatter(dfx['職階'].to_list(), dfx['評価'].to_list(), s=[u*20 for u in 
dfx['発生数'].to_list()])

for k, v in dfx.iterrows():
    print(v)
    ax.annotate(v[2],xy=(v[0],v[1]),size=14)
plt.show()

Pandasで散布図を書くとき各要素のラベルを表示 - Qiitaではaxを作る代りに、

import pandas as pd

# DataFrameにデータをセット
dd = pd.DataFrame([
        [10,50,'hoge'],
        [50,30,'fpp'],
        [20,30,'baa']
    ], columns=['ax','ay','label'])

# 散布図を描画
a = dd.plot.scatter(x='ax',y='ay')
# 各要素にラベルを表示
for k, v in dd.iterrows():
    a.annotate(v[2], xy=(v[0],v[1]), size=15)

のように、a = df.plot.scatter() としておいて、このaに対して a.annotate()。

pandasのplotでx軸ラベルの文字の向きを回転させる

裸のmatplotlibでは

plt.xticks(rotation=90)

などとする。90は右90度回転(縦書き、これがデフォルト)。横書きにするにはrotation=0。

pandasのplotの場合は、上記のxticksを加えてもよいが、plotの引数に指定することもできる。たとえば

df.plot(kind='bar', rot=0)
df.plot.bar(rot=0)

いずれも、角度の指定はxticksと同様で、0が横書きになるが、デフォルトは90の縦書き。

matplotlibで凡例legendの位置を調整する

plt.legend(bbox_to_anchor=(0.005, 0.995), loc='upper left', borderaxespad=0)

plt.legend(bbox_to_anchor=(1.005, 0.995), loc='upper left', borderaxespad=0)

凡例のテキストを変更する

python - Modify the legend of pandas bar plot - Stack Overflow

1本ごとにpltを呼んでいるとき(pandasではなくて生のmatplotlibの時)

plt.plot.bar(... label='自分のテキスト', ...)

で指定できる。

pandasでDataFrameを一気に描いてしまう(列ごとに描画など)ときは、1つ目は plt.legend()で、リストを指定できる。

df = pd.DataFrame({'A':26, 'B':20}, index=['N'])
ax = df.plot(kind='bar')
ax.legend(["AAA", "BBB"]);

1つのfigure内に複数のaxが存在してもいい。

fig, ax = plt.subplots(nrows=1, ncols=len(years), figsize=(8*len(years), 6))
for i, year in enumerate(years):
   dfhx.plot.line(ax=ax[i])
   ax[i].legend([図i用の、線の数に対応したリスト])

凡例の表示順序を反転する

Legend guide — Matplotlib 1.3.1 documentation

ax = subplot(1,1,1)
p1, = ax.plot([1,2,3], label="line 1")
p2, = ax.plot([3,2,1], label="line 2")
p3, = ax.plot([2,3,1], label="line 3")

handles, labels = ax.get_legend_handles_labels()

# reverse the order
ax.legend(handles[::-1], labels[::-1])

自分のプログラムでは、

# df2を準備しておく
a = df2.plot.bar(stacked=True)
plt.title(lvl1[0]+'/'+lvl2)
handles, labels = a.get_legend_handles_labels()
plt.legend(handles[::-1], labels[::-1], bbox_to_anchor=(1.005, 0.995), loc='upper left', borderaxespad=0)
pdf.savefig(bbox_inches='tight')
plt.show()

散布図で上記annotationを加えたときに文字が重なるのを防ぐ

いるかのボックス: Matplotlibでテキストラベルを重ならないように表示する

散布図で点のラベルを入れたときに、数が多いと、重なり合って読めなくなることがある。それを避けるには、adjust_textライブラリが使える。(adjustとtextの間に下線があったりなかったりなので要注意)

pip install adjusttext

でインストールした後、

from adjustText import adjust_text
...
texts = []
a = dfout.plot.scatter(x='重要度', y='満足度')
for k, v in dfout.iterrows():
    u = a.annotate(v[2], xy=(v[0],v[1]), size=10)
    texts.append(u)
plt.title('...')
adjust_text(texts)
plt.savefig(...)
plt.show()

これで、かなりの場合自動的に位置を決めてくれる。(但し遅くなるらしい)

複数のプロットがある場合、axを指定してadjust_textをする必要がある。指定しないと位置もずれる。

With multiple subplots, run adjust_text for one subplot at a time

fig, axes = plt.subplots(1, 2, figsize=(8, 3))
for k, ax in enumerate(axes):
   ...
   ax.plot(x, y, 'bo')
   ...
   texts = []
   for i in range(len(x)):
       t = ax.text(x[i], y[i], 'Text%s' %i, ha='center', va='center')
       texts.append(t)
   adjust_text(texts, ax=ax)

matplotlibで軸のメモリの文字を変更するには

matplotlib - 目盛、目盛のラベル、グリッドの設定方法 - Pynote

ax.set_xticklabels(リスト)、ax.set_yticklabels(リスト)で設定すればよいらしい。 但しリストの個数は元の個数(つまりxticksでの個数)に合わせる必要がありそう。 例題では、

# x 軸 (major) の目盛りを設定する。
ax.set_xticks(np.linspace(0, np.pi * 4, 5))
# x 軸 (major) の目盛りのラベルを設定する。
ax.set_xticklabels(["0", "$1\pi$", "$2\pi$", "$3\pi$", "$4\pi$"])

# y 軸 (major) の目盛りを設定する。
ax.set_yticks(np.linspace(-1, 1, 3))
# y 軸 (major) の目盛りのラベルを設定する。
ax.set_yticklabels(["A", "B", "C"])

のようにしている。自分の例では、

a = dfhxzout.plot.line(ax=ax[i], marker='x', xticks=range(len(DHankelist[col])), \
      title=year+'年入学 入学時 '+col, rot=90)
a.set_xticklabels([u[3:8] for u in DHankelist[col]])

ヒートマップ

階層的クラスタリング

import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial.distance import pdist
import matplotlib.pyplot as plt
X = np.array([[1,2], [2,1], [3,4], [4,3]])
Z = linkage(X, 'single')  # ward法を使うならば 'single' の代わりに 'ward' を指定する
dendrogram(
    Z,
    leaf_font_size=8.,  # font size for the x axis labels
)
plt.show()
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram
df_count_tpm = pd.read_csv("count_tpm.tsv", sep="\t", index_col=0)
tpm_t = df_count_tpm.T
print(df_count_tpm.head())
from scipy.spatial.distance import pdist
linkage_result = linkage(tpm_t, method='average', metric='correlation')
plt.figure(num=None, figsize=(16, 9), dpi=200, facecolor='w', edgecolor='k')
dendrogram(linkage_result, labels=df_count_tpm.columns)
plt.show()

クラスター自体を取出したい時は fcluster が使える。

import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from scipy.spatial.distance import pdist
import matplotlib.pyplot as plt
X = np.array([[1,2], [2,1], [3,4], [4,3], [1,3], [3,1]])
Z = linkage(X, 'single')  # ward法を使うならば 'single' の代わりに 'ward' を指定する
plt.figure(figsize=(20,20), dpi=200, facecolor='w', edgecolor='k')
dendrogram(
    Z,
    leaf_font_size=8.,  # font size for the x axis labels
    labels = ['A', 'B', 'C', 'D', 'E', 'F']
)
plt.savefig('dendrogram.pdf')
plt.show()

NUM_CLUSTERS = 5
nodelabels = ['A', 'B', 'C', 'D', 'E', 'F']
for num in range(5, NUM_CLUSTERS+1):
    labels = fcluster(Z, t=num, criterion='maxclust')
    #fclusterは、入力がどのクラスタに属するか(クラスタ番号 labels)を返す
    print(num, labels)
    # クラスタごとに、それに属する入力をリストとして表示
    for cl_id in range(1, num+1):
        l = [nodelabels[n] for n in range(0,len(labels)) if labels[n]==cl_id]
        print(' ', cl_id, l)

出力はtest_dendrogram.png

fclusterの出力:

5 [1 2 3 4 1 2]  # リストに各要素のクラスタid(1〜4)。第1要素と第5要素はクラスタ1に、第2と第6はクラスタ2に属する

それを書き直した結果:

  1 ['A', 'E']   # 上記を、クラスタ1には要素1と5が属すると変形し、更に要素id(1,5)をラベル(A,E)に置換えた
  2 ['B', 'F']
  3 ['C']
  4 ['D']
  5 []

k-means法によるクラスタリング

mySQLアクセス

def read_db(query):
    engine = create_engine('mysql+mysqldb://userid:password@localhost/dbname?charset=utf8',\
                           echo=False)
    dfr = pd.io.sql.read_sql(query, engine)
    return(dfr)

query = 'select * from tablename'
df = read_db(query)

ファイルの存在

import os
path = "./sample"
os.path.exists(path)

pickle

プレーンなPythonの場合

with open(picklefname, 'rb') as pf:
    df = pickle.load(pf)
with open(picklefname, 'wb') as pwf:
    pickle.dump(df, pwf)

PandasのDataFrameの場合、(メソッドがある)

df.to_pickle(picklefname)
df = pd.read_pickle(picklefname)

条件式(3項演算子)

x = "OK" if n == 10 else "NG"

文字列のリストを繋いで1つの文字列にする

s = ''
for u in list:
   s += u

'間に挿入する文字列'.join([連結したい文字列のリスト])

s = ''.join(list)

文字列の検索

>>> "spam".find("pa")
1
>>> "spam".find("x")
-1

indexは見つからないときにエラーを返す

Seriesに2引数の関数をapplyする場合

df.x.apply(f1, args=(2,))
# または、 apply にキーワード引数で与える
df.x.apply(f1, b=2)

DF内の検索

df[X].isin([list])  listでなければならない。

pandasで任意の位置の値を取得・変更するat, iat, loc, iloc

https://note.nkmk.me/python-pandas-at-iat-loc-iloc/

argv, argc

import sys
argvs = sys.argv;
argc = len(argvs)
if (argc != 2):
    print('Usage: python %s filename' % argvs[0])
    quit()

リストのソート

newls = sorted(list)

メソッドsortはin-placeでソートするので注意。

list.sort()  # listそのものが置き換わる

リストのflatten

# Python 3 で flatten する方法いろいろ

# https://qiita.com/hoto17296/items/e1f80fef8536a0e5e7db

sum([[1,2,3],[4,5,6],[7,8,9]], [])  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

第2引数の[]が必要。これは、第2引数のデフォルト値が0なので数値の+だと思い込むから。

l = [['A', 'B', 'C'], ['D', 'E'], ['F']]
print(sum(l, []))

グローバル変数

https://uxmilk.jp/12505

var = 1
def add_ten(x):
    global var
    var = 10
    print var + x
 
add_ten(2)    # 12
print var     # 10

リストの差分

https://python.ms/sub/optimization/if-vs-try/list-deletion/#_6-1-1-%E6%AF%94%E8%BC%83%E5%AF%BE%E8%B1%A1

def subtract_list(lst1, lst2):
    lst = lst1.copy()
    for e2 in lst2:
        try:
            lst.remove(e2)
        except ValueError:
            continue
    return lst

ifでチェックするより早い

型のチェック

特にNaNがあると、NaNはfloat型だと思い込むので、後の処理でぶつかることがある。

その時に、型チェックで逃げるとすると、(pandas的には個別の型チェックよりはdropnaなどの方がいいと思うが)必要になる。

Pythonで型を取得・判定するtype関数, isinstance関数 | note.nkmk.me

なのだが、isinstance()の方がよさそうだ。typeで引っかかない場合がある。

列の型変換

df['innings'].astype(np.int64)

リストの任意の位置への項目挿入

list.insert(位置, 値)

リストを置き換えるので注意

絶対値 pythonのabsとmathのfabs、numpyのabs(absolute)、fabs

pythonのabsは、整数の絶対値は整数、小数の絶対値は小数、複素数の絶対値も可。

mathのfabsは、整数に対しても小数に対しても、絶対値は小数を返す。複素数は不可。

numpyのabs(=absoluteと書いてもいいらしい)は配列ndarrayに対しても適用できる。 もし整数と小数が混ざっていると、小数にする(最大精度)。

numpyのfabsはmathのfabsと同様に、必ず小数を返す。複素数は不可。

pie chartで生のデータを入れる

python - Pandas pie plot actual values for multiple graphs - Stack Overflow

ヒストグラム・カウント

df.hist()
(df['column']).hist()
(df['column']).value_counts()
(df['column']//10*10).value_counts()

Pandasでデータの値の頻度を計算するvalue_counts関数の使い方 - DeepAge

カウント結果はSeriesなので、

s = pd.Series([3, 2, 7, 2, 3, 4])
u = s.value_counts(sort=False)
print(u)
# 2    2
# 3    2
# 4    1
# 7    1

print(type(u))
<class 'pandas.core.series.Series'>

print(u.index)
Int64Index([2, 3, 4, 7], dtype='int64')

# カウント結果uをindexでソートすると(黙っていると値の降順でソート)
print(u.sort_index(ascending=False))
# 7    1
# 4    1
# 3    2
# 2    2

科目別の成績データをGroupByで集計整理し、点数ヒストグラムを描く例

# df が入力の科目別成績データ 
df = df[['学籍番号', '学科', '入学年度', '取得素点']]
dfmean = df.groupby('学籍番号').mean()  # 1人1人の在学中の評点平均
dfgakka = dfmean.groupby(['学科', '入学年度'])['取得素点'].apply(lambda d: (d//5*5).value_counts(bins=list(range(0,101,5))).sort_index()) 
# dfgakkaは、Seriesで、indexが ('学科', '入学年度', '評点の5点刻みのクラス')の3レベル
   
for year in [2010, 2011, 2012, 2013, 2014, 2015]:
    # DataFrameに直す
    for gakka in [51, 52, 53, 54, 55, 56]:    # 学科ごとに列にする
        ser = dfgakka.xs(year, level='入学年度').xs(gakka, level='学科')
        if gakka==51:
            dfout = pd.DataFrame(ser)
            dfout.columns = [gakka]
        else:
            dfout[gakka] = ser
    dfout = dfout.rename(columns=gakkaname)
    dfout.index = [u.left for u in dfout.index.to_list()]
    #dfoutx.plot.bar(figsize=(8,6), rot=0)
    dfout.plot.line(figsize=(8,6), marker='x', rot=0, xticks=[u for u in dfout.index.to_list()])
    plt.title('通算評点頻度分布 '+str(year)+' 年入学')
    plt.xlabel('平均通算評点')
    plt.ylabel('人数')
    plt.legend(bbox_to_anchor=(1.01, 0.995), loc='upper left', borderaxespad=0)
    pdf.savefig(bbox_inches='tight')
    plt.show()

またヒストグラムが人数の代わりに%にするには

    dfm = dfout.sum()
    dfm = dfout/dfm*100
    dfm = dfm.rename(columns=gakkaname)
    #dfm.plot.bar(figsize=(8,6), rot=0)
    dfm.plot.line(figsize=(8,6), marker='x', rot=0, xticks=[u for u in dfm.index.to_list()])

特定条件を満たす要素数をカウント

pandasで特定の条件を満たす要素数をカウント(全体、行・列ごと) | note.nkmk.me

たとえば

df['age']<25

はdf中の25未満のデータに対してTrueを入れたSeriesを返すから

(df['age']<25).sum()

によって個数を数えることができる。(Trueは1、Falseは0)

単にデータの個数(NaNでないデータの個数)を数えるだけなら、

df.count()   # 列ごと(縦)にカウント
df.counts(axis=1)   # 行ごと(横)にカウント

が使える。

matplotlibでsavefigしたときに下が切れる場合

Matplotlib (with seaborn) で出力するPDFの下のほうが切れてしまうときは bbox_inches='tight' - Scala日記

bbox_inches = "tight"とかそれ系のやつ - virsalusの日記

plt.savefig('sample.pdf', bbox_inches='tight')

とするか

plt.tight_layout()

とするかで対応。

距離行列dmatがあるとき、

ndmat =squareform(dmat)
lk = linkage(ndmat,method='average')
plt.figure(num=None, figsize=(22, 12), dpi=200, facecolor='w', edgecolor='k')
dendrogram(lk, labels=dmat.index, leaf_rotation=90)
plt.tight_layout()
# plt.savefig('corr_coeff_dendrogram.png', bbox_inches="tight")
plt.savefig('corr_coeff_dendrogram.png')

XML to Dict

Python: xmltodict を使って XML を辞書へ変換

biopython, SNP in feature

BioPython Tutorial

4.3.2.4 Location testing

You can use the Python keyword in with a SeqFeature or location object to see if the base/residue for a parent coordinate is within the feature/location or not.

For example, suppose you have a SNP of interest and you want to know which features this SNP is within, and lets suppose this SNP is at index 4350 (Python counting!). Here is a simple brute force solution where we just check all the features one by one in a loop:

>>> from Bio import SeqIO
>>> my_snp = 4350
>>> record = SeqIO.read("NC_005816.gb", "genbank")
>>> for feature in record.features:
...     if my_snp in feature:
...         print("%s %s" % (feature.type, feature.qualifiers.get("db_xref")))
...
source ['taxon:229193']
gene ['GeneID:2767712']
CDS ['GI:45478716', 'GeneID:2767712']

Note that gene and CDS features from GenBank or EMBL files defined with joins are the union of the exons – they do not cover any introns.

Matplotlibで、複数のfigureを1つのPDFファイルに保存する方法

matplotlibで複数のfigureを一つのpdfに保存する - Qiita

import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages  # <-- 追加

pdf = PdfPages('ファイル名.pdf')   # <-- 追加 (pdfをオープン)

for ... :   # 複数枚作る
    plt.scatter(....)    # plotして図を作る
    pdf.savefig()   #  <-- savefigの行先はpdfになる
    plt.show()    # <-- 画面表示

pdf.close()   # <-- 最後に忘れないように 

pandas plot.barでX軸目盛の文字が縦を向かない件

DataFrame df を df.plot.bar() でグラフ表示するとき、X軸目盛の文字が 横向きになってしまう。

いろいろやってわかったのは、

df.plot.bar(y='カラム名')

とすると正しく表示されるが、

df.plot.bar()

だとダメ。y=を書かないスタイルは、すべてのカラムが表示されるので具合がいい。 dfに1つしかカラムがなければそれで済むし、複数あればそれらがみな表示される。 ラベルのことさえ気にしなければこれでも十分だろう)

また、xticksを使って回転させるワザは働かなかった。たとえば

plt.xticks(rotation=90)

とかは効かなかった。

Seaborn heatmapで上下単が切れる問題

seabornのheatmapでy軸が足りなくて、annotが活用できない - Qiita

バージョンアップで解決。古いバージョンの時は逃げる方法がある。 ax.set_ylim(len(flights), 0)

Seaborn heatmapまわり

とりあえずマニュアル

seaborn.heatmap — seaborn 0.9.0 documentation

annotation

fmtの指定: fmt='.2f'とかfmt='d'とか

カラーパレット

Seabornのカラーパレットの選び方 - Qiita

Seabornで相関をヒートマップにする(行・列を並び替えながら) / Heatmap using Seaborn (order rows and columns as you like) - Qiita

seabornの細かい見た目調整をあきらめない - Qiita

annot_kws={'fontsize': 9, 'color': 'green'} とかできる。

連関係数の計算に使えるCrosstab

crosstab() — researchpy 0.1.1 documentation

PythonからPDFが書き出せるreportlab

ReportLab - Content to PDF Solutions

で、この辺が使えそう。

# reportlab platypus Tableを使う例
# reportlab の Table の表示位置をコントロールする
# https://qiita.com/ekzemplaro/items/09bd10b02ecbb35c0efa

from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfgen import canvas
from reportlab.platypus import Image, Paragraph, Table
from reportlab.pdfbase import pdfmetrics
#from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfbase.ttfonts import TTFont
# ------------------------------------------------------------------
#fontname_g = "HeiseiKakuGo-W5"
#pdfmetrics.registerFont(UnicodeCIDFont(fontname_g))
pdfmetrics.registerFont(TTFont('IPAexGothic', 'ipaexg.ttf')) 

cc = canvas.Canvas('example.pdf', pagesize=A4)
width, height = A4

cc.setFont("IPAexGothic", 16)
str_out = "こんにちは"
cc.drawString(100, 730, str_out)

data = [["テスト", 2, 3], ["日本語", 1, 3], [3, 2, 10]]

table = Table(data, colWidths=20*mm)
table.setStyle([("VALIGN", (0,0), (-1,-1), "MIDDLE"),
                ("ALIGN", (0,0), (-1,-1), "CENTER"),
                ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
        ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
#        ('FONT', (0, 0), (-1, -1), "HeiseiKakuGo-W5", 16),
        ('FONT', (0, 0), (-1, -1), "IPAexGothic", 16),
        ])
#
table.wrapOn(cc, width, height)
#
table.drawOn(cc, 140*mm, 250*mm)
table.drawOn(cc, 75*mm, 225*mm)
table.drawOn(cc, 10*mm, 200*mm)
#
styles = getSampleStyleSheet()
my_style = styles["Normal"]
my_style.name = "bonlife"
#my_style.fontName = "HeiseiKakuGo-W5"
my_style.fontName = "IPAexGothic"
my_style.fontSize=16

ptext = "これはサンプルです。"
pp = Paragraph(ptext, style=my_style)
pp.wrapOn(cc, 70*mm, 50*mm)  # size of 'textbox' for linebreaks etc.
pp.drawOn(cc, 50*mm, 190*mm)    # position of text / where to draw

cc.showPage()
cc.save()
print('complete')

と、もっと簡易版で

# reportlab platypus Tableを使う例
# reportlabのplatypusを使ってtableを描画するサンプル。wrapOnを呼びなさいということ
#https://gist.github.com/bgnori/4452571
   
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm

from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

from reportlab.lib import colors
from reportlab.platypus import Table

#pdfmetrics.registerFont(TTFont('IPA Gothic',
#    '/usr/share/fonts/ipa-gothic/ipag.ttf'))
pdfmetrics.registerFont(TTFont('IPAexGothic', 'ipaexg.ttf'))

xmargin = 8.4*mm
ymargin = 8.8*mm
swidth = 48.3*mm
sheight = 25.4*mm

c = canvas.Canvas('example2.pdf', pagesize=A4)

#c.drawString(xmargin, ymargin, u"どや、pdfやで。reportlab!")

t = Table([['a', 'b'], ['1', '2']])
#t.setStyle([('TEXTCOLOR', (0,0), (1,0), colors.red)])  # 元サンプルではこうしている
t.setStyle([('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), # 罫線、上のサンプルから借用
        ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
        ('FONT', (0, 0), (-1, -1), "IPAexGothic", 16),
        ])

t.wrapOn(c, 100*mm, 100*mm)

t.drawOn(c, 100*mm, 100*mm)
c.showPage()
c.save()
print('complete')

〜〜〜〜〜〜〜〜〜〜〜〜

おまけ jupyter notebookのセルの幅を広げたいとき

Jupyter Notebookのセル幅を広げたい! - Qiita がよさそう

pip install jupyterthemes

jt -cellw 95%

または、
IPython/Jupyter Notebook enlarge/change cell width · GitHub

custom.css で

.container {
    width: 99% !important;
}   

div.cell.selected {
    border-left-width: 1px !important;	
}

div.output_scroll {
    resize: vertical !important;
}

で、Jupyter Notebookの再起動が必要らしい。

おまけ bashのループ

#!/usr/bin/bash
samples=("Anc" "1_2-1" "2_2-1" "2_5-1" "2_5-1-7A" "1_2-2" "2_2-2" "2_6-2" "2_6-2-10E")
for f in "${samples[@]}"
do
  #nohup python ProcessGD.py $f > $f.out &
  echo $f
done

おまけ linux上のunzipで漢字コード名の文字コードをWindows(SJIS)からLinux(UTF-8)へ変換する

unzip -O sjis foo.zip

これによって、内蔵されるファイルの名前をSJISからUTF-8に変換できる。


添付ファイル: file階層カラムの例.png 100件 [詳細] filetest_dendrogram.png 105件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2020-07-08 (水) 13:56:44 (37d)