Clojure におけるデータ操作:R と Python と比較して

2026/03/22 18:55

Clojure におけるデータ操作:R と Python と比較して

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

本記事では、Clojure の

tablecloth
、R の
tidyverse/dplyr
、Python の
pandas
、そして Polars の 4 つのデータサイエンスライブラリを比較し、https://codewithkira.com/assets/penguins.csv から取得した Palmer Penguin CSV データセットを使用しています。
読み込み:
tablecloth
は文字列 “NA” を自動的に欠損(
nil
)として扱います;
pandas
はデフォルトでそれを認識します;R では
read_csv
na = "NA"
が必要です;Polars は
null_values="NA"
を使用します。
探索: 各ライブラリは同様のコマンド(
head
、列名表示、ソート)を提供しますが、欠損値を扱う際の構文や行選択方法に違いがあります。
フィルタリング:
tablecloth
select-rows
は明示的に
nil
を除外できる予測関数を受け取ります;
pandas
はブールマスクを使用します;dplyr と Polars も同等のフィルタ構文を提供します。
機能チェーン: Clojure は一次関数とスレッドファーストマクロ(
->
)を利用して簡潔なパイプラインを作成し、対照的に
pandas
/Polars は命令型の変更スタイル、tidyverse は宣言型動詞を採用します。
列選択: 各エコシステムで「すべての列から 1 列除外」「接頭辞一致」「数値列」「範囲フィルタ」など高度なセレクタが示されています。
リシェイプ: ピボットは
pivot->longer
(tablecloth)、
pivot_longer
(dplyr)、
pd.melt
(pandas)、および
unpivot
(Polars)で行われます。
列追加:
tablecloth
は不変に列を追加します(
ratio = bill_length_mm / flipper_length_mm
);一方、
pandas
は DataFrame をインプレースで変更します。
リネーム: すべての列は
:all str/upper-case
で大文字に変換できます;他のライブラリでも同等のマッピングや変換関数が存在します。
グルーピングと集計: 例として、種別ごとのカウントや各種別で最小体重を求める方法(
group-by
/
aggregate
group_by/summarise
.agg()
、および
group_by().agg()
)が示されています。
この比較は、すべてのライブラリが同様のタスクを実行できる一方で、設計哲学(関数型不変性対命令型変更)がコードの可読性・保守性・パフォーマンスに影響することを強調しています。今後はより詳細なベンチマークやライブラリセットの拡張を行い、
tablecloth
のような不変アプローチが長期的にメリットを提供できるかどうかをチームが判断できるようにすることが検討されます。

本文

発行日 2024‑07‑18

私はデータを扱うための Clojure のオープンソースツールを開発・教えることに多くの時間を費やしています。
この種の作業に Clojure を使おうとするほぼすべての人は、別の言語環境(主に R か Python)から来ています。Daniel Slutsky と共に、こうした背景を持つ方々向けに「翻訳」も含めた講座を体系化しています。

その一環として、このブログ記事で初期プレビューを共有したいと思います。
形式は、R と Polars を並べて比較した素晴らしい投稿(ここでの R は tidyverse ― データサイエンス向けに設計された R ライブラリ群)を読んだことからインスパイアされました。Pandas もデータセット操作ライブラリとして非常に人気があるため入れ、Clojure では tablecloth(我々のエコシステムで主要なデータ操作ライブラリ)を加えます。

元記事と同じ Palmer Penguin データセットを使います。
簡略化のため、CSV ファイルとしてコピーし、このサイトに公開しています。また、本記事では「dataset」という言葉を繰り返し使用しますが、Clojure ではテーブル型(列主導)のデータ構造であり、他言語の dataframe や data table と同等です。

必要なパッケージのインストール方法は既にご存知だと仮定し、初回出現時点でコードスニペットに必要なインポート・要件を含めます。では始めましょう!


データ読み込み

データを読むことはすべての言語で簡単ですが、便利なのは「欠損値」として解釈する値を即座に指定できる点です。このデータセットでは文字列

"NA"
が欠損値を表します。そこで、できるだけ早くコンストラクタに伝えます。

ライブラリコード
Tablecloth
clojure (require '[tablecloth.api :as tc])  (def ds (tc/dataset "https://codewithkira.com/assets/penguins.csv"))
R
r library(tidyverse)  ds <- read_csv("https://codewithkira.com/assets/penguins.csv", na = "NA")
Pandas
python import pandas as pd  ds = pd.read_csv("https://codewithkira.com/assets/penguins.csv")
Polars
python import polars as pl  ds = pl.read_csv("https://codewithkira.com/assets/penguins.csv", null_values="NA")

注:tablecloth はデフォルトで

"NA"
を欠損値(Clojure では
nil
)として解釈します。


データ探索の基本操作

以下は各ライブラリで基本的な探索タスクを行う方法を比較したものです。

操作TableclothdplyrPandasPolars
先頭10行を見る
tc/head ds 10
head(ds, 10)
ds.head(10)
ds.head(10)
全列名を確認
tc/column-names ds
colnames(ds)
ds.columns
ds.columns
1列選択
tc/select-columns ds "year"
select(ds, year)
ds[["year"]]
ds.select(pl.col("year"))
複数列選択
tc/select-columns ds ["year" "sex"]
select(ds, year, sex)
ds[["year", "sex"]]
ds.select(pl.col("year", "sex"))
行をフィルタ
tc/select-rows ds #(> (% "year") 2008)
filter(ds, year > 2008)
ds[ds["year"] > 2008]
ds.filter(pl.col("year") > 2008)
列でソート
tc/order-by ds "year"
arrange(ds, year)
ds.sort_values("year")
ds.sort("year")

ソート時の欠損値処理はライブラリごとに異なります。
tablecloth と polars では昇順で先頭、降順で末尾に配置されますが、dplyr と pandas はどちらも最後に置きます。

Tablecloth の行選択

Clojure の匿名関数を短縮書式で書くと次のようになります。

(tc/select-rows ds #(> (% "year") 2008))

Clojure の関数は第一級オブジェクトなので、

select-rows
の第3引数は行(列名→値のマップ)を受け取る predicate です。
Tablecloth は行を一つずつ処理せず、カラム単位で最適化された操作を実行します。


基本操作の組み合わせ例

body_mass_g
が 3800 以下のすべてのペンギンに対し
bill_length_mm
を取得する方法です。

ライブラリコード
Tablecloth```clojure (-> ds
(tc/select-rows #(and (% "body_mass_g") (> (% "body_mass_g") 3800)))
(tc/select-columns "bill_length_mm"))```
dplyr```r ds
Pandas
python ds[ds["body_mass_g"] < 3800]["bill_length_mm"]
Polars
python ds.filter(pl.col("body_mass_g") < 3800).select(pl.col("bill_length_mm"))

Tablecloth では欠損値(

nil
)は数値と比較できないため、明示的に除外する必要があります。
これは Clojure が動的でありながらも強く型付けされている性質を示しています。

thread-first マクロ

Tablecloth は

->
(thread‑first)マクロを使用し、R のパイプや Unix パイプに似た形で関数の出力を次へ渡します。


より高度なフィルタリングと選択

タスクライブラリコード
1つの列を除外して全列選択Tablecloth
tc/select-columns ds (complement #{"year"})
dplyr
select(ds, -year)
Pandas
ds.drop(columns=["year"])
Polars
ds.select(pl.exclude("year"))
特定文字列で始まる全列選択Tablecloth
tc/select-columns ds #(str/starts-with? % "bill")
dplyr
select(ds, starts_with("bill"))
Pandas
ds.filter(regex="^bill")
Polars
import polars.selectors as cs  ds.select(cs.starts_with("bill"))
数値列のみ選択Tablecloth
tc/select-columns ds :type/numerical
dplyr
select(ds, where(is.numeric))
Pandas
ds.select_dtypes(include='number')
Polars
ds.select(cs.numeric())
値の範囲で行をフィルタTablecloth
tc/select-rows ds #(< 3500 (% "body_mass_g" 0) 4000)
dplyr
filter(ds, between(body_mass_g, 3500, 4000))
Pandas
ds[ds["body_mass_g"].between(3500, 4000)]
Polars
ds.filter(pl.col("body_mass_g").is_between(3500, 4000))

Tablecloth の欠損値処理は柔軟で、マップ検索時にデフォルト値を指定できます(例:

(% "body_mass_g" 0)
)。


データセットの再構成

操作ライブラリコード
長形式へピボットTablecloth
clojure (tc/pivot->longer ds ["bill_length_mm" "bill_depth_mm" "flipper_length_mm" "body_mass_g"] {:target-columns "measurement" :value-column-name "value"})
dplyr`ds
Pandas
python pd.melt(ds, id_vars=ds.columns.drop(["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"]), var_name="measurement", value_name="value")
Polars
ds.unpivot(index=set(ds.columns) - set(["bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g"]), variable_name="measurement", value_name="value")

列の作成・リネーム

既存列に基づく新列追加

ライブラリコード
Tablecloth
clojure (require '[tablecloth.column.api :as tcc])  (tc/add-columns ds {"ratio" (tcc// (ds "bill_length_mm") (ds "flipper_length_mm"))})
dplyr
Pandas
Polars

Clojure のデータ構造は不変なので、tablecloth は元のデータセットを変更せず新しいものを生成します。
Pandas はインプレースで変更し、認知的負荷が増す場合があります。

列名リネーム

ライブラリコード
Tablecloth
tc/rename-columns ds {"bill_length_mm" "bill_length"}
dplyr
Pandas
Polars

列名の変換

タスクライブラリコード
全列を大文字にTablecloth
tc/rename-columns ds :all str/upper-case
dplyr
rename_with(ds, toupper)
Pandas
ds.columns = ds.columns.str.upper()
Polars
ds.select(pl.all().name.to_uppercase())
単位を列名から除去Tablecloth`tc/rename-columns ds #".+_(mm
dplyr`rename_with(penguins, ~ str_replace(.x, "^(.+)_(mm
Pandas```python import re ds = ds.rename(columns=lambda x: re.sub(r"(.+)_(mm
Polars
ds = ds.rename({col: col.replace("_mm","").replace("_g","") for col in ds.columns})

グループ化と集約

Tablecloth のグループ化は少し特殊です。
単一列名、複数列名、あるいは任意関数でグループ化できます。結果は「group name」「group id」「ネストされたデータセット」の3列からなる新しいデータセットになります。

タスクライブラリコード
種別ごとの件数集計Tablecloth```clojure (-> ds
(tc/group-by ["species"])
(tc/aggregate {"count" tc/row-count}))```
dplyr`ds
Pandas
ds.groupby("species").agg(count=("species","count"))
Polars
ds.group_by("species").agg(pl.count().alias("count"))
種別ごとの最低体重Tablecloth```clojure (-> ds
(tc/group-by ["species"])
(tc/aggregate {"lowest_body_mass_g" #(->> (% "body_mass_g")
                                        tcc/drop-missing
                                        (apply tcc/min))}))``` |

| | dplyr |

ds |> group_by(species) |> summarise(lowest_body_mass_g = min(body_mass_g, na.rm = TRUE))
| | | Pandas |
python ds.groupby("species").agg(     lowest_body_mass_g=("body_mass_g", lambda x: x.min(skipna=True)) ).reset_index()
| | | Polars |
ds.group_by("species").agg(pl.col("body_mass_g").min().alias("lowest_body_mass_g"))
|


結論

これらすべてのライブラリは共通のデータ操作タスクを実行できます。
言語とライブラリの選択は、コードの可読性・保守性・パフォーマンスに影響します。

Clojure の tablecloth は関数型プログラミングと不変性を重視し、より予測可能で再利用性の高いコードを提供しますが、新しいパラダイムへの適応が必要です。
この比較が「翻訳ガイド」としてだけでなく、各データサイエンスツールに根ざす哲学を紹介する一助となれば幸いです。

読んでいただきありがとうございます! 🙂


バージョン

ツールバージョン
MacOSSonoma 14.5
JVM21.0.2
Clojure1.11.1
Tablecloth7.02.1
R4.4.1 (tidyverse 2.0.0)
Python3.12.3
Pandas2.1.4
Polars1.1.0

同じ日のほかのニュース

一覧に戻る →

2026/03/25 0:29

Appleビジネス

## Japanese Translation: > **Apple Business ― デバイス管理と広告を統合したプラットフォーム** Apple は 4 月 14 日に、200 以上の地域で *Apple Business* を開始し、Apple Business Essentials、Apple Business Manager、および Apple Business Connect を置き換えます。新しいオールインワンソリューションは、組み込み型モバイルデバイス管理(MDM)と「Blueprints」を統合し、ゼロタッチ展開、自動化された Managed Apple Account の作成、従業員グループ/役割管理、アプリ配布、および Admin API を提供します。さらに、ビジネスメール、カレンダー、ディレクトリサービス、カスタムドメインサポート、iOS 26+、iPadOS 26+、macOS 26+ 向けの Apple Business アプリも付属しています。 既存顧客のデータは自動的に移行されます。 Apple Business は米国とカナダで Apple Maps 上の広告を追加(今夏開始)し、検索結果の上部および Suggested Places に表示される広告は明確にラベル付けされて透明性を保ちます。このプラットフォームには、Apple Business Connect のブランドプロファイルとロケーション機能が組み込まれており、リッチプレイスカード、ショーケース/カスタムアクション、ロケーションインサイト、ブランデッドコミュニケーション、および Tap to Pay ブランド化が含まれます。 コアサービスは全世界の新規ユーザーと既存ユーザーに対して無料で提供されます。有料オプションとして、最大 2 TB の iCloud ストレージ($0.99/ユーザー/月)と AppleCare+ for Business(デバイスあたり $6.99 またはユーザーあたり $13.99/月)が利用可能です。ゼロタッチ展開は、Apple または認定販売業者から購入したデバイスで利用でき、プライバシーモデルではユーザーの位置情報と広告インタラクションデータを Apple アカウントから分離し、オンデバイスに保存され、第三者と共有されません。

2026/03/25 6:24

害虫駆除向けの垂直型 SaaS を構築したいと思ったので、技術者として働くことにしました。

## Japanese Translation: (主要ポイントをすべて組み込む)** 著者は30 億ドルのTAMと強固な継続収益性が見えたため、害虫駆除業界へ転職しました。彼は業界最大手グループの子会社に採用され、即座にローカルブランド全体で**数十億ドル規模の売上を担当**しました。13日という記録的な期間で集中的な学習・セミナー・試験・監督付きトラック時間を経てライセンスを取得しました。 現場業務では、典型的な物流上の課題に直面しました:バッテリーがフラット、燃料カードが5週間以上遅れ、経費精算が遅い。会社のコアシステムは高度にカスタマイズされたSalesforce環境であり、オンボーディングには10個以上のモバイルアプリが必要でした。彼はトラックアイドリング、GPS、訪問時間、電話活動を通じて技術者のパフォーマンスを監視しました。最初は厳格な監視に抵抗したものの、結局は従事しました。 現場での成功により、彼は上級技術者を追跡し正式なトレーニングなしに小規模アップセルを実行したことから「潜入ボス」というニックネームを得ました。その後セールスへ転身し、アウトバウンドワークフローを構築して21日で24 k ARR契約(さらに小規模なアップセル)を確保しました。しかし、内部見積もりプロセスが複数署名と新規アカウント作成を必要とするため手間がかかり、取引損失のリスクがあることに気付きました。 セールスチームは10年以上の経験を持つベテラン担当者で構成されており、それぞれ800k–1.2M ARRを生成し低いチャーン率を維持しています。彼らの文化は変化に抵抗が強く、エグジットインタビュー後、マネージャーから自分自身の会社を立ち上げるよう提案されました。 今後、著者はローカルニッチオペレーターを取得し、専用ツールを開発し、見積もり・オンボーディング・監視を統合したスケーラブルなプラットフォームを構築する計画です。このモデルにより現場技術者とセールス担当者の効率が向上し、チャーンが減少し、収益獲得が加速し、害虫駆除業界で競争力のダイナミクスが変わる可能性があります。

2026/03/24 21:06

HNに知らせてください:PyPI 上の Litellm 1.82.7 と 1.82.8 が改ざんされていること。

## 日本語訳: > PyPI の wheel **`litellm==1.82.8`** には、悪意のある `.pth` ファイル(`litellm_init.pth`、34 628 バイト)が含まれており、このファイルは `litellm` をインポートしなくても、Python インタープリターが起動するたびに自動的に実行されます。 > 隠されたスクリプトは二重 Base64 エンコードされたペイロードを実行し、ホストから認証情報(SSH キー、クラウドプロバイダーのクレデンシャル、Git の設定、Docker 設定、データベースパスワード、暗号ウォレットの秘密鍵など)を収集します。取得したデータは一時ファイルに書き込まれ、その後ランダムな AES‑256 キーで暗号化されます。AES キー自体はハードコードされた 4096 ビット公開鍵(キーは `MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A...` から始まります)を使って RSA 暗号化されています。 > 暗号化されたアーカイブ(`tpcp.tar.gz`)は **`https://models.litellm.cloud/`** に POST されます。 > この脆弱性は **2026‑03‑24** に、Ubuntu 24.04 の Docker コンテナで Python 3.13 を実行している環境で発見され、影響を受けたのはバージョン 1.82.8 だけが確認されています(他のリリースも脆弱になっている可能性があります)。 > PyPI 管理者はこの wheel を直ちに削除または取り下げるべきです。`litellm==1.82.8` をインストールしたユーザーは、`site‑packages/` 内の怪しい `.pth` ファイルを確認し、漏洩した認証情報をローテーションし、CI/CD パイプラインが侵害されていないか監査してください。この脆弱なバージョンを使用していたシステム(ローカル開発マシン、Docker コンテナ、本番サーバー、自動ビルドシステムなど)は、多数の組織にわたる幅広い秘密情報が漏洩した可能性があります。

Clojure におけるデータ操作:R と Python と比較して | そっか~ニュース