はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

カジュアルなETLで利用したい人のためのPython/ Pandasミニミニミニチュートリアル

まえおき

他のスクリプト言語などの経験からPythonは雰囲気程度の読みこなしはできるけど、ちょっとした文字列中心のETLっぽいことがしたくてfor文はあまり書きたくなくて、Pandasだとそれが楽にできそうだけど、NumPyから入るのもちょっと目的が違うし、一度しか使わないかもしれないし、という人向けに、Pandasのシンタックスをまとめてみました。

特別何かの処理をするというところまでは示していないのですが、ここにあげたシンタックス例を眺めてみると、Pandasで何ができそうか察することができる?ようなものを目指しました。

f:id:azotar:20190323141339p:plain

例えばElasticsearchにデータを入れるためにちょっとデータ加工したいというレベルでPandasをかじりたい時に、見よう見まねでコピペできるぐらいのものが見つけられませんでした*1

といことで、どうせならということで、Pandasのシンタックスの基本的なものをざっくりあげてみたのでした。ぼちぼち解説は盛り込みましたが、この目的に沿った範囲のものになっていますので、いろいろツメが甘いと思いますので、興味がわいたら書籍や公式サイトをあたってください。

参考リンク

一応、公式のリンクはこちら。 pandas.pydata.org

また、私が良く参考にさせていただいているチートシートを日本語訳されているのがこちら。

qiita.com

Pandasシンタックスに慣れるためのサンプルコード

Pythonが入っている環境だと pip install numpy、 pip install pandas すれば動くレベルのものです。

import pandas as pd
import numpy as np
import sys
import copy
import json
from pandas.io.json import json_normalize


"""
本題とは関係ない解説用のログ出力用の関数。 Jyupiter利用などははしていないもので...
単なるタイプ量削減です。
"""


def msg(var):
    BAR = '----------------'
    print(BAR + str(var) + BAR)
    return True


def p(var):
    print(var)


def check_member_simply(method_name, obj):
    if method_name in list(map(str, dir(obj))):
        return True
    return False


"""
★1★

PandasはDataFrame(行と列それぞれに見出し情報付きの2次元の帳票データみたいなもの)を利用。
Series(1次元)とPanel(3次元)もあるけど、後者はテキスト系のカジュアルなデータ処理にはあまり使わなそう。
前者は、DataFrameの1行か1列分と考えるとわかりやすい。
"""
# DataFrameのコンストラクタの例
col = ['a', 'b', 'c', 'fruit']
foo = pd.DataFrame(columns=col,
                   data=[[1, 1, 1, 'apple'],
                         [2, 2, 2, 'orange'],
                         [3, 3, 3, 'lemon'],
                         [4, 4, 4, 'lemon']
                         ]
                   )

msg("DataFrame取り込み")
p(foo)

msg("列方向の項目名")
p(foo.columns)


msg("DataFrameの中身")
p(foo)

msg("DataFrameの値をlistのlistで出力")
p(foo.values)

msg("DataFrameの列へのアクセス")
p(foo.a)
msg("")
p(foo['a'])
msg("")
p(foo[['a', 'c']])
msg("")
aaa = 'a'
p(foo[aaa])

msg("applyによる宣言っぽい一括データ操作その1")
p(foo.apply(lambda x: x * 2))
# DataFrameの全てのマス目に対して2倍にしたDataFrameを返す。
# applyは掘り下げ要。NumPyを通過していない人が、Pandasの例でこの例だけ見ると実は混乱する。
# →後述します。

"""
↓ こんな結果が返ってきます。2倍ですね。

dtype: int64
   a  b  c         fruit
0  2  2  2    appleapple
1  4  4  4  orangeorange
2  6  6  6    lemonlemon
3  8  8  8    lemonlemon
"""

# applyの件は後述するものの、↓の感触を覚えておくと良い。
msg("Numpy由来のブロードキャスト")
p(foo * 2)
# 注目:前出のapplyの例と同じ結果が得られる。

# [ブロードキャスト]
# 通常の数学における行列の演算では扱えないような、行や列の数(shape)があっていないデータどうしでも簡易な記法で計算できるようにする仕掛け
#  例. 2次元の数値の配列と 1次元の数値の配列を足す。
# 逆にいうと計算できるようにするために、未定義のカラムなどの値にデフォルト値をおぎなったり、pandasが計算可能な形にもっていくことができる(4つのルール)ことが条件となる
# ↓ 参考
# https://deepage.net/features/numpy-broadcasting.html
#
#


"""
★2★
"""
msg("applyによる宣言っぽい一括データ操作その2")
p(foo.apply(lambda x: max(x)))
# →後述します。


msg("loc[行の指定,列の指定]によるアクセス")
p(foo.loc[:, :])
msg("")
p(foo.loc[:, 'a'])
msg("")
p(foo.loc[:, 'a'])
msg("")
p(foo.loc[1, 'a'])
msg("")
p(foo.loc[foo['a'] > 1, ['a', 'b']])
msg("")

msg("filterによる列名の正規表現でのアクセス")
p(foo.filter(regex='c+'))

msg("queryによる列の値が該当の条件にヒットする行の抽出。つまりselect")
p(foo.query("a==2 and b==2"))

msg("DataFrame['a'] > 1 は、列aの値が1より大きい行の、boolのSeries(行インデックス番号付き)返す")
p(foo['a'] > 1)

msg("なので、foo[foo['a'] > 1]は、selectとして機能する")
p(foo[foo['a'] > 1])

msg("条件に該当した行のみ、値を追加")

print("補足:Pythonは参照渡しでこれは普通なのかもしれないけど、PandasでETLっぽい視点でデータ操作しているとデータを潰してしまう。")
print("ここでは、DataFrame fooは触りたくないので、foo.copy()で新しいfootmpに代入する。")
print("footmp = foo.copy()")
footmp = foo.copy()
print("footmp['x'] = 1 ")
footmp['x'] = 1
p(footmp)
footmp['only_over2'] = footmp[footmp['a'] >
                              2]['b'].apply(lambda x: x * 10)
print("'a'の値が2より大きい行だけ、'b'の値を10倍して、その値を'only_over2'の列に代入")
p(footmp)


msg("複数のDataFrameの結合(SQLのJOIN、....)")
print("SQLのJOINにあたるのはDataFrame.merge")
p(pd.merge(foo, foo, left_on=["a"], right_on=[
    "a"], how='left', indicator='ind', suffixes=['_l', '_r']))


"""
参考
http://sinhrks.hatenablog.com/entry/2014/11/27/232150
"""
msg("PandasはNumPyが親なので(だからだと思うが)、こんな演算もできる")
p(foo['a'] + foo['b'])
msg("")
p(foo['a'] * foo['b'])

msg("代入もできる。Seriesどうしを演算して新たなSeriesを生成してそれを、DataFrameの1列(Series)に代入する")
footmp = foo.copy()
footmp['a+b'] = foo['a'] + foo['b']
p(footmp)

msg("複数列にアクセスするとDataFrameが戻る")
footmp = footmp[['a', 'a+b']]
p(footmp)


"""

★3★

applyとmapとapplymap
こちらの図解を参考にさせていただく
http://sinhrks.hatenablog.com/entry/2015/06/18/221747

まず、DataFrame.apply(関数, axis=0) から理解。
  ※axis=0はデフォルト扱いで省略可能
     → この例だと、各列について、その列のSeriesを「関数」に渡す。
       ここで、関数をlambdaにすると良くわからないので、理解のためには、関数を定義して、引数で渡されるSeriesをprintしてみると良い(と思った)
"""

"""
ということでこんな関数を定義
"""


def my_func(series):
    print("my_funcがapplyよりコールされました")
    print("seriesのtype")
    print(type(series))
    print("seriesのサイズ")
    print(series.size)
    print("seriesのvalues")
    print(series.values)
    print("seriesのindex")
    print(series.index)
    print("seriesをprint")
    print(series)

    # 通常は、seriesを集計するなどの演算をするが、以下、あえて固定値の10000を返してみる
    return 10000


msg("applyのからくり: DataFrame.apply()")
mf = my_func

p(foo.apply(mf))
"""
こんな感じで出力される。
a        10000
b        10000
c        10000
fruit    10000

元の列ごとに集計したSeriesが得られる。
(fruitの列は、apple、orange、lemon、lemonを取り扱うが、今回は10000を固定で返したので、ここでは、10000)
"""

msg("applyのからくり: DataFrame.apply(,axis=1)")
p(foo.apply(mf, axis=1))

"""
こんな感じで出力される。
0    10000
1    10000
2    10000
3    10000

0、1、2、3は、わかりにくいが、元のDataFrameのindexの値。
つまり、各行ごとに列の値を使って、なんらか演算したSeriesが得られる。
ここでは、各列の値は使わずに固定で10000を返したが、my_funcの中で、series['fruit']で値が取得できるので、それを使って、条件判定したり、新たな編集後の値を生成できる。



文字列をカジュアルに扱うETL風にPandasを使うなら、この用法の活用頻度は高い。例えば、カラムAとBを結合したカラムXを派生するという場合はこのaxis=1を使う。
"""

""" 
apply(lambda x: x * 2)のヒミツ

"""
#  ↑これ相当の関数はコレ↓


def my_func2(series):
    # series * 2 により、seriesに「ブロードキャスト」の演算がされて、各要素を2倍したSeriesが得られる。
    return series * 2


""" 
DataFrameにmy_func2をapplyメソッド適用
"""
p(foo.apply(my_func2))
# ↑ 縦方向のSeriesが、'a'、'b'、'c'、'fruit'の列ごとに得られるので、元のdfの全ての要素を2倍したように見えるDataFrameが得られる。


"""
★4★
   applyやmapも良いが、イテレーターが使えると手続き型ぽくて安心するので見てみます。
"""


# Series
msg("Seriesのイテレーター")
p("__iter___方式")
for val in foo['a'].__iter__():  # 実際は「in foo」 という記述で良い
    print(val)
msg("")
p("teritems方式")
for idx, val in foo['a'].iteritems():
    print(idx)
    print(val)


# DataFrame
msg("DataFrameのイテレーター")
p("__iter___方式")
for column_name in foo.__iter__():   # 実際は「in foo」 という記述で良い
    print(type(column_name))
    print(column_name)
msg("")
p("teritems方式")
for column_name, item in foo.iteritems():
    print(column_name)
    print(item)
    print(item[0])
msg("")
p("iterrows方式")
for idx, row in foo.iterrows():
    print(idx)
    print(row)
    print(row[0])

for row in foo.itertuples():
    print(row)


msg("dropによる列の削除。ここでは略しているが破壊的に削除するオプション(引数)もある")
p(foo.drop('b', axis=1))

msg("select_dtypesの活用。説明略。")
print(foo.select_dtypes(include=['int']).apply(lambda x: x.tolist()))
print(foo.select_dtypes(include=['int']).apply(lambda x: x))

print(foo.select_dtypes(include=['int']).values)

print(foo.select_dtypes(include=['int']).apply(lambda x: x.values[3] * 2))

print(foo.select_dtypes(include=['int']).apply(
    lambda x: ','.join(map(str, x.values))))

"""

★5★

GROUP BY 

"""


foogb = foo.groupby(['fruit'])

p(type(foogb))
#  ↓ 出力
# <class 'pandas.core.groupby.DataFrameGroupBy' >
#  ※ GroupByデータ型(?)

msg("")
p(foogb['a'])
#  ↓ 出力
# <pandas.core.groupby.SeriesGroupBy object at 0x109f69d68 >

msg("")
p(type(foogb['a']))
#  ↓ 出力
# <class 'pandas.core.groupby.SeriesGroupBy' >


msg("groupbyと基本的な使い方。まずは雰囲気をつかむ。ここではfruit列でグループ化")

p(foogb.groups)
msg("")

p(foogb.size())

msg("")

p(foogb.sum())
msg("")

p(foogb.mean())
msg("")

p(foogb.agg(np.mean))
msg("")

p(foogb.agg('min'))
msg("")

p(foogb.agg(min))

msg("groupby だんだん分からなくなってくる")

print(foogb.agg([np.mean, 'min', 'max', lambda x: max(x) - min(x)]))
msg("")

print(foogb.agg({'a': pd.np.mean}))
msg("")

print(foogb.apply(lambda d: (d.a * d.b).sum()))
msg("")

print(foogb.apply(lambda d: d.name))
msg("")

msg("groupby のイテレーション")
for name, group in foogb:
    print('--name--')
    print(name)
    print('--group--')
    print(group)
    print('--type(group)--')
    print(type(group))

msg("")

for name, group in foogb:
    print(name)
    gl = group['a'].tolist()
    print(gl)
    print(name + ':' + ','.join(map(str, gl)))

msg("")


msg("グループ化の条件を関数で与えることができる")
# DataFrame fooのカラムaの値が、奇数か偶数かを1、0で保持したSeriesを定義
odd_or_even_labels = foo['a'] % 2
p(foo.groupby(odd_or_even_labels).size())

msg("nuniqueなど、重複関連をgroupbyに適用。groupby だけでなく、DataFrameやSeriesにも使える。")
p(foogb['a'].nunique())
p(foogb['a'].unique())
p(foogb['a'].value_counts())

msg("SQLのGROUP BY 時のHAVING句相当の演算")
s = foogb['a'].nunique()
p(s >= 2)
p(s[s >= 2])
p(s.reset_index().query('a >= 2'))


msg("pandasのオブジェクト_groupbyその1")

p(foogb['fruit'])
p(type(foogb['fruit']))
p(foogb['a'])
foogb_tmp = foogb.apply(lambda x: x)
p(type(foogb_tmp))
p(foogb_tmp)

msg("pandasのオブジェクト_groupbyその2")
chktype = [
    foogb,
    foogb.apply(lambda x: x),
    foogb.agg(['max'])
]

for idx, i in enumerate(chktype):
    print('---------------------------------------------')
    print(idx)
    print(type(i))
    print(i.dtypes)
    print('------------')
    if check_member_simply('columns', i):
        print(i.columns)
    print('------------')
    if check_member_simply('index', i):
        print(i.index)
    print('------------')
    print(i)

print('------確認-----------')
foogb_tmp = foogb.agg(['max']).reset_index()
print(foogb_tmp)
print(foogb_tmp.columns)
print('------確認-----------')

foogb_tmp = foogb.agg(['max'])[('a', 'max')]
print(type(foogb_tmp))
print(foogb_tmp)

foogb_tmp = foogb.agg(['max'])[[('a', 'max'), ('c', 'max')]]
print(type(foogb_tmp))
print(foogb_tmp)


"""
★6★

様々なデータの読み込み。...といってもここではJSONに絞りましたが... 

"""

msg("JSON読み込み1: read_json")
data1 = '{"col1":{"r1":1,"r2":3},"col2":{"r1":[{"v":2}],"r2":[{"v":4}]},      "col3": { "r1":{"w":10},    "r2":{"w":20}   }  }'

df_s = pd.read_json(data1)

p(df_s)

msg("JSON読み込み2: json.loadsによるJSON→ Pythonのdict、これをDataFrameコンストラクタに与える。")
df_x = pd.DataFrame(json.loads(data1))
p(df_x)

data = {'json_col': ['{"name": "soudegesu", "age": "30", "address": {"area": "東京"}}',
                     '{"name": "hogehoge", "age": "10", "address": {"area": "北海道"}}']}

msg("JSON読み込み3: json_normalize。 ")
"""
json_normalizeは俗な理解の仕方だと...
ネストされたJSONを「prop1.prop2.prop3」のような列名にバラしてDataFrameに入れてくれる。
(配列が出てくるまではネストしたJSONを掘り下げてくれる)
"""
pd.set_option('display.max_columns', None)
df_json = json_normalize(data)
p(df_json)
p(df_json.head())

msg("以下、json_normalizeの実験")
data = {"col1": {"r1": 1, "r2": 3}, "col2": {"r1": [{"v": 2}], "r2": [
    {"v": 4}]},      "col3": {"r1": {"w": 10},    "r2": {"w": 20}}}

msg("")
p(json_normalize(data).head())
msg("")
p(json_normalize({"x": {"a": 1, "b": 2}, "y": {"c": 3, "d": 4}}))
msg("")
p(json_normalize({"x": {"a": {"c": 99}, "b": 2}, "y": {"a": 3, "b": 4}}))
msg("")
p(json_normalize({"x": {"a": {"c": 99}, "b": 2}, "y": {"a": 3, "b": 4}}).to_json(
    force_ascii=False, orient='records', lines=True))
msg("")
p(json_normalize({"a": [1, 2, 3], "b": 4}))
msg("")
p(json_normalize([{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"c": 5}]))


"""

ちなみに、JSONから入ったが、基本のDataFrame.read_csvや DataFrame.to_csvといった名前からお察しできる便利な関数も充実している。
ここではご紹介しないですが、ファイル入出力については、たいていのことはできるのでつど確認。

"""

"""
★7★

ここまで、見よう見まねの雰囲気で使って見た例のからくりを確認。
(「type」を確かめるなどして、型を意識すると理解が進むかも。最初に意識した方が良い話で、今更ですが...)
"""
chktype = [1, 'str', (lambda x: x), [1, 2], (1, 2), {
    '1': '2'}, foo, foo['a'], foo[['a', 'b']], foogb]

for i in chktype:
    print(type(i))

msg("pandasのオブジェクト")
chktype = [
    foo.apply(lambda x: x),
    foo.apply(lambda x: x, axis=1),
    foo['a'].apply(lambda x:x),
    foo.apply(lambda x: max(x)),
    foo.apply(lambda x: x['a'], axis=1),
    foo.apply(lambda x: [x['a'], x['b']], axis=1),
    foo.apply(lambda x: pd.Series([x['a'], x['b']]), axis=1),

]


for idx, i in enumerate(chktype):
    print('---------------------------------------------')
    print(idx)
    print(type(i))
    print(i.dtypes)
    print('------------')
    if check_member_simply('columns', i):
        print(i.columns)
    print('------------')
    print(i)


"""
★8★

reset_indexというのがあります。
これは、データを削除した後などにインデックスを振り直すのが主要な目的ですが、文字列ごにょごにょ系のETLとしてPandasを使う場合には、どちらかといえば、groupby後に得られたDataFrameGroupBy、SeriesGroupByを通常のDataFrameやSeriesに変換して、以降の処理を続けるのに使うという用途が多いような気がします。

"""

msg("reset_indexとは???")
p(foo)
msg("")
p(foo.index)
msg("")
p(foo.index.values)
msg("")
p(foo.reset_index())
msg("")
p(foo.reset_index().reset_index())
msg("")
p(foo.reset_index().reset_index()['level_0'])


"""
 Numpy由来の関数のベクトル化(便利だよ)
 俗に言うと、おまじないをつけると、普通の関数を配列を引数にとって結果の配列を戻り値に返せるように拡張できる。
"""
msg("関数のベクトル化")


def a_plus_b(a, b):
    # 単一の値をとる変数を引数にとる関数
    return a + b


msg("おまじない適用前")
p(a_plus_b(1, 3))
# ↓
# 4

# おまじない
v_a_plus_b = np.vectorize(a_plus_b)

msg("おまじない適用後")
# listを引数にとる
p(v_a_plus_b([1, 2, 3], [3, 6, 10]))


# その他、覚えておくと便利な用語や概念など ------------
# 集合関数、ピボット(pivot_table)、カテゴリ、replace...
# メソッドチェインで記述したい → pandas pipe() でググる
# 実はここではあまり触れなかったが、欠損値の扱い(当然便利な関数がそれなりに揃っている)。
# ソートしたり、列名を変えたりといったことも当然可能。
#
# ※私の広くない経験から言うと、エンプラ系のETLツールはその位置づけから、型や項目定義に厳格すぎる。
#  例えば、最初にデータを取り込んだ後に、一括で列名の変更を行うといったこともやりづらい。
# 今回テーマにしているような文字列中心のラフなデータに関するデータを扱うにあたっては、エンプラ系のETLツールが想定しているビジネスロジック・ルールの扱いよりも、メタな文字列処理を全ての列にざっくり適用する... というようなケースが多い....気がする。
# ...ので、その点では、もともとPandasがこれらを意識したものだからかどうかはわからないが、
#
# Pandasと言うよりは、Pythonならではだが、「リスト内表記 Python」でググっておきましょう。

サンプルコードの実行結果

----------------DataFrame取り込み----------------
   a  b  c   fruit
0  1  1  1   apple
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
----------------列方向の項目名----------------
Index(['a', 'b', 'c', 'fruit'], dtype='object')
----------------DataFrameの中身----------------
   a  b  c   fruit
0  1  1  1   apple
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
----------------DataFrameの値をlistのlistで出力----------------
[[1 1 1 'apple']
 [2 2 2 'orange']
 [3 3 3 'lemon']
 [4 4 4 'lemon']]
----------------DataFrameの列へのアクセス----------------
0    1
1    2
2    3
3    4
Name: a, dtype: int64
--------------------------------
0    1
1    2
2    3
3    4
Name: a, dtype: int64
--------------------------------
   a  c
0  1  1
1  2  2
2  3  3
3  4  4
--------------------------------
0    1
1    2
2    3
3    4
Name: a, dtype: int64
----------------applyによる宣言っぽい一括データ操作その1----------------
   a  b  c         fruit
0  2  2  2    appleapple
1  4  4  4  orangeorange
2  6  6  6    lemonlemon
3  8  8  8    lemonlemon
----------------Numpy由来のブロードキャスト----------------
   a  b  c         fruit
0  2  2  2    appleapple
1  4  4  4  orangeorange
2  6  6  6    lemonlemon
3  8  8  8    lemonlemon
----------------applyによる宣言っぽい一括データ操作その2----------------
a             4
b             4
c             4
fruit    orange
dtype: object
----------------loc[行の指定,列の指定]によるアクセス----------------
   a  b  c   fruit
0  1  1  1   apple
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
--------------------------------
0    1
1    2
2    3
3    4
Name: a, dtype: int64
--------------------------------
0    1
1    2
2    3
3    4
Name: a, dtype: int64
--------------------------------
2
--------------------------------
   a  b
1  2  2
2  3  3
3  4  4
--------------------------------
----------------filterによる列名の正規表現でのアクセス----------------
   c
0  1
1  2
2  3
3  4
----------------queryによる列の値が該当の条件にヒットする行の抽出。つまりselect----------------
   a  b  c   fruit
1  2  2  2  orange
----------------DataFrame['a'] > 1 は、列aの値が1より大きい行の、boolのSeries(行インデックス番号付き)返す----------------
0    False
1     True
2     True
3     True
Name: a, dtype: bool
----------------なので、foo[foo['a'] > 1]は、selectとして機能する----------------
   a  b  c   fruit
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
----------------条件に該当した行のみ、値を追加----------------
補足:Pythonは参照渡しでこれは普通なのかもしれないけど、PandasでETLっぽい視点でデータ操作しているとデータを潰してしまう。
ここでは、DataFrame fooは触りたくないので、foo.copy()で新しいfootmpに代入する。
footmp = foo.copy()
footmp['x'] = 1 
   a  b  c   fruit  x
0  1  1  1   apple  1
1  2  2  2  orange  1
2  3  3  3   lemon  1
3  4  4  4   lemon  1
'a'の値が2より大きい行だけ、'b'の値を10倍して、その値を'only_over2'の列に代入
   a  b  c   fruit  x  only_over2
0  1  1  1   apple  1         NaN
1  2  2  2  orange  1         NaN
2  3  3  3   lemon  1        30.0
3  4  4  4   lemon  1        40.0
----------------複数のDataFrameの結合(SQLのJOIN、....)----------------
SQLのJOINにあたるのはDataFrame.merge
   a  b_l  c_l fruit_l  b_r  c_r fruit_r   ind
0  1    1    1   apple    1    1   apple  both
1  2    2    2  orange    2    2  orange  both
2  3    3    3   lemon    3    3   lemon  both
3  4    4    4   lemon    4    4   lemon  both
----------------PandasはNumPyが親なので(だからだと思うが)、こんな演算もできる----------------
0    2
1    4
2    6
3    8
dtype: int64
--------------------------------
0     1
1     4
2     9
3    16
dtype: int64
----------------代入もできる。Seriesどうしを演算して新たなSeriesを生成してそれを、DataFrameの1列(Series)に代入する----------------
   a  b  c   fruit  a+b
0  1  1  1   apple    2
1  2  2  2  orange    4
2  3  3  3   lemon    6
3  4  4  4   lemon    8
----------------複数列にアクセスするとDataFrameが戻る----------------
   a  a+b
0  1    2
1  2    4
2  3    6
3  4    8
----------------applyのからくり: DataFrame.apply()----------------
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[1 2 3 4]
seriesのindex
RangeIndex(start=0, stop=4, step=1)
seriesをprint
0    1
1    2
2    3
3    4
Name: a, dtype: object
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[1 2 3 4]
seriesのindex
RangeIndex(start=0, stop=4, step=1)
seriesをprint
0    1
1    2
2    3
3    4
Name: b, dtype: object
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[1 2 3 4]
seriesのindex
RangeIndex(start=0, stop=4, step=1)
seriesをprint
0    1
1    2
2    3
3    4
Name: c, dtype: object
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
['apple' 'orange' 'lemon' 'lemon']
seriesのindex
RangeIndex(start=0, stop=4, step=1)
seriesをprint
0     apple
1    orange
2     lemon
3     lemon
Name: fruit, dtype: object
a        10000
b        10000
c        10000
fruit    10000
dtype: int64
----------------applyのからくり: DataFrame.apply(,axis=1)----------------
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[1 1 1 'apple']
seriesのindex
Index(['a', 'b', 'c', 'fruit'], dtype='object')
seriesをprint
a            1
b            1
c            1
fruit    apple
Name: 0, dtype: object
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[2 2 2 'orange']
seriesのindex
Index(['a', 'b', 'c', 'fruit'], dtype='object')
seriesをprint
a             2
b             2
c             2
fruit    orange
Name: 1, dtype: object
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[3 3 3 'lemon']
seriesのindex
Index(['a', 'b', 'c', 'fruit'], dtype='object')
seriesをprint
a            3
b            3
c            3
fruit    lemon
Name: 2, dtype: object
my_funcがapplyよりコールされました
seriesのtype
<class 'pandas.core.series.Series'>
seriesのサイズ
4
seriesのvalues
[4 4 4 'lemon']
seriesのindex
Index(['a', 'b', 'c', 'fruit'], dtype='object')
seriesをprint
a            4
b            4
c            4
fruit    lemon
Name: 3, dtype: object
0    10000
1    10000
2    10000
3    10000
dtype: int64
   a  b  c         fruit
0  2  2  2    appleapple
1  4  4  4  orangeorange
2  6  6  6    lemonlemon
3  8  8  8    lemonlemon
----------------Seriesのイテレーター----------------
__iter___方式
1
2
3
4
--------------------------------
teritems方式
0
1
1
2
2
3
3
4
----------------DataFrameのイテレーター----------------
__iter___方式
<class 'str'>
a
<class 'str'>
b
<class 'str'>
c
<class 'str'>
fruit
--------------------------------
teritems方式
a
0    1
1    2
2    3
3    4
Name: a, dtype: int64
1
b
0    1
1    2
2    3
3    4
Name: b, dtype: int64
1
c
0    1
1    2
2    3
3    4
Name: c, dtype: int64
1
fruit
0     apple
1    orange
2     lemon
3     lemon
Name: fruit, dtype: object
apple
--------------------------------
iterrows方式
0
a            1
b            1
c            1
fruit    apple
Name: 0, dtype: object
1
1
a             2
b             2
c             2
fruit    orange
Name: 1, dtype: object
2
2
a            3
b            3
c            3
fruit    lemon
Name: 2, dtype: object
3
3
a            4
b            4
c            4
fruit    lemon
Name: 3, dtype: object
4
Pandas(Index=0, a=1, b=1, c=1, fruit='apple')
Pandas(Index=1, a=2, b=2, c=2, fruit='orange')
Pandas(Index=2, a=3, b=3, c=3, fruit='lemon')
Pandas(Index=3, a=4, b=4, c=4, fruit='lemon')
----------------dropによる列の削除。ここでは略しているが破壊的に削除するオプション(引数)もある----------------
   a  c   fruit
0  1  1   apple
1  2  2  orange
2  3  3   lemon
3  4  4   lemon
----------------select_dtypesの活用。説明略。----------------
   a  b  c
0  1  1  1
1  2  2  2
2  3  3  3
3  4  4  4
   a  b  c
0  1  1  1
1  2  2  2
2  3  3  3
3  4  4  4
[[1 1 1]
 [2 2 2]
 [3 3 3]
 [4 4 4]]
a    8
b    8
c    8
dtype: int64
a    1,2,3,4
b    1,2,3,4
c    1,2,3,4
dtype: object
<class 'pandas.core.groupby.DataFrameGroupBy'>
--------------------------------
<pandas.core.groupby.SeriesGroupBy object at 0x10dbde400>
--------------------------------
<class 'pandas.core.groupby.SeriesGroupBy'>
----------------groupbyと基本的な使い方。まずは雰囲気をつかむ。ここではfruit列でグループ化----------------
{'apple': Int64Index([0], dtype='int64'), 'lemon': Int64Index([2, 3], dtype='int64'), 'orange': Int64Index([1], dtype='int64')}
--------------------------------
fruit
apple     1
lemon     2
orange    1
dtype: int64
--------------------------------
        a  b  c
fruit          
apple   1  1  1
lemon   7  7  7
orange  2  2  2
--------------------------------
          a    b    c
fruit                
apple   1.0  1.0  1.0
lemon   3.5  3.5  3.5
orange  2.0  2.0  2.0
--------------------------------
          a    b    c
fruit                
apple   1.0  1.0  1.0
lemon   3.5  3.5  3.5
orange  2.0  2.0  2.0
--------------------------------
        a  b  c
fruit          
apple   1  1  1
lemon   3  3  3
orange  2  2  2
--------------------------------
        a  b  c
fruit          
apple   1  1  1
lemon   3  3  3
orange  2  2  2
----------------groupby だんだん分からなくなってくる----------------
          a                     b                     c                 
       mean min max <lambda> mean min max <lambda> mean min max <lambda>
fruit                                                                   
apple   1.0   1   1        0  1.0   1   1        0  1.0   1   1        0
lemon   3.5   3   4        1  3.5   3   4        1  3.5   3   4        1
orange  2.0   2   2        0  2.0   2   2        0  2.0   2   2        0
--------------------------------
          a
fruit      
apple   1.0
lemon   3.5
orange  2.0
--------------------------------
fruit
apple      1
lemon     25
orange     4
dtype: int64
--------------------------------
fruit
apple      apple
lemon      lemon
orange    orange
dtype: object
--------------------------------
----------------groupby のイテレーション----------------
--name--
apple
--group--
   a  b  c  fruit
0  1  1  1  apple
--type(group)--
<class 'pandas.core.frame.DataFrame'>
--name--
lemon
--group--
   a  b  c  fruit
2  3  3  3  lemon
3  4  4  4  lemon
--type(group)--
<class 'pandas.core.frame.DataFrame'>
--name--
orange
--group--
   a  b  c   fruit
1  2  2  2  orange
--type(group)--
<class 'pandas.core.frame.DataFrame'>
--------------------------------
apple
[1]
apple:1
lemon
[3, 4]
lemon:3,4
orange
[2]
orange:2
--------------------------------
----------------グループ化の条件を関数で与えることができる----------------
a
0    2
1    2
dtype: int64
----------------nuniqueなど、重複関連をgroupbyに適用。groupby だけでなく、DataFrameやSeriesにも使える。----------------
fruit
apple     1
lemon     2
orange    1
Name: a, dtype: int64
fruit
apple        [1]
lemon     [3, 4]
orange       [2]
Name: a, dtype: object
fruit   a
apple   1    1
lemon   3    1
        4    1
orange  2    1
Name: a, dtype: int64
----------------SQLのGROUP BY 時のHAVING句相当の演算----------------
fruit
apple     False
lemon      True
orange    False
Name: a, dtype: bool
fruit
lemon    2
Name: a, dtype: int64
   fruit  a
1  lemon  2
----------------pandasのオブジェクト_groupbyその1----------------
<pandas.core.groupby.SeriesGroupBy object at 0x10dc2c0b8>
<class 'pandas.core.groupby.SeriesGroupBy'>
<pandas.core.groupby.SeriesGroupBy object at 0x10dc2c0b8>
<class 'pandas.core.frame.DataFrame'>
   a  b  c
0  1  1  1
1  2  2  2
2  3  3  3
3  4  4  4
----------------pandasのオブジェクト_groupbyその2----------------
---------------------------------------------
0
<class 'pandas.core.groupby.DataFrameGroupBy'>
            a      b      c
fruit                      
apple   int64  int64  int64
lemon   int64  int64  int64
orange  int64  int64  int64
------------
------------
------------
<pandas.core.groupby.DataFrameGroupBy object at 0x10db6ac50>
---------------------------------------------
1
<class 'pandas.core.frame.DataFrame'>
a    int64
b    int64
c    int64
dtype: object
------------
Index(['a', 'b', 'c'], dtype='object')
------------
RangeIndex(start=0, stop=4, step=1)
------------
   a  b  c
0  1  1  1
1  2  2  2
2  3  3  3
3  4  4  4
---------------------------------------------
2
<class 'pandas.core.frame.DataFrame'>
a  max    int64
b  max    int64
c  max    int64
dtype: object
------------
MultiIndex(levels=[['a', 'b', 'c'], ['max']],
           labels=[[0, 1, 2], [0, 0, 0]])
------------
Index(['apple', 'lemon', 'orange'], dtype='object', name='fruit')
------------
         a   b   c
       max max max
fruit             
apple    1   1   1
lemon    4   4   4
orange   2   2   2
------確認-----------
    fruit   a   b   c
          max max max
0   apple   1   1   1
1   lemon   4   4   4
2  orange   2   2   2
MultiIndex(levels=[['a', 'b', 'c', 'fruit'], ['max', '']],
           labels=[[3, 0, 1, 2], [1, 0, 0, 0]])
------確認-----------
<class 'pandas.core.series.Series'>
fruit
apple     1
lemon     4
orange    2
Name: (a, max), dtype: int64
<class 'pandas.core.frame.DataFrame'>
         a   c
       max max
fruit         
apple    1   1
lemon    4   4
orange   2   2
----------------JSON読み込み1: read_json----------------
    col1        col2       col3
r1     1  [{'v': 2}]  {'w': 10}
r2     3  [{'v': 4}]  {'w': 20}
----------------JSON読み込み2: json.loadsによるJSON→ Pythonのdict、これをDataFrameコンストラクタに与える。----------------
    col1        col2       col3
r1     1  [{'v': 2}]  {'w': 10}
r2     3  [{'v': 4}]  {'w': 20}
----------------JSON読み込み3: json_normalize。 ----------------
                                            json_col
0  [{"name": "soudegesu", "age": "30", "address":...
                                            json_col
0  [{"name": "soudegesu", "age": "30", "address":...
----------------以下、json_normalizeの実験----------------
--------------------------------
   col1.r1  col1.r2     col2.r1     col2.r2  col3.r1.w  col3.r2.w
0        1        3  [{'v': 2}]  [{'v': 4}]         10         20
--------------------------------
   x.a  x.b  y.c  y.d
0    1    2    3    4
--------------------------------
   x.a.c  x.b  y.a  y.b
0     99    2    3    4
--------------------------------
{"x.a.c":99,"x.b":2,"y.a":3,"y.b":4}
--------------------------------
           a  b
0  [1, 2, 3]  4
--------------------------------
     a    b    c
0  1.0  2.0  NaN
1  3.0  4.0  NaN
2  NaN  NaN  5.0
<class 'int'>
<class 'str'>
<class 'function'>
<class 'list'>
<class 'tuple'>
<class 'dict'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.groupby.DataFrameGroupBy'>
----------------pandasのオブジェクト----------------
---------------------------------------------
0
<class 'pandas.core.frame.DataFrame'>
a         int64
b         int64
c         int64
fruit    object
dtype: object
------------
Index(['a', 'b', 'c', 'fruit'], dtype='object')
------------
   a  b  c   fruit
0  1  1  1   apple
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
---------------------------------------------
1
<class 'pandas.core.frame.DataFrame'>
a         int64
b         int64
c         int64
fruit    object
dtype: object
------------
Index(['a', 'b', 'c', 'fruit'], dtype='object')
------------
   a  b  c   fruit
0  1  1  1   apple
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
---------------------------------------------
2
<class 'pandas.core.series.Series'>
int64
------------
------------
0    1
1    2
2    3
3    4
Name: a, dtype: int64
---------------------------------------------
3
<class 'pandas.core.series.Series'>
object
------------
------------
a             4
b             4
c             4
fruit    orange
dtype: object
---------------------------------------------
4
<class 'pandas.core.series.Series'>
int64
------------
------------
0    1
1    2
2    3
3    4
dtype: int64
---------------------------------------------
5
<class 'pandas.core.series.Series'>
object
------------
------------
0    [1, 1]
1    [2, 2]
2    [3, 3]
3    [4, 4]
dtype: object
---------------------------------------------
6
<class 'pandas.core.frame.DataFrame'>
0    int64
1    int64
dtype: object
------------
RangeIndex(start=0, stop=2, step=1)
------------
   0  1
0  1  1
1  2  2
2  3  3
3  4  4
----------------reset_indexとは???----------------
   a  b  c   fruit
0  1  1  1   apple
1  2  2  2  orange
2  3  3  3   lemon
3  4  4  4   lemon
--------------------------------
RangeIndex(start=0, stop=4, step=1)
--------------------------------
[0 1 2 3]
--------------------------------
   index  a  b  c   fruit
0      0  1  1  1   apple
1      1  2  2  2  orange
2      2  3  3  3   lemon
3      3  4  4  4   lemon
--------------------------------
   level_0  index  a  b  c   fruit
0        0      0  1  1  1   apple
1        1      1  2  2  2  orange
2        2      2  3  3  3   lemon
3        3      3  4  4  4   lemon
--------------------------------
0    0
1    1
2    2
3    3
Name: level_0, dtype: int64
----------------関数のベクトル化----------------
----------------おまじない適用前----------------
4
----------------おまじない適用後----------------
[ 4  8 13]

おしまいです。

*1:ググった範囲では、体系的にまとめられているもの、ガチ勢か、MLなど「その先」の高度な内容が中心でした。