実データ分析(1)_summarize.R

要約

・複数列の計算はmapply(fnc, x, y, z, ...)だがパイプ処理と相性が悪い
・pivot_longer() %>% group_by() %>% summarize()

実データの分析例

 ここからは、某データベースからDLしてきた特定分野の企業群に関するデータを使って模擬的に分析してみたい。このデータには、企業名・設立年などの簡易的な企業情報と一緒に、特許の件数が入力されている。

 詳しいことはひとまず置いておくとして、特許とは、特定の技術を独占的な権利として確保したものと考えてよい。
 そのために特許庁(東京特許許可局ではない)へ申請=出願を行って、公報として発行=公開されたのち、審査を無事通過したものだけが登録されて、晴れて権利となる。権利になったらそれで終わりではなく、お金を払うことで権利が維持でき、払わなくなったら権利が失効する。

 このデータでいう特許の件数には、(保有・出願)×(国内・海外)の4種類がある。
 保有・出願の定義は不明だが、おそらくは保有=権利として生きているかまだ審査中の特許であり、出願は未公開の特許だと思われる。もしくは保有が生存中の登録特許で出願・審査中のものは出願に入れてあるのかもしれない。
 まあ定義が分からないから全部足すんだけどね!

 というわけで、イメージ的には下のような感じになっていて、ここに会社ごとの件数総数である特許件数を足したい。

NO  特許保有_国内  特許保有_海外  特許出願_国内  特許出願_海外 設立年
 1             4           NA             2            NA   2007
 2            NA           NA             3            NA   2012

分析の最初のステップ

 乱暴な一般論としてだが、何かしらの技術開発を行っている企業が事業を行うにあたっては、後発に対する牽制・参入障壁として特許が有効な手段の一つになる。特許権そのものの質(権利としての強弱)もあるのだけれど、質は一律評価しづらい指標なので、代わりに件数を用いることが多い。
 従って、事業性を評価する際の一指標として、特許を持っているかどうか、持っているとしたらどの程度かというのを見たくなる。
 あくまでも一指標であって、特許がないからといって事業性がないとは言えない。逆に、事業として成立していれば継続的に特許を出願することができるようにもなるし、特許に金をかけられる企業は企業間交渉にも強くなれる。
 まあ、因果関係はともかく、何らかの関連がありそうな指標の一つとして特許の数があるということだ。

集計の方針

 そういうわけで、ここではまず企業ごとの特許の件数を集計してみたい。
 Excelだったら右に一列足して、SUM関数で行ごとの合計を作って全行にコピーすることが多いだろう。
 素直にこれをやるのがmapply()になる。
 もう1つはdplyr::pivot_longer()で、これは前に扱ったことがある。
 前回は、カウント用に1を格納した列を作っておいて、pivot_wider()する際になんやかんやしたのだったが、今回は値をそのまま使う。

 こうすると、同じNOが格納された行が4行作られて、元々列として並んでいた種類ごとの件数が対応する各行に移動されて、long形式の表ができることになる。行の数は4倍になり、列の数は3つ減る。
 じゃあ、この1列に押し込められた件数を合計したら……全部の件数が合計されるだけなので、ここでdplyr::group_by()を使用する。
 group_by()は列を指定すると、その列の値を使ってグルーピングしてくれる。例えば、出身地が都道府県で入力された列があれば、各都道府県ごとにグルーピングされる。
 この時点ではデータフレームそのものの見た目に変化はないのだが列をグループごとで分割しながら集計してくれるようになる(グルーピングを解除するにはungroup()。)
 つまり、データフレームdfに対して df %>% group_by(NO) %>% 集計用関数 としておけば、NOごとに件数を合計することができる。
 もちろん、sqldf()を使ってSQL文で抽出しながらSUMしてやることもできるんだろうけれど、そこまでするとSQLがメインになってしまうのでここではパスする。でも、sqldf()でデータフレームをデータソース扱いするのはアリだと思う。

集計のしかたの比較

 まずは、mapply()の結果から。
 mapply(fnc, x, y, z)としておくと、ベクタx~yの同一行の値を用いてfncを適用してくれる。おおよそ、fnc(x[n], y[n], z[n])をfor文で行数分だけグルグル回してるような感じだと思う。
 今回はfnc=sumにして行ごとの合計を行い、最後に特許件数という列に値を格納した。
 ちょっと面倒なのは、base::の関数なのでdplyrのパイプ処理とは相性が良くないことだ。(素直にやると、パイプしたものがfncに引き継がれてしまうっぽい。)

> mapply(sum, df0$特許保有_国内, df0$特許保有_海外,
   df0$特許出願_国内, df0$特許出願_海外) %>% mutate(df0, 特許件数=.) %>%
  select(NO, starts_with("特許"), -特許識別番号)
# A tibble: 157 x 6
     NO 特許保有_国内 特許保有_海外 特許出願_国内 特許出願_海外 特許件数
  <int>         <dbl>         <dbl>         <dbl>         <dbl>    <dbl>
1     1            NA            NA            NA            NA       NA
2     2            NA            NA            NA            NA       NA
3     3             4             2            11             8       25
4     4            NA            NA            NA            NA       NA
5     5            NA            NA             1             1       NA
6     6            NA            NA            NA            NA       NA
7     7             2             1             1            NA       NA
8     8            NA            NA            NA            NA       NA
9     9            NA            NA            17            NA       NA
10    10            NA            NA            NA            NA       NA
# ... with 147 more rows

 次に、pivot_longer() %>% group_by() %>% 集計用関数とした結果。当然だけれど、件数は同じ値になる
 集計用関数はsummarize()を用いた。summarize()は列ごとの計算結果をサマリーっぽく表示させることができる関数で、今回はsum()を使って合計を出しているが、n()で件数を数えたり、mean()で平均値を出したり、sd()で標準偏差を出したりすることができるし、もちろんmax()、min()もある。そしてgroup_by()とあわせるとExcelのピボットテーブルみたいなことができる……と考えると、Excelさんも何だかんだで有能なのだ。

------------------------
pivot_longer()した結果
------------------------
> df0 %>% select(NO, starts_with("特許"), -特許識別番号) %>%
  pivot_longer(c("特許保有_国内", "特許保有_海外", "特許出願_国内", "特許出願_海外"),
   names_to = "件数種別", values_to = "件数")
# A tibble: 628 x 3
     NO 件数種別       件数
  <int> <chr>         <dbl>
1     1 特許保有_国内    NA
2     1 特許保有_海外    NA
3     1 特許出願_国内    NA
4     1 特許出願_海外    NA
5     2 特許保有_国内    NA
6     2 特許保有_海外    NA
7     2 特許出願_国内    NA
8     2 特許出願_海外    NA
9     3 特許保有_国内     4
10     3 特許保有_海外     2
# ... with 618 more rows

-----------------------------------------
pivot_longer()した結果をさらにsummarize()
-----------------------------------------
> df3 <- df0 %>% select(NO, starts_with("特許"), -特許識別番号) %>%
  pivot_longer(c("特許保有_国内", "特許保有_海外", "特許出願_国内", "特許出願_海外"),
   names_to = "件数種別", values_to = "件数") %>%
  group_by(NO) %>%
  summarize(特許件数 = sum(件数)) %>%
  mutate(df0, 特許件数 = .$特許件数)
`summarise()` ungrouping output (override with `.groups` argument)

----------
最終結果
----------
> df3 %>% select(NO, starts_with("特許"), -特許識別番号)
# A tibble: 157 x 6
     NO 特許件数 特許保有_国内 特許保有_海外 特許出願_国内 特許出願_海外
  <int>    <dbl>         <dbl>         <dbl>         <dbl>         <dbl>
1     1       NA            NA            NA            NA            NA
2     2       NA            NA            NA            NA            NA
3     3       25             4             2            11             8
4     4       NA            NA            NA            NA            NA
5     5       NA            NA            NA             1             1
6     6       NA            NA            NA            NA            NA
7     7       NA             2             1             1            NA
8     8       NA            NA            NA            NA            NA
9     9       NA            NA            NA            17            NA
10    10       NA            NA            NA            NA            NA
# ... with 147 more rows

さて皆さんお気づきのように

 種別ごとの件数は0じゃないのに、合計の特許件数がNAになっている。
 例えば行3はちゃんと計算されているが、行5や行7などは値があるにもかかわらず合計がNAになっている。
 そう、NAが1つでもあるとちゃんと計算してくれないのである!!!

 えーっと、はい、NAを0に置換するのを忘れていました。だって最近、量的データ扱ってなかったんだもんよ。ごめんな。
 でもまあ、そういうときにはdplyr::replace_na()がある。これを使ってNAを0に置換して再計算させてみた。
 replaceの指定がlistになってるというクセがあって、list内では、"この列では" = "NAを0に置換する"、のように指定していく。

-----------------------------------------------
NAを0に置換して、df01というデータフレームにしておく
-----------------------------------------------
> df01 <- df0 %>%
   replace_na(replace = list(特許保有_国内 = 0, 特許保有_海外 = 0, 特許出願_国内 = 0,
    特許出願_海外 = 0))
    
------------------------------------
再計算した特許件数をdf01にmutateで追加
------------------------------------
> df01 %<>% select(NO, starts_with("特許"), -特許識別番号) %>%
   pivot_longer(c("特許保有_国内", "特許保有_海外", "特許出願_国内", "特許出願_海外"),
    names_to = "件数種別", values_to = "件数") %>%
   group_by(NO) %>% #グループ化して
   summarize(特許件数 = sum(件数)) %>% #集計して
   mutate(df01, 特許件数 = .$特許件数) #df01に特許件数列を追加

`summarise()` ungrouping output (override with `.groups` argument)

-----------
結果を表示
-----------
> df01 %>% select(NO, starts_with("特許"), -特許識別番号)
# A tibble: 157 x 6
     NO 特許件数 特許保有_国内 特許保有_海外 特許出願_国内 特許出願_海外
  <int>    <dbl>         <dbl>         <dbl>         <dbl>         <dbl>
1     1        0             0             0             0             0
2     2        0             0             0             0             0
3     3       25             4             2            11             8
4     4        0             0             0             0             0
5     5        2             0             0             1             1
6     6        0             0             0             0             0
7     7        4             2             1             1             0
8     8        0             0             0             0             0
9     9       17             0             0            17             0
10    10        0             0             0             0             0
# ... with 147 more rows

 解決☆(
 でも、よく考えたら、pivot_longer()後の件数列にna.raplace()する方が効率的だったんじゃないかなぁ。まあいいや。

最後は集計結果に基づいて抽出(何事もなかったかのように)

 当然、集計して終わりではなく、各会社ごとの特許件数を見ていくことになる。
 まずはざっくりと、特許を持っている会社とそうでない会社に分ける。
 簡易的にfilter()を用いて抽出された列の数でカウントしてみたが、157社中63社、つまり4割の会社は何らかの特許を持っていることが分かった。反対に、6割の会社は特許を持っていない。

> df01 %>% select(NO, starts_with("特許"), -特許識別番号) %>%
   filter(特許件数>0)
# A tibble: 63 x 6
     NO 特許件数 特許保有_国内 特許保有_海外 特許出願_国内 特許出願_海外
  <int>    <dbl>         <dbl>         <dbl>         <dbl>         <dbl>
1     3       25             4             2            11             8
2     5        2             0             0             1             1
3     7        4             2             1             1             0
4     9       17             0             0            17             0
5    13        6             1             0             5             0
6    14        7             5             2             0             0
7    15        1             0             0             1             0
8    21        8             3             3             0             2
9    26       60            12            27             3            18
10    27        1             1             0             0             0
# ... with 53 more rows

 すると次は、特許を持っている企業と持っていない企業の違いが気になる。
 ところが、そもそも業界によっては、はたまた同じ業界の中でもビジネスの上下流の違いによっては、ノウハウ勝負だったり特許での保護が難しかったりで、特許を持っていなくても成立するビジネスモデルになってることがある。
 また上で述べたように、事業が成り立っているところは継続的に出願できるのだけれど、設立年が遅い最近できた企業ほど持っている特許の数は少なくならざるを得なかったりする。
 最初からビジネスモデルの違いに着目してしまうと大変なので、まずは後者の設立年などの数値データと件数の関係を見てみたい。これで説明がつくのであれば、データ全体の解釈がとても簡単になる。

今日はここまで。

 
 


この記事が気に入ったらサポートをしてみませんか?