[[Pythonバイオ]] ~
&counter();   &lastmod();~

*pandasティップス [#gfafdcc8]

**行選択と列選択 [#s06c1b34]
***行位置選択 [#ocd73bc9]

***行の値選択、列の値選択 [#c8f0f893]

**セル選択 [#l2d0b6fa]

**行演算と列演算 [#g8a09294]

**行間での足し算 [#p4eda8c1]
 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で数値の桁数を丸めるには [#o7af6fd8]
-表示だけ丸める~
[[pandasの表示設定変更(小数点以下桁数、有効数字、最大行数・列数など) | note.nkmk.me
https://note.nkmk.me/python-pandas-option-display/]]
 pd.options.display.precision = 2  # すべての表示を小数点以下2桁に丸める

-数値そのものを丸める(描画で字を書くとか)
 df.round(2)  # すべてを小数点以下2桁に丸める

**2列間での演算 [#b2cbdb07]
[[Pandasで複数の列を値をもとに、新しい列を任意の関数で定義する方法 | Shikoan's ML Blog:https://blog.shikoan.com/pandas-newcolumn-lambda/]]

 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)

**値・行名/列名でソートする [#n7f98c79]
[[pandas.DataFrame, Seriesをソートするsort_values, sort_index | note.nkmk.me:https://note.nkmk.me/python-pandas-sort-values-sort-index/]]

値でソートする
 df.sort_values()
 df.sort_values(ascending=False)
行名でソートする
 df.sort_index()
列名でソートする
 df.sort_index(axis=1)


**ifのある内包 [#nc7b9210]
 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)]

**三項演算子(条件式) [#b032fe2b]
[[Pythonの三項演算子(条件演算子)でif文を一行で書く | note.nkmk.me:https://note.nkmk.me/python-if-conditional-expressions/]]~
[[6. 式 (expression) — Python 3.8.0 ドキュメント 6.12. 条件式 (Conditional Expressions):https://docs.python.org/ja/3/reference/expressions.html#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 [#h8fdf3a7]
[[pandas.DataFrame, Seriesの要素の値を置換するreplace | note.nkmk.me:https://note.nkmk.me/python-pandas-replace/]]
 df = df.replace(oldvalue, newvalue)

**列名の付け替え rename [#bbba1027]
[[pandas.DataFrameの行名・列名の変更 | note.nkmk.me:https://note.nkmk.me/python-pandas-dataframe-rename/]]
 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 [#b993a345]
[[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('列名') # 元のインデックスを列として残しつつ変更

**列位置の入れ替え [#ffcfd8c3]
 df = df.loc[:, ["col3", "col1", "col2", "col0"]]

**行・列のdrop [#z8e03439]
[[pandas.DataFrameの行・列を指定して削除するdrop | note.nkmk.me:https://note.nkmk.me/python-pandas-drop/]]
 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']!='')]
でできる。

**列名が重複してしまった時の列の削除 [#bf69d059]
 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

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

**DataFrameから辞書 [#n9c1c571]
[[pandas.DataFrame, Seriesを辞書に変換(to_dict) | note.nkmk.me:https://note.nkmk.me/python-pandas-to-dict/]]

DataFrameの2つの列の間の関係を辞書にするためには、
-キー側の列の値をindexとし、
-値を(列値として)並べたSeriesを作る ~
DataFrameからSeriesを作るには、Ser = DF['カラム名']とする。(四角括弧が一重)
-そのSeriesを、ser.to_dict() で辞書型に変換する

**Series/DataFrameからリストに変換するなら .values.tolist() [#zff41b08]
[[pandas.DataFrame, SeriesとPython標準のリストを相互に変換 | note.nkmk.me:https://note.nkmk.me/python-pandas-list/]]

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する [#dacc5443]
[[duplicatedとdrop_duplicates:https://note.nkmk.me/python-pandas-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つだけ残して削除

**文字列を含む行の抽出 [#g058fbd0]
[[pandasで特定の文字列を含む行を抽出(完全一致、部分一致) | note.nkmk.me:https://note.nkmk.me/python-pandas-str-contains-match/]]

完全一致は == でよい。
 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と同様の動作) が使える。

**要素が指定リストに含まれる行の抽出 (文字列ではなくてリスト) [#h6f9b92f]
[[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 [#u931854d]
[[pandas.DataFrameをGroupByでグルーピングし統計量を算出 | note.nkmk.me:https://note.nkmk.me/python-pandas-groupby-statistics/]]
 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の列が残る。

**Groupbyのグループを取出す [#t7c1b24c]
[[Pandas の groupby の使い方 - Qiita:https://qiita.com/propella/items/a9a32b878c77222630ae]]

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 [#f837b768]
[[pandasで要素、行、列に関数を適用するmap, applymap, apply | note.nkmk.me:https://note.nkmk.me/python-pandas-map-applymap-apply/]]


**複数dfをjoin [#g92089e4]
[[reference:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.join.html]]

DFのリスト joinlist = [df1, df2, ..., dfn] を用意し、dfにjoinする。
 dfout = df.join(joinlist)
このとき、キー指定("on"の指定)はできない。インデックスをキーにしてjoinする。

つまり、あらかじめインデックスを付け替えておく必要がある。
 df1 = df1.set_index('column to use as key')

**NaN扱い [#s643db78]
NaNはnumpyのnan

DFでなければ
 if np.isnan(np.nan):
     hogehoge

DFでのnan判定

[[Reference→API reference のgeneral function:https://pandas.pydata.org/pandas-docs/stable/reference/general_functions.html#top-level-missing-data]]にある
 df = pd.DataFrame8{[1,3],[2,np.nan]])
 print(df.isnull())
 print(df.notnull())
なお、isnull()はisna()のalias、notnull()はnotna()のalias


**初めに空のDataFrameを作って後からデータ行を足す [#u5bf5dd4]
 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の結果を加える [#h1f83e12]
[[[Python] リストの要素の数を数える (collections.Counter) | Hibiki Programming Notes:https://hibiki-press.tech/learn_prog/python/collections-counter/3769]]

 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 --- コンテナデータ型 &#8212; Python 3.6.10rc1 ドキュメント:https://docs.python.org/ja/3.6/library/collections.html]]では
 >>> 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はほぼ辞書と同じなので、次項のソートが使える。

**辞書のソート [#gc6f2ebd]
[[Pythonの辞書(dict)をソートする方法まとめ | HEADBOOST:https://www.headboost.jp/python-how-to-sort-dict/#21]]
 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 [#uc197a4a]
***read_csv [#c3b65411]
[[pandasでcsv/tsvファイル読み込み(read_csv, read_table) | note.nkmk.me:https://note.nkmk.me/python-pandas-read-csv-tsv/]]~
[[pandas.read_csv &#8212; pandas 0.25.0 documentation:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html]]~
[[codecs &#8212; Codec registry and base classes &#8212; Python 3.7.4 documentation:https://docs.python.org/3/library/codecs.html#standard-encodings]]

 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:https://stackoverflow.com/questions/15026698/how-to-make-separator-in-pandas-read-csv-more-flexible-wrt-whitespace]]

***to_csv [#v2cce651]
 df = ...
 df.to_csv('filename', ...)


**read_excelとto_excel [#of1b91ac]
[[pandas.DataFrame.to_excel &#8212; pandas 0.25.0 documentation:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_excel.html]]

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')

**to_excelで、セル内に改行を置きたい場合 [#kbbbf58d]
デフォルトでは改行は不可(\nを入れても効果ない)

セルのスタイルプロパティで、wrap_text=Trueを設定する。
-[[Python: How to create multi line cells in excel when exporting a pandas dataframe Stack Overflow:https://stackoverflow.com/questions/50908676/python-how-to-create-multi-line-cells-in-excel-when-exporting-a-pandas-datafram]] StyleFrameは使いたくない?
-[[python - Writing multi-line strings into cells using openpyxl - Stack Overflow:https://stackoverflow.com/questions/15370432/writing-multi-line-strings-into-cells-using-openpyxl]] Excelファイルを作りながら(データを置きながら)改行を入れる感じ。データを入れるところはto_Excelで楽したい。
-[[python - Apply 'wrap_text' to all cells using openpyxl - Stack Overflow:https://stackoverflow.com/questions/42215933/apply-wrap-text-to-all-cells-using-openpyxl/52494457]] これですべてのセルにwrap-textを付けられる。

結局、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')    # ファイルへ書き出し(書き戻し)
これによって実現できる。

**ヒストグラム [#ic4eefe7]

**散布図 [#h1e623b6]

**散布図の各点に値を書く [#b68bb648]
matplotlibのannotateを使う

[[<Python, matplotlib> 散布図の各要素に文字を付ける。 - ねこゆきのメモ:http://nekoyukimmm.hatenablog.com/entry/2015/10/08/224607]]

[[Pandasで散布図を書くとき各要素のラベルを表示 - Qiita:https://qiita.com/kujirahand/items/bdc574102148c7f1f041]]

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:https://qiita.com/kujirahand/items/bdc574102148c7f1f041]]では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()。


** 散布図で上記annotationを加えたときに文字が重なるのを防ぐ [#n4c36725]
[[いるかのボックス: Matplotlibでテキストラベルを重ならないように表示する:https://irukanobox.blogspot.com/2019/12/matplotlib.html]]

散布図で点のラベルを入れたときに、数が多いと、重なり合って読めなくなることがある。それを避けるには、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()
これで、かなりの場合自動的に位置を決めてくれる。(但し遅くなるらしい)

**ヒートマップ [#z0569817]

**階層的クラスタリング [#uec2b39e]
 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)
出力は&ref(./test_dendrogram.png,200x200);

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法によるクラスタリング [#g269d509]

**mySQLアクセス [#mb48d1b0]
 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)

**ファイルの存在 [#ead665d2]
 import os
 path = "./sample"
 os.path.exists(path)

**pickle [#r9cb401a]
プレーンな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項演算子) [#g859559a]
 x = "OK" if n == 10 else "NG"

**文字列のリストを繋いで1つの文字列にする [#ec82a71e]
 s = ''
 for u in list:
    s += u

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

**文字列の検索 [#i02e3afc]
 >>> "spam".find("pa")
 1
 >>> "spam".find("x")
 -1
indexは見つからないときにエラーを返す

**Seriesに2引数の関数をapplyする場合 [#k62ded28]
 df.x.apply(f1, args=(2,))
 # または、 apply にキーワード引数で与える
 df.x.apply(f1, b=2)

**DF内の検索 [#t51adbc8]
 df[X].isin([list])  listでなければならない。

**pandasで任意の位置の値を取得・変更するat, iat, loc, iloc [#r3974ed5]
 https://note.nkmk.me/python-pandas-at-iat-loc-iloc/

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

**リストのソート [#k7cb2fe9]
 newls = sorted(list)
メソッドsortはin-placeでソートするので注意。
 list.sort()  # listそのものが置き換わる

**リストのflatten [#bdcafeb5]
#   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]

**グローバル変数 [#hd2fe0e1]
https://uxmilk.jp/12505
 var = 1
 def add_ten(x):
     global var
     var = 10
     print var + x
  
 add_ten(2)    # 12
 print var     # 10

**リストの差分 [#w1338c7e]
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でチェックするより早い

**型のチェック [#yf21f317]
特にNaNがあると、NaNはfloat型だと思い込むので、後の処理でぶつかることがある。

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

[[Pythonで型を取得・判定するtype関数, isinstance関数 | note.nkmk.me:https://note.nkmk.me/python-type-isinstance/]]

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

**列の型変換 [#la791077]
 df['innings'].astype(np.int64)

**リストの任意の位置への項目挿入 [#aa79d344]
 list.insert(位置, 値)
リストを置き換えるので注意


**絶対値 pythonのabsとmathのfabs、numpyのabs(absolute)、fabs [#z9e9ee09]
pythonのabsは、整数の絶対値は整数、小数の絶対値は小数、複素数の絶対値も可。

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

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

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

**pie chartで生のデータを入れる [#o036590d]
[[python - Pandas pie plot actual values for multiple graphs - Stack Overflow:https://stackoverflow.com/questions/48299254/pandas-pie-plot-actual-values-for-multiple-graphs]]


**ヒストグラム・カウント [#s4f2f043]

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

[[Pandasでデータの値の頻度を計算するvalue_counts関数の使い方 - DeepAge:https://deepage.net/features/pandas-value-counts.html]]

カウント結果は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

**matplotlibでsavefigしたときに下が切れる場合 [#b8a7177a]
[[Matplotlib (with seaborn) で出力するPDFの下のほうが切れてしまうときは bbox_inches='tight' - Scala日記:http://ym.hatenadiary.jp/entry/2018/08/16/111900]]

[[bbox_inches = "tight"とかそれ系のやつ - virsalusの日記:http://virsalus.hatenablog.com/entry/2015/01/19/120931]]

 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 [#ica5d9f8]
[[Python: xmltodict を使って XML を辞書へ変換:https://ccieojisan.net/post-1346/]]

** biopython, SNP in feature [#sfc15684]
[[BioPython Tutorial:http://biopython.org/DIST/docs/tutorial/Tutorial.html]]

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 &#8211; they do not cover any introns.


**Matplotlibで、複数のfigureを1つのPDFファイルに保存する方法 [#lda0034f]
[[matplotlibで複数のfigureを一つのpdfに保存する - Qiita:https://qiita.com/ceptree/items/c7b18fdf938ec1b5021e]]

 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軸目盛の文字が縦を向かない件 [#l3ea0ec0]

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

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

また、xticksを使って回転させるワザは働かなかった。たとえば
 plt.xticks(rotation=90)
とかは効かなかった。


**Seaborn heatmapで上下単が切れる問題 [#j4fcdba6]
[[seabornのheatmapでy軸が足りなくて、annotが活用できない - Qiita:https://qiita.com/yoshi65/items/90532732bf9d3875bec7]]

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

**Seaborn heatmapまわり [#x9109b9e]
***とりあえずマニュアル [#v4bf727c]
[[seaborn.heatmap &#8212; seaborn 0.9.0 documentation:https://seaborn.pydata.org/generated/seaborn.heatmap.html]]

*** annotation [#s2ce3c29]
fmtの指定: fmt='.2f'とかfmt='d'とか

***カラーパレット [#u346c5db]
[[Seabornのカラーパレットの選び方 - Qiita:https://qiita.com/SaitoTsutomu/items/c79c9973a92e1e2c77a7]]

[[Seabornで相関をヒートマップにする(行・列を並び替えながら) / Heatmap using Seaborn (order rows and columns as you like) - Qiita:https://qiita.com/Ken-Kuroki/items/81a09c956118e04cd00f]]

[[seabornの細かい見た目調整をあきらめない - Qiita:https://qiita.com/skotaro/items/7fee4dd35c6d42e0ebae]]

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

**連関係数の計算に使えるCrosstab [#d394122a]
[[crosstab() &#8212; researchpy 0.1.1 documentation:https://researchpy.readthedocs.io/en/latest/crosstab_documentation.html]]


*PythonからPDFが書き出せるreportlab [#x2283b9c]
[[ReportLab - Content to PDF Solutions:https://www.reportlab.com/]]

-[[PythonのPDFライブラリ「ReportLab」の使い方(用紙サイズ、向き、文字出力、改ページ) - Symfoware:https://symfoware.blog.fc2.com/blog-entry-769.html]]
-[[PythonのReportLabで、表(TableやTableStyle)について調べてみた - メモ的な思考的な:https://thinkami.hatenablog.com/entry/2017/01/19/062615]]
-[[reportlab の Table で日本語を使う - Qiita:https://qiita.com/ekzemplaro/items/a3e3d4419a560f3185e3]]
-[[reportlab の Table の表示位置をコントロールする - Qiita:https://qiita.com/ekzemplaro/items/09bd10b02ecbb35c0efa]]
-[[reportlabのplatypusを使ってtableを描画するサンプル。wrapOnを呼びなさいということ &#183; GitHub:https://gist.github.com/bgnori/4452571]]

で、この辺が使えそう。

 # 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')



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

**おまけ bashのループ [#m8969d16]
 #!/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

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS