LoginSignup
1
0

GitHub Copilot Labs で R から色々翻訳してもらったら、なんだか色んな言語を喋れる気分になって楽しかった話

Last updated at Posted at 2023-05-18

GitHub Copilot 便利ですよね。
僕は R / Tidyverse でデータ処理をする事が多いのですが、最近は Python / Rust はじめ色んな言語のチームと仕事をする機会が増えてきました。

「データ処理言語R」は全く見たことも無い、という人も少なくないのですが慣れると多分データ処理、クレンジングから初期分析には今でも最強の言語だと思います。

Rの特徴として言われることの一つに、

「コンピュータに苦労させて、人間がラクをする言語」

があります。Rに比べると、他の言語は「コンピュータにラクをさせるために、人間が苦労をする言語」なのですと。
それを証明するような、アルゴリズム的にもなんか凄いライブラリがたくさんありますが、僕みたいなへっぽこには「最小限のタイプ量で結果が得られる」事が大きなメリットだったりします。

GitHub Copilot Labs ベータへの参加権が得られましたので、Rから色んな言語に翻訳してみて「Rより短く書ける言語はあるのかな?」をざっくり調べてみました。

僕は R 以外は殆ど書いていないので、正直動くコードなのかすら不安です!
「へーそうなんだ?」くらいにだらーっと見て頂ければ幸いです。

R - dplyr (tibble)

対象としたのはこんなコードです。特になんちゃない、データ整形スクリプトです。
たまたま今書いていたスクリプトから拾っただけであまり深く考えずに選んでいます。
明らかに不要な arrange があったりしますね。これを削除してくれたりもするかな?

code.R
id_creators <- meta_all %>%
  filter(metatype == "creator") %>%
  select(id, creator = meta1, num) %>%
  arrange(num) %>%
  group_by(id) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

R - data.table

まずはRの別ライブラリ。今でもpolarsと並ぶ最速クラスのデータフレーム処理ライブラリです。dtply を使うと dplyr コードからほぼそのまま変換出来る上に超高速です。個人的には括弧とかが複数行を跨いでちょっと書きづらいです。
(Copilotからはライブラリの指定が出来ないのでこちらとdata.frame()だけ、chatGPTからコピペしています)

code-datatable.R
id_creators <- meta_all[metatype == "creator", 
                        .(id = id, creator = meta1, num = num)
                        ][order(num),
                        .(count = .N), by = .(id)
                        ][order(-count)]

R - data.frame()

tibble() data.table() が生まれる前はこちらが標準でした。最近ではほぼ書かないですね。
(pythonのpandasはこちらのデータフレームを参考にしたと言われています。 )
なぜかこちらとJAVAだけコメントが付きました。文化的な違い?

code-dataframe.R
# Filter
meta_filtered <- meta_all[meta_all$metatype == "creator",]
# Select
meta_selected <- meta_filtered[, c("id", "meta1", "num")]
names(meta_selected)[names(meta_selected) == "meta1"] <- "creator"
# Arrange by num
meta_arranged <- meta_selected[order(meta_selected$num),]
# Group_by and summarise
meta_grouped <- aggregate(. ~ id, meta_arranged, length)
names(meta_grouped)[names(meta_grouped) == "creator"] <- "count"
# Arrange by count (desc)
meta_final <- meta_grouped[order(-meta_grouped$count),]
# Assign result to id_creators
id_creators <- meta_final

SQL

データ処理と言えばまずSQL。dplyr は dbplyr をつかってSQL変換が可能なくらいで、難無く翻訳してくれました。 要らないarrangeも省かれてますね。

code.sql
SELECT id, COUNT(*) AS count
FROM meta_all
WHERE metatype = 'creator'
GROUP BY id
ORDER BY count DESC

Python

多種多様なやり方が出てきました。最初のは polars かな? dplyrっぽい。
他にも実行するたびに違うやり方が出てきたのですが、選択肢多すぎてみんな迷子にならないのかな、って時々思います。

code.py
id_creators = meta_all.filter("metatype = 'creator'")
                      .select("id", "meta1 as creator", "num")
                      .sort("num")
                      .group("id")
                      .summarize(:count => :n)
                      .sort("count", :desc)

id_creators = meta_all[meta_all.metatype == 'creator'][['id', 'meta1', 'num']] \
    .assign(creator=lambda x: x.meta1) \
    .sort_values('num') \
    .groupby('id') \
    .size() \
    .reset_index(name='count') \
    .sort_values('count', ascending=False)

id_creators = meta_all[
    meta_all["metatype"] == "creator", ["id", "meta1", "num"]
].rename(columns={"meta1": "creator", "num": "count"}).groupby(
    "id").agg({"count": "count"}).sort_values("count", ascending=False)

Julia

最近のデータ処理ならJulia! って思ってたのとだいぶ違う感じで出てきました。Rの data.table っぽい?

code.jl
meta_all = DataFrame(id = rand(1:10, 100), metatype = rand(["creator", "notcreator"], 100), meta1 = rand(1:10, 100), num = rand(1:10, 100))
id_creators = meta_all[meta_all.metatype .== "creator", :]
id_creators = by(id_creators, :id, df -> DataFrame(creator = df.meta1[1], num = df.num[1], count = size(df, 1)))
id_creators = sort!(id_creators, cols=[:count], rev=true)

Rust

こちらは polars ですかね。 あれ qiita ってrust認識しない? 2つ目のはなかなかわからないです。

code.rs
let id_creators = meta_all
    .filter(|row| row.metatype == "creator")
    .select(&["id", "creator = meta1", "num"])
    .arrange(&["num"])
    .group_by(&["id"])
    .summarise(&[("count", "n()")])
    .arrange(&["desc(count)"]);

let id_creators = meta_all
    .iter()
    .filter(|&x| x.metatype == "creator")
    .map(|x| (x.id, x.meta1, x.num))
    .collect::<Vec<_>>()
    .into_iter()
    .sorted_by(|a, b| a.2.cmp(&b.2))
    .group_by(|x| x.0)
    .into_iter()
    .map(|(k, v)| (k, v.count()))
    .collect::<Vec<_>>()
    .into_iter()
    .sorted_by(|a, b| a.1.cmp(&b.1))
    .collect::<Vec<_>>();

JAVA

触ってる方が多いのはこちらでしょうか。データフレーム処理ライブラリは何が人気なのだろ?こちらも色んな提案がありました。最初の奴は結構好きな感じだけど、これホントにJAVA?
なぜか JAVA だけコメントも付けてくれました。変数名がどんどん長くなっていくのも雰囲気出てるなと感じました。

code.java
d_creators = meta_all
  .filter(x -> x.metatype.equals("creator"))
  .select("id", "creator", "num")
  .arrange("num")
  .group_by("id")
  .summarise("count = n()")
  .arrange("desc(count)");

public class IdCreators {
    public static void main(String[] args) {
        // Read the data
        DataFrame metaAll = DataFrame.readCsv("meta_all.csv");
        // filter(metatype == "creator")
        DataFrame metaAllFiltered = metaAll.filter(metaAll.col("metatype").eq("creator"));
        // select(id, creator = meta1, num)
        DataFrame metaAllSelected = metaAllFiltered.select(metaAllFiltered.col("id"),
                metaAllFiltered.col("meta1").as("creator"), metaAllFiltered.col("num"));

        // arrange(num)
        DataFrame metaAllSelectedArranged = metaAllSelected.sort(metaAllSelected.col("num"));
        // group_by(id)
        DataFrame metaAllSelectedArrangedGrouped = metaAllSelectedArranged.groupBy(metaAllSelectedArranged.col("id"));
        // summarise(count = n())
        DataFrame metaAllSelectedArrangedGroupedSummarised = metaAllSelectedArrangedGrouped.count();
        // arrange(desc(count))
        DataFrame metaAllSelectedArrangedGroupedSummarisedArranged = metaAllSelectedArrangedGroupedSummarised
                .sort(metaAllSelectedArrangedGroupedSummarised.col("count").desc());

        metaAllSelectedArrangedGroupedSummarisedArranged.show();
    }
}

Dart

Flutter で知名度上がってますね。そろそろ動くのか本気でわからないw

code.dart
id_creators = meta_all
    .where((row) => row["metatype"] == "creator")
    .map((row) => {"id": row["id"], "creator": row["meta1"], "num": row["num"]})
    .toList();
id_creators.sort((a, b) => a["num"].compareTo(b["num"]));
id_creators = groupBy(id_creators, (row) => row["id"])
    .map((rows) => {"id": rows[0]["id"], "count": rows.length})
    .toList();
id_creators.sort((a, b) => b["count"].compareTo(a["count"]));

Swift

データフレーム系のライブラリが無いと、mapを多用するのはどの言語も似ているなと思います

code.swift
let id_creators = meta_all
    .filter { $0.metatype == "creator" }
    .map { ($0.id, $0.meta1) }
    .grouped { $0.id }
    .map { (id: $0, creator: $1[0].meta1, num: $1.count) }
    .sorted { $0.num < $1.num }
    .grouped { $0.id }
    .map { (id: $0, count: $1.count) }
    .sorted { $0.count > $1.count }

lua

え、まじでこれだけで動くの??

code.lua
id_creators = meta_all:filter(metatype == "creator"):select(id, creator = meta1, num):arrange(num):group_by(id):summarise(count = n()):arrange(desc(count))

TypeScript

({{これで}} => ほんとに).動くの === "かなぁ..." {})) <- 書けないやつ

code.ts
const id_creators = meta_all
  .filter((d) => d.metatype === 'creator')
  .map((d) => ({ id: d.id, creator: d.meta1, num: d.num }))
  .sort((a, b) => a.num - b.num)
  .reduce((acc, d) => {
    if (!acc[d.id]) acc[d.id] = [];
    acc[d.id].push(d.creator);
    return acc;
  }, {});

const id_creators2 = Object.keys(id_creators)
  .sort((a, b) => id_creators[b].length - id_creators[a].length)
  .map((d) => ({ id: d, count: id_creators[d].length }));

perl

いつのまにかちょっと懐かしい言語、と思うようになっていました。web初期のcgiはみんなperlでしたね。思ったよりコンパクトなコードでした

code.perl
my $id_creators = $meta_all->select_cols(qw(id meta1 metatype num))
    ->filter(sub { $_->at('metatype') eq 'creator' })
    ->rename({ meta1 => 'creator' })
    ->arrange(sub { $_->at('num') })
    ->group_by(sub { $_->at('id') })
    ->summarise(sub { $_->at('count') = $_->nrow })
    ->arrange(sub { - $_->at('count') });

php

Web言語といえばこちらも。

code.php
$meta_all = $this->getMetaAll();
$metatype = 'creator';
$id_creators = $meta_all->filter(function ($row) use ($metatype) {
    return $row['metatype'] === $metatype;
})->map(function ($row) {
    return [
        'id' => $row['id'],
        'creator' => $row['meta1'],
        'num' => $row['num'],
    ];
})->sortBy('num')->groupBy('id')->map(function ($group, $id) {
    return [
        'id' => $id,
        'count' => count($group),
    ];
})->sortByDesc('count');

go

データ処理のイメージは殆どない go ですが、書けなくは無いみたい。データフレーム処理のライブラリを指定とかしたらもっと変わるのかな?

code.go
func idCreators(metaAll []Meta) []IdCreator {
  var idCreators []IdCreator
  for _, meta := range metaAll {
    if meta.MetaType == "creator" {
      var idCreator IdCreator
      idCreator.Id = meta.Id
      idCreator.Creator = meta.Meta1
      idCreator.Num = meta.Num
      idCreators = append(idCreators, idCreator)
    }
  }
  sort.Slice(idCreators, func(i, j int) bool {
    return idCreators[i].Num < idCreators[j].Num
  })
  var idCreatorsByCount []IdCreatorByCount
  for _, idCreator := range idCreators {
    var idCreatorByCount IdCreatorByCount
    idCreatorByCount.Id = idCreator.Id
    idCreatorByCount.Creator = idCreator.Creator
    idCreatorByCount.Num = idCreator.Num
    idCreatorByCount.Count = 1
    idCreatorsByCount = append(idCreatorsByCount, idCreatorByCount)
  }
  var idCreatorsGroupById []IdCreatorGroupById
  for _, idCreatorByCount := range idCreatorsByCount {
    var idCreatorGroupById IdCreatorGroupById
    idCreatorGroupById.Id = idCreatorByCount.Id
    idCreatorGroupById.Creator = idCreatorByCount.Creator
    idCreatorsGroupById = append(idCreatorsGroupById, idCreatorGroupById)
  }
  for i, idCreatorByCount := range idCreatorsByCount {
    for j, idCreatorGroupById := range id

C

なぜかCへの翻訳は、まともに動かないコードや、明らかなエラーが頻繁に出ました。 プロンプト漏れてたり。
まだ多少動きそうな雰囲気があったのがこちら。 map/reduce とかCで書いた記憶殆ど無いと思ったら、メソッドチェーンなんて出来たんでしたっけ?
高級アセンブラとか言ってましたね。

  var id_creators = meta_all
    .filter(function(row) {
        return row.metatype == "creator";
    })
    .map(function(row) {
        return {
            id: row.id,
            creator: row.meta1,
            num: row.num
        };
    })
    .sort(function(a, b) {
        return a.num - b.num;
    })
    .reduce(function(acc, row) {
        if (!acc[row.id]) {
            acc[row.id] = 1;
        } else {
            acc[row.id] += 1;
        }
        return acc;
    }, {});
id_creators = Object.keys(id_creators).map(function(key) {
    return {
        id: key,
        count: id_creators[key]
    };
});
id_creators = id_creators.sort(function(a, b) {
    return b.count - a.count;
});

c++

こちらも殆ど動作しそうなコードは出ませんでした。 お、なんか見れるコードだ?と思ったら Rcpp用のコードみたい?

[[Rcpp::export]]
DataFrame id_creators(DataFrame meta_all) {
  // id_creators <- meta_all %>%
  //   filter(metatype == "creator") %>%
  //   select(id, creator = meta1, num) %>%
  //   arrange(num) %>%
  //   group_by(id) %>%
  //   summarise(count = n()) %>%
  //   arrange(desc(count))
  DataFrame meta_all_filtered = meta_all[meta_all["metatype"] == "creator"];
  DataFrame meta_all_selected = meta_all_filtered[{"id", "meta1", "num"}];
  DataFrame meta_all_reordered = meta_all_selected.sort("num");
  DataFrame meta_all_grouped = meta_all_reordered.group_by({"id"});
  DataFrame meta_all_summarised = meta_all_grouped.summarise({"count"}, {"nrow"});
  DataFrame meta_all_sorted = meta_all_summarised.sort({"count"}, false);
  return meta_all_sorted;
}

Objective-C

こちらは力技って感じでそれっぽいコードが出てきました。スマホアプリのデータフレーム処理は何がベストなんだろう? sqliteでSQL使えって事かな?

code.mm
NSMutableArray *id_creators = [[meta_all filter:^BOOL(id obj) {
    NSDictionary *dict = (NSDictionary *)obj;
    BOOL metatype = [[dict objectForKey:@"metatype"] isEqualToString:@"creator"];
    return metatype;
}] select:^id(id obj) {
    NSDictionary *dict = (NSDictionary *)obj;
    NSString *id = [dict objectForKey:@"id"];
    NSString *creator = [dict objectForKey:@"meta1"];
    NSString *num = [dict objectForKey:@"num"];
    return @{@"id": id, @"creator": creator, @"num": num};
}];

id_creators = [[id_creators arrange:^NSArray *(id obj) {
    NSDictionary *dict = (NSDictionary *)obj;
    NSString *num = [dict objectForKey:@"num"];
    return @[num];
}] groupBy:^id(id obj) {
    NSDictionary *dict = (NSDictionary *)obj;
    NSString *id = [dict objectForKey:@"id"];
    return id;
}];

id_creators = [[id_creators summarise:^id(id obj) {
    return @{@"count": @([obj count])};
}] arrange:^NSArray *(id obj) {
    NSDictionary *dict = (NSDictionary *)obj;
    NSNumber *count = [dict objectForKey:@"count"];
    return @[count];
}];

Cobol

みんな大好きCobolです。初めて見ました。どんな感情になれば良いのかわからずに居ます。

code.cob
IDENTIFICATION DIVISION.
       PROGRAM-ID. ID-CREATORS.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  ID-CREATORS-RECORD.
           05  ID-CREATORS-ARRAY OCCURS 100 TIMES
               DEPENDING ON ID-CREATORS-COUNT.
               10  ID-CREATORS-ID PIC X(20).
               10  ID-CREATORS-COUNT PIC 9(5).
       01  ID-CREATORS-COUNT PIC 9(5).
       01  ID-CREATORS-INDEX PIC 9(5).
       01  ID-CREATORS-ARRAY-INDEX PIC 9(5).
       PROCEDURE DIVISION.
           MOVE 0 TO ID-CREATORS-COUNT
           PERFORM 100 TIMES
               ADD 1 TO ID-CREATORS-INDEX
               IF ID-CREATORS-INDEX > 100
                   GO TO END-OF-PERFORM
               END-IF
               PERFORM 100 TIMES
                   ADD 1 TO ID-CREATORS-ARRAY-INDEX
                   IF ID-CREATORS-ARRAY-INDEX > 100
                       GO TO END-OF-PERFORM
                   END-IF
                   IF ID-CREATORS-ARRAY(ID-CREATORS-ARRAY-INDEX)
                       NOT EQUAL TO "creator"
                       GO TO END-OF-PERFORM
                   END-IF
                   IF ID-CREATORS-ID(ID-CREATORS-ARRAY-INDEX)
                       EQUAL TO ID-CREATORS-ID(ID-CREATORS-INDEX)
                       ADD 1 TO ID-CREATORS-COUNT(ID-CREATORS-INDEX)
                       GO TO END-OF-SECOND-PERFORM
                   END-IF
               END-PERFORM
               ADD 1 TO ID-CREATORS-COUNT
               MOVE ID-CREATORS-ID(ID-CREATORS-INDEX)
                   TO ID-CREATORS-ID(ID-CREATORS-ARRAY-INDEX)
               MOVE ID-CREATORS-COUNT
                   TO ID-CREATORS-COUNT(ID-CREATORS-ARRAY-INDEX)
               END-PERFORM
           END-PERFORM
           END-OF-PERFORM.
       STOP RUN.
       END PROGRAM ID-CREATORS.

終わりです!

僕はプログラミングの専門家でもなんでもありません!あくまで「わー Copilotって今こんな感じなのね」のプレビュー程度にお読み頂ければ幸いです。
chatGPTにコピペするより圧倒的に反応が速いのも嬉しかったです。(chatGPTって、あのぽちぽち... って喋ってる感じの演出がウマいなと思います)

それぞれの言語の専門家の方なら、もっと短く、それこそ dplyr 構文よりコンパクトに書けるのかもしれませんが、NSEにパイプといった R の機能のありがたみを再確認する時間でもありました。

僕自身はよく知らない言語をいっぱい見れて楽しかったです。 Python触る時は polars でやろ〜と思ったりもしました。
もし R をご存じない方がご覧頂けていたら、ちょっとでも便利そうと思って頂けるとお遊び冥利に尽きます。

それでは最後までお付き合い頂きましてありがとうございました!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0