Python(+Pandas), R, Julia(+DataFrames) の比較 (テーブル内容のサマリ、列単位のアクセス)

2017/1/3 追記: [DataFrames.jl はAPIの互換性も一部失われる大規模な改修を行った v.0.9 のリリースを2017年の2月に予定しているそうです。](https://discourse.julialang.org/t/announcement-dataframes-0-9-0-planned-for-february/266)この記事は v.0.6.10に基づいて書かれているので注意してください。時間が出来てなおかつ気が向いたら(おい)それに合わせて書き直すかもしれません。
ただ、ここのところあまり Julia を触る時間もなくて……。

---

[前回](http://qiita.com/ngr_t/items/57867de223f741f735b8)の記事の続きです。前回はほとんどデータの中身も見ないまま終わってしまいましたが、気を取り直して今日は簡単な内容確認とか列の取り出し方とかについて書いていきます。

以下のコードの実行例では REPL には前回の内容で読み込んだデータが乗っているという前提です。

# テーブルのサマリ

今回紹介しているどのライブラリ・言語にも「簡単なデータ構造の要約を返す」という関数が用意されているので、使ってみましょう。

Pandas:

```Python
>>> unique_artists.describe()
                 artist_id                           artist_mbid  \
count                 3888                                  3489   
unique                3888                                  3488   
top     AROBQ0B1187FB404F6  2c0be2ba-fa63-430f-afca-7499852158f6   
freq                     1                                     2   

                  track_id            artist_name  
count                 3888                   3888  
unique                3888                   3875  
top     TRAPGXH128F426D205  Bill & Gloria Gaither  
freq                     1                      3  
>>> artist_term.describe()
                 artist_id   term
count                97493  97493
unique                3885   3502
top     ARLYKKT11F4C83B350   rock
freq                    91   2346
```

DataFrames.jl:

```jl
julia> DataFrames.describe(unique_artists)
artist_id
Length  3888
Type    UTF8String
NAs     0
NA%     0.0%
Unique  3888

artist_mbid
Length  3888
Type    UTF8String
NAs     399
NA%     10.26%
Unique  3489

track_id
Length  3888
Type    UTF8String
NAs     0
NA%     0.0%
Unique  3888

artist_name
Length  3888
Type    UTF8String
NAs     0
NA%     0.0%
Unique  3875

Julia> DataFrames.describe(artist_term)
artist_id
Length  97493
Type    Nullable{UTF8String}
NAs     0
NA%     0.0%
Unique  3885

term
Length  97493
Type    Nullable{UTF8String}
NAs     0
NA%     0.0%
Unique  3502
```

R:

```R
> summary(unique_artists)
              artist_id                                  artist_mbid  
 AR009211187B989185:   1                                       : 399  
 AR00A6H1187FB5402A:   1   2c0be2ba-fa63-430f-afca-7499852158f6:   2  
 AR00LNI1187FB444A5:   1   001a1d90-56dc-4992-bd7d-970ecd7105d0:   1  
 AR00MBZ1187B9B5DB1:   1   001e4a87-fc46-4d66-ba37-e9714e0fe58a:   1  
 AR01IP11187B9AF5D2:   1   0039c7ae-e1a7-4a7d-9b49-0cbc716821a6:   1  
 AR01VU31187B997DA0:   1   0039cd9c-ea95-4553-86c4-4f729984cc2f:   1  
 (Other)           :3882   (Other)                             :3483  
               track_id                          artist_name  
 TRAAAFD128F92F423A:   1   Bill & Gloria Gaither       :   3  
 TRAAARJ128F9320760:   1   2-Gether feat. Sarinah      :   2  
 TRAABCL128F4286650:   1   Abbott & Chambers           :   2  
 TRAABJL12903CDCF1A:   1   Art Ensemble Of Chicago     :   2  
 TRAABRB128F9306DD5:   1   Billy Price_ Fred Chapellier:   2  
 TRAABVM128F92CA9DC:   1   Eddie Palmieri              :   2  
 (Other)           :3882   (Other)                     :3875  
> summary(artist_term)
  artist_id             term          
 Length:97493       Length:97493      
 Class :character   Class :character  
 Mode  :character   Mode  :character  
```

Pandas も DataFrames.jl もユニークな要素や最頻値を出してくれたりするのは良いですね。R の方は上位何件かを見れるのですが、データの性質によっては意味が薄い感もあり。 DataFrames.jl は NA の比率を出してくれるのも良いです。
数値データのほうが表示結果が面白いというか有り難みが感じられるので、ちょっと例を出してみます。

```Python
>>> pandas.DataFrame({"a":range(1,11)}).describe()
              a
count  10.00000
mean    5.50000
std     3.02765
min     1.00000
25%     3.25000
50%     5.50000
75%     7.75000
max    10.00000
```

```jl
julia> DataFrames.describe(DataFrames.DataFrame(a=1:10))
a
Min      1.0
1st Qu.  3.25
Median   5.5
Mean     5.5
3rd Qu.  7.75
Max      10.0
NAs      0
NA%      0.0%


```

```R
> summary(seq(10))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00    3.25    5.50    5.50    7.75   10.00 
```


# 列単位のアクセス

ではとりあえず列の取り出し方を。unique_artists の 1 始まりで数えて 4 列目 "artist_name" の列を取り出すということをします。

Pandas は次の2つが同じ結果を返します。

```Python
>>> unique_artists.artist_name
0                                       Carroll Thompson
1                                            The Meatmen
2                                           Bruce BecVar
3                                         Memphis Minnie
4                                       Call To Preserve
5                                             Grand Funk
6                                                  3OH!3
7                                                   Ross
8                                         Annie Philippe
9                                            Tito Puente
10                                      Hi-Fi Companions
11                                               Delta 9
12                                    Bersuit Vergarabat
13                                                    BT
14                                         John Williams
15                                        Britney Spears
16                                                Sylver
17                       The Bug Featuring Ricky Ranking
18                                          Alexisonfire
19                                            Yellowcard
20                   The Disposable Heroes Of Hiphoprisy
21                                         Dead Kennedys
22                         Wilks featuring pee wee ellis
23                                       Mike Bloomfield
24                                            Roy Drusky
25                                            Alex Ubago
26                                               D'Molls
27                                               Kassav'
28                                     Ol' Dirty Bastard
29                                          Fudge Tunnel
                              ...                       
3858                      Smokin' Joe Kubek & Bnois King
3859                                       William Orbit
3860                                         Warner mack
3861                                               Biddu
3862                                               Ideal
3863                                     Reginaldo Bessa
3864                                             Rainbow
3865                                       Babylon Disco
3866                                    Mawatari Matsuko
3867                                Widespread Bloodshed
3868                                  Top Of The Poppers
3869                                   Elliot Goldenthal
3870                                    Alexander Zonjic
3871                                          Jim Reeves
3872                                         Mister Ries
3873                                           Martriden
3874                                        Günter Noris
3875                                        Rise Against
3876                                       Henry Mancini
3877                               Jason Michael Carroll
3878                                   Los Ninos De Sara
3879                               Juelz Santana / Bezel
3880                                Vaggelis Perpiniadis
3881                                               Davol
3882                                        John Stevens
3883                                               Taboo
3884                                 Mt. Wilson Repeater
3885    Chapta_ Stage McCloud_ SupaStarr & Tony Manshino
3886                                              Rooney
3887                                       Delroy Wilson
Name: artist_name, dtype: object
>>> unique_artists["artist_name"]
(略)
```

DataFrames.jl はこう書くようです。

```jl
unique_artists[:artist_name]
```

R だと次のようになります。（※ デフォルト状態の R の REPL で安易にこれを叩くと列の全部のデータを表示しにかかるので注意してください。）

```R
unique_artists$artist_name
unique_artists[, "artist_name"]
```

えーと、R だけ\[\]の中に","が入ってるのが気になった方はいますか。いませんか。いるということにします。

「列を取り出す」と安易に書きましたが、上記は全て「データフレームの列を構成するデータ構造を取り出す」際の書き方です。「データフレームの列を構成するデータ構造」というのは Pandas なら Series、R なら vector、DataFrames.jl なら DataArray です。
これらは一列のデータフレームとは異なるので注意が要ります。適当にしていると、関数の返り値が data.frame だと思っていたら vector になっててあばばばーってなったりするので注意しましょう。

「一列のデータフレームを取り出す」場合はそれぞれ以下です。

Pandas:

```Python
unique_artists[["artist_name"]]
```

DataFrames.jl:

```jl
unique_artists[[:artist_name]]
```

R:

```R
unique_artists["artist_name"]
unique_artists[, "artist_name", drop=F]
```

というか基本的には複数列を取り出す操作と一緒なのですが、R の場合は drop がデフォルトで TRUE になっているのがトラップだったりします。

複数列取り出す場合はそのまま取得する列名として渡している配列やらリストの要素を複数にすれば良くて、以下のようになります。


Pandas:

```Python
unique_artists[["artist_id", "artist_name"]]
```

DataFrames.jl:

```jl
unique_artists[[:artist_id, :artist_name]]
```

R:

```R
unique_artists[c("artist_id", "artist_name")]
unique_artists[, c("artist_id", "artist_name"), drop=F]
```

特定の列を除く場合は以下です。

Pandas:

```Python
>>> unique_artists.drop(["track_id", "artist_mbid"], axis=1)
               artist_id                                       artist_name
0     AR009211187B989185                                  Carroll Thompson
1     AR00A6H1187FB5402A                                       The Meatmen
2     AR00LNI1187FB444A5                                      Bruce BecVar
3     AR00MBZ1187B9B5DB1                                    Memphis Minnie
4     AR01IP11187B9AF5D2                                  Call To Preserve
5     AR01VU31187B997DA0                                        Grand Funk
6     AR01W2D1187FB5912F                                             3OH!3
7     AR022JO1187B99587B                                              Ross
8     AR02DB61187B9A0B5E                                    Annie Philippe
9     AR02IU11187FB513F2                                       Tito Puente
10    AR02KZG1187FB3B3B4                                  Hi-Fi Companions
11    AR02PUA1187FB52574                                           Delta 9
12    AR02YGA1187B9B8AC4                                Bersuit Vergarabat
13    AR035N21187FB3938E                                                BT
14    AR039B11187B9B30D0                                     John Williams
15    AR03BDP1187FB5B324                                    Britney Spears
16    AR03GWG1187B9B6C85                                            Sylver
17    AR040M31187B98CA41                   The Bug Featuring Ricky Ranking
18    AR040QX1187FB4CFE1                                      Alexisonfire
19    AR048JZ1187B9AEB85                                        Yellowcard
20    AR04KTB1187B99B6B7               The Disposable Heroes Of Hiphoprisy
21    AR050VJ1187B9B13A7                                     Dead Kennedys
22    AR051KA1187B98B2FF                     Wilks featuring pee wee ellis
23    AR0569B1187B9A4036                                   Mike Bloomfield
24    AR059HI1187B9A14D7                                        Roy Drusky
25    AR05IU31187B9B9A1A                                        Alex Ubago
26    AR05KQA1187B9963B3                                           D'Molls
27    AR05NQH1187B98E875                                           Kassav'
28    AR05OJD1187B9B99A6                                 Ol' Dirty Bastard
29    AR05VW21187FB407B4                                      Fudge Tunnel
...                  ...                                               ...
3858  ARZRVFK11F4C83DF46                    Smokin' Joe Kubek & Bnois King
3859  ARZS5VW1187FB567E8                                     William Orbit
3860  ARZSFIJ1187B98B8BC                                       Warner mack
3861  ARZSTXE1187B99DD77                                             Biddu
3862  ARZTGQE1187FB461D9                                             Ideal
3863  ARZTSMH122988F522A                                   Reginaldo Bessa
3864  ARZTSYB1187FB54987                                           Rainbow
3865  ARZU9NI1187B9AEE08                                     Babylon Disco
3866  ARZUVRN1269FB3759D                                  Mawatari Matsuko
3867  ARZUZYU12086C17110                              Widespread Bloodshed
3868  ARZVBBM1241B9CB622                                Top Of The Poppers
3869  ARZVEF91187B9AC770                                 Elliot Goldenthal
3870  ARZVJ641187FB36FA4                                  Alexander Zonjic
3871  ARZVTZN1187FB579D4                                        Jim Reeves
3872  ARZVZRN1241B9C4B14                                       Mister Ries
3873  ARZWAHK119B8668273                                         Martriden
3874  ARZWBH21187B99ACE1                                      Günter Noris
3875  ARZWK2R1187B98F09F                                      Rise Against
3876  ARZWPWP1241B9CA793                                     Henry Mancini
3877  ARZXLZD11E2835CEA7                             Jason Michael Carroll
3878  ARZXMYV1187FB5B99B                                 Los Ninos De Sara
3879  ARZXTEY1187B9997A7                             Juelz Santana / Bezel
3880  ARZXTUO12509411DAE                              Vaggelis Perpiniadis
3881  ARZXVY01187B9972EC                                             Davol
3882  ARZXYRK1187B99E432                                      John Stevens
3883  ARZY28S1187FB40BAB                                             Taboo
3884  ARZYFWI11F4C84225B                               Mt. Wilson Repeater
3885  ARZYP6O1187B9892E7  Chapta_ Stage McCloud_ SupaStarr & Tony Manshino
3886  ARZYPLF1187FB45B9B                                            Rooney
3887  ARZZRK91187B9A5CA5                                     Delroy Wilson

[3888 rows x 2 columns]
```

Pandas の drop は inplace=True を指定しなかった場合には非破壊的で、drop メソッドを呼び出したあとも DataFrame から列が削除されるということはありません。
このように非破壊的に特定の列を除くという操作を行う関数がどうも DataFrames.jl や R には無いようです。（下記のような関数を自分で定義すればいいといえばそうですが）

DataFrames.jl では内包表記なりなんなりで残したい列だけ true になる bool 配列を作ってしまうのが良いようです。
cf: http://stackoverflow.com/questions/24665439/julia-dataframe-remove-column-by-name

```jl
julia> unique_artists[~[(x in [:artist_mbid, :track_id]) for x in names(unique_artists)]]
3888x2 DataFrames.DataFrame
| Row  | artist_id            |
|------|----------------------|
| 1    | "AR009211187B989185" |
| 2    | "AR00A6H1187FB5402A" |
| 3    | "AR00LNI1187FB444A5" |
| 4    | "AR00MBZ1187B9B5DB1" |
| 5    | "AR01IP11187B9AF5D2" |
| 6    | "AR01VU31187B997DA0" |
| 7    | "AR01W2D1187FB5912F" |
| 8    | "AR022JO1187B99587B" |
⋮
| 3880 | "ARZXTEY1187B9997A7" |
| 3881 | "ARZXTUO12509411DAE" |
| 3882 | "ARZXVY01187B9972EC" |
| 3883 | "ARZXYRK1187B99E432" |
| 3884 | "ARZY28S1187FB40BAB" |
| 3885 | "ARZYFWI11F4C84225B" |
| 3886 | "ARZYP6O1187B9892E7" |
| 3887 | "ARZYPLF1187FB45B9B" |
| 3888 | "ARZZRK91187B9A5CA5" |

| Row  | artist_name                                        |
|------|----------------------------------------------------|
| 1    | "Carroll Thompson"                                 |
| 2    | "The Meatmen"                                      |
| 3    | "Bruce BecVar"                                     |
| 4    | "Memphis Minnie"                                   |
| 5    | "Call To Preserve"                                 |
| 6    | "Grand Funk"                                       |
| 7    | "3OH!3"                                            |
| 8    | "Ross"                                             |
⋮
| 3880 | "Juelz Santana / Bezel"                            |
| 3881 | "Vaggelis Perpiniadis"                             |
| 3882 | "Davol"                                            |
| 3883 | "John Stevens"                                     |
| 3884 | "Taboo"                                            |
| 3885 | "Mt. Wilson Repeater"                              |
| 3886 | "Chapta_ Stage McCloud_ SupaStarr & Tony Manshino" |
| 3887 | "Rooney"                                           |
| 3888 | "Delroy Wilson"                                    |


julia> # destructive

julia> # DataFrames.delete!(unique_artists, [:artist_mbid, :track_id])
```

R の場合も発想は一緒です。

```R
> unique_artists[!names(unique_artists) %in% c("artist_mbid", "track_id")]
(略)
> # destructive
> # unique_artists$artist_mbid <- NULL
> # unique_artists$track_id <- NULL
```


スライスとか言って列のアクセス方法までしか書いてませんが、今回はこの辺りで。

環境：

- OS: Manjaro Linux
- Python: 3.5.0
    - pandas: 0.17.1
- R: 3.2.2
    - RSQLite 1.0.0
- Julia: 0.4.0
    - DataFrames: 0.6.10
    - DataStreams: 0.0.3
    - SQLite: 0.3.0