アメリエフの技術ブログ

Amelieff Staff Blog

Rで重複しないrownamesを作る

統計解析ソフトRのデータ解析では、データフレームの行(row)に重複した名前をつけることができません。

対処法はケースバイケースで、重複する名前を削除して1個(一行)だけ残す方法がよく紹介されますが、たまたま同じ名前の行を持つデータを使いたいこともありますね。

本日は削除はせず、重複した名前に、機械的に異なる名前を割り振って重複しないようにする方法を紹介します。
例えば、"Sato" "Sato" ⇒ "Sato" "Sato.1" のようにします。

Rで最初から使えるbaseパッケージの make.names()を使います。

f:id:Fuku-I:20211004133535p:plain:h250
Satoが重複するので、SatoとSato.1に改名

😀 行名やリストのnameによく使うと思いますが、この方法は文字列ベクトルに対する処理なので他の使い道もあるかもしれません。

やってみよう

見本データ "namae_number.csv" です
1列目に名前、2列目に何かの数値があります。
SatoやSuzukiが複数行ありますが、すべて別人のデータであるとします。日本人に多い苗字ですから被ることもありますね*1

NAMAE,NUMBER
Sato,12
Suzuki,4
Takahashi,5
Sato,13
Tanaka,16
Ito,8
Watanabe,9
Suzuki,14
Yamamoyo,12
Nakamura,15
Kobayashi,7
Suzuki,10
Kato,6

読み込み

Rに見本データを読み込みます

> data <- read.table("namae_number.csv", sep=",", header=TRUE)
> head(data)
      NAMAE NUMBER
1      Sato     12
2    Suzuki      4
3 Takahashi      5
4      Sato     13
5    Tanaka     16
6       Ito      8

ここで、NAMAE列を行名(rownames)に変換しようとすると、重複した 'row.names' は許されませんとエラーが発生します。

> rownames(data) <- data$NAMAE
 `.rowNamesDF<-`(x, value = value) でエラー: 
   重複した 'row.names' は許されません 
 追加情報:  警告メッセージ: 
non-unique values when setting 'row.names': ‘Sato’, ‘Suzuki’ 

新しいNamae Oshiete

あらためて NAMAE列にある文字列の重複を確認してみます

> namae <- data$NAMAE
> namae
 [1] "Sato"      "Suzuki"    "Takahashi" "Sato"      "Tanaka"    "Ito"      
 [7] "Watanabe"  "Suzuki"    "Yamamoyo"  "Nakamura"  "Kobayashi" "Suzuki"   
[13] "Kato"     
> table(namae)  # 文字列を集計
namae
      Ito      Kato Kobayashi  Nakamura      Sato    Suzuki Takahashi    Tanaka 
        1         1         1         1         2         3         1         1 
 Watanabe  Yamamoyo 
        1         1


ここから、make.names() を使って新しい名前を作成します。
オプションで unique=TRUE をつけると、前述のように重複した文字列が 固有のものに変換されます。

> new_name <- make.names(namae, unique=TRUE)   #変換!  

## 変換後の確認
> new_name
 [1] "Sato"      "Suzuki"    "Takahashi" "Sato.1"    "Tanaka"    "Ito"      
 [7] "Watanabe"  "Suzuki.1"  "Yamamoyo"  "Nakamura"  "Kobayashi" "Suzuki.2" 
[13] "Kato"     

一人目のSatoさんは "Sato" のまま、二人目から "Sato.1" "Sato.2" になりました。
※ 最初の人は "Sato.0" と考えるとわかりやすいでしょうか。0 から数え始めるのは、ちょっとRらしくない挙動ですね


新しい名前を行名に変換しようとすると、今度は成功します。

> rownames(data) <- new_name
> head(data)
              NAMAE NUMBER
Sato           Sato     12
Suzuki       Suzuki      4
Takahashi Takahashi      5
Sato.1         Sato     13
Tanaka       Tanaka     16
Ito             Ito      8

これで解決です。
改名したことを覚えていれば、元のNAMAE 列は消してもいいでしょう。

make.names()の変換法則

今回は重複解消のために使いましたが、 make.names()は 文字列を syntactically valid nameにするのが主な機能である関数です。
その変換法則は以下の通りです。

  • システム的に正しい名前に変更される
    • nameに使えない文字(+, -, / など)はドット.に変換
    • 数字から始まる名前は "X<数字>"に変換
      ※元から "X<数字>" という文字列がある場合は注意して!

unique=TRUE の場合さらに  

  • 重複する場合は名前の後に ".1" ".2" ... が付与される

syntactically valid nameとは、Rの変数名に用いることができるname と同様の法則だと筆者は理解しました。
詳細には help(make.names)を実行してヘルプをご覧ください。