🔰

NimでDataFrameを操作

2023/02/15に公開

はじめに

datamancerは、多次元配列を操作するarraymancerをコアとして作られた100%Nimで書かれたDataFrameライブラリです。
R言語のDataFrameライブラリであるdplyrを元にしています。
※この操作説明は、datamancerの「SciNim Getting Started」を元に作成しています。

環境

私のPC環境はWindowsで動作させています。

  • OS: Windows11
  • Nim 2.0.8
  • datamancer 0.4.6

datamancerの特徴

  • カラムの型は、int, float, string, bool, valueがあるが、現時点datetimetime型は存在しません。作者曰く無くても、そんなに問題ないとの事
  • メソッドには、DataFrameの値を直接扱う物(row, add, getKeys, head, tailなど)と、DataFrameを条件抽出するの物(select, filter, mutate, group_by, summarizeなど)が存在し、操作可能です。

datamancerライブラリのインストール

nimble install datamancer

ターミナルからnimbleコマンドを実行すると、datamancerが利用するライブラリも含めて、インストールされます。

動作確認

1. DataFrameの生成

DataFrameを生成する方法は4つあります。

  • newDataFrameで空のDataFrameを作成
  • toDf関数としてDataFrameを作成
  • CSVファイルからDataFrameを生成
  • CSV形式の文字列からDataFrameを生成

1-1. newDataFrame関数による生成

newDataFrame関数による生成
import datamancer

var df = newDataFrame()                      # 空のDataFrameを生成し、カラムを追加
for i in 0 ..< 3:
  df["x" & $i] = @[1 + i, 2 + i, i + 3]      # 列(カラム)毎の追加

1-2. toDf関数による生成

toDf関数による生成
import datamancer

# カラム列を抽出
let s1 = [1, 2, 3]                           # array型
let s2 = @["hello", "foo", "bar"]            # seq型
let s3 = @[1.5, 2.5, 3.5].toTensor           # tensor型

let df = toDf({"clm01": s1, "clm02": s2, "clm03": s3})  # カラムを挿入
echo df                                                 # toDf(s1, s2, s3)でも良いが、その場合、変数名s1がカラム名になる

# 直接値を入れても良い
let df2 = toDf({"x" : @[1, 2, 3], "y" : @[4.0, 5.0, 6.0], "z" : @["a", "b", "c"]})
echo df2

1-3. CSVファイルから生成

CSVファイルから生成
import datamancer

let df = readCsv("test.csv")                 # ヘッダー行を指定したCSVファイルを読む
echo df

1-4. CSV形式の文字列から生成

CSV形式の文字列から生成
import datamancer

let exp = """
x,y,z
1,2,3
4,5,6
7,8,9
"""
let df = parseCsvString(exp, sep = ',')      # 第二引数のsepは`,`なら指定なくても問題ない
echo df

2. 列(カラム)の追加・削除・カラム名変更

2-1. 列(カラム)の追加

列(カラム)の追加
import datamancer

let exp = """
x,y,z
1,2,3
4,5,6
7,8,9
"""
var df = parseCsvString(exp)

let a = @["japan","america","china"]
# カラム名をseqデータを追加 (行数が合わないとエラーになる)
df["country"] = a
echo df
結果
DataFrame with 4 columns and 3 rows:
     Idx            x            y            z      country
  dtype:          int          int          int       string
       0            1            2            3        japan
       1            4            5            6      america
       2            7            8            9        china

2-2. 列(カラム)の削除

列(カラム)の削除
# 2.1のプログラムに追記する
# 列名(カラム名)を指定して、drop関数で削除
df.drop("country")
echo df
結果
DataFrame with 3 columns and 3 rows:
     Idx            x            y            z
  dtype:          int          int          int
       0            1            2            3
       1            4            5            6
       2            7            8            9

2-3. 列(カラム)のカラム名変更

列(カラム)のカラム名を変更
# 2.2のプログラムに追記する
# renameは、マクロf{}を使用して右の旧名称から新名称に変更し、コピーする形になります。
var df2 = df.rename(f{"xxx" <- "x"})
echo df2
結果
DataFrame with 3 columns and 3 rows:
     Idx            y            z          xxx
  dtype:          int          int          int
       0            2            3            1
       1            5            6            4
       2            8            9            7

※追記になるので、カラム列は最後尾に移動される

3. 行の追加

3-1. 行の追加

行の追加
import datamancer

let data = """
   name     height     age
   John     178.9      13
   ben      182.0      11
   alice    162.5      15
   mark     201.5      29
   zion     198.0      20
"""
var df = parseCsvString(data, sep = ' ')

# 1行追加 (追加するカラム属性は、同一でないとエラーになる)
df.add("tyson", 178, 58)
echo df
結果
DataFrame with 3 columns and 6 rows:
     Idx         name       height          age
  dtype:       string        float          int
       0         John        178.9           13
       1          ben          182           11
       2        alice        162.5           15
       3         mark        201.5           29
       4         zion          198           20
       5        tyson          178           58

3-2. 同一型のDataFrame結合

同一型のDataFrame結合
# 3-1.のプログラムに追記する
# 結合する複数行のDataFrameを用意する
let df2 = toDf({ "name": ["terry", "rock"],
                 "height": @[175.2, 205.1],
                 "age": @[18, 42]})

# bind_rows関数で結合を行う
var expDf = bind_rows([df, df2])
echo expDf
結果
DataFrame with 3 columns and 8 rows:
     Idx         name       height          age
  dtype:       string        float          int
       0         John        178.9           13
       1          ben          182           11
       2        alice        162.5           15
       3         mark        201.5           29
       4         zion          198           20
       5        tyson          178           58
       6        terry        175.2           18
       7         rock        205.1           42

※df2の値が最後尾2行に追加される

4. 値の抽出と変更

4-1. 値の抽出 (指定列・行の抽出)

値の抽出 (指定列・行の抽出)
import datamancer

let data = """
   name     height     age
   John     178.9      13
   ben      182.0      11
   alice    162.5      15
   mark     201.5      29
   zion     198.0      20
"""
var df = parseCsvString(data, sep = ' ')

# 指定行・列から値を抽出
# ※注意 [カラム名][行数]は登録時のみ可能、値の抽出は[カラム名, 行数]
var value_name1: string  = df["name", 3, string]
var value_height1: float = df["height", 3, float]
var value_age1: int      = df["age", 3, int]
echo value_name1, ", ", $value_height1, ", ", $value_age1

# または 上下どちらの記載でも、同じ値を求める事が出来る

var value_name2: string  = df["name", 3].toStr()
var value_height2: float = df["height", 3].toFloat()
var value_age2: int      = df["age", 3].toInt()
echo value_name2, ", ", $value_height2, ", ", $value_age2

# 行指定後に列指定しても、同じ値を求める事が出来る

var value_name3: string  = df.row(3)["name"].toStr()
var value_height3: float = df.row(3)["height"].toFloat()
var value_age3: int      = df.row(3)["age"].toInt()
echo value_name3, ", ", $value_height3, ", ", $value_age3
結果
mark, 201.5, 29
mark, 201.5, 29
mark, 201.5, 29

4-2. 列(カラム)の抽出

列(カラム)の抽出
# 4-1.のプログラムに追記する
# height列だけを取得しsortする (この時の型はTensor[float]型になる)
var height_clm = df["height", float].sorted 

echo "--- height sorted ---"
for i in height_clm:
   echo i
結果
--- height sorted ---
162.5
178.9
182.0
198.0
201.5

4-3. 行の抽出

行の抽出
# 4-2.のプログラムに追記する
# row関数で指定行を抽出 (1行目を取得)
var value_row = df.row(0)

# これは、前説の4-1. 値の抽出 (指定列・行の抽出)と同じです
var value_name4: string  = value_row["name"].toStr()
var value_height4: float = value_row["height"].toFloat()
var value_age4: int      = value_row["age"].toInt()
echo value_name4, ", ", $value_height4, ", ", $value_age4
結果
John, 178.9, 13

4-4. 指定行・列の変更

指定行・列の変更
# 4-3.のプログラムに追記する
# 値の修正と追加
df["age"][1] = 10                             # age列の2行目だけを変更
echo df
結果
DataFrame with 3 columns and 5 rows:
     Idx         name       height          age
  dtype:       string        float          int
       0         John        178.9           13
       1          ben          182           10   <= ここ変更される
       2        alice        162.5           15
       3         mark        201.5           29
       4         zion          198           20

4-5. 指定列に全て同一の固定値を設定

指定列に全て同一の固定値を設定
# 4-4.のプログラムに追記する
# 値の修正と追加
df["age"] = 100                            # test列を追加、行の値はすべて100
echo df
結果
DataFrame with 3 columns and 5 rows:
     Idx         name       height          age
  dtype:       string        float     constant
       0         John        178.9          100
       1          ben          182          100
       2        alice        162.5          100
       3         mark        201.5          100
       4         zion          198          100

※ageカラム全ての値が100になる

5. 式マクロf{}の説明

5-1. f{}マクロの使い方

f{}マクロはカラムの名称変更、追加、算術などに使われます。
使う関数は、6.1 関数一覧に記載された関数群で利用可能

5-2. カラム名の表示

# f{}マクロ内でのカラム表示は、"clm_name"・`clm_name`・c"clm_name"・col("clm_name")の4通りで表現されます。
# 式を現す「~ << <- ->」の代入する方の左カラム名は「"clm_name"」が無難
# 逆に計算式を行う右カラム名は、「col("clm_name")」が無難
# ※min maxの計算の時は、「col("clm_name")」でないと何故かエラーになる

# 式マクロだけを使った結果表示
import datamancer
let f = f{`height` ~ (c"hwy" + c"cty")} # 式マクロを変数に代入
echo f.colName, " : ", f.name # 出力結果 height : (~ height ((+ hwy cty)))
                              # colNameからカラム名が抽出できる、nameは式情報を表示(ポーランド記法で表示される)
let f2 = f{1 + 2}             # 数値だけの算術の場合
echo f2.val                   # 出力結果 3が表示される

5-3. <-<<~->式記号の使い方

式マクロ内では式記号を使って、カラムに代入される。

  • <-はカラム名を変更する場合に使われます。
    • 例:f{"new_colum" <- "old_colum">}
  • <<は集計カラムを追加する場合に使われます。
    • 別カラム追加時に集計結果表示時に使用される
    • summarize関数で使用される
  • ~は算術カラムを追加する場合に使われます。
    • 別カラム追加時に算術結果表示時に使用される
    • mutate関数や、transmute関数で使用される
  • ->はカラム属性を変更する場合に使われます。
    • 属性の変更時に使用される
    • f{string -> float: "yFloat" ~ parseFloat(df["y"][idx])}) stringをfloatにカラム属性変更などで利用される

6. DataFrameの集計・集約・計算関数の使い方

6-1. 関数一覧

関数名 内容 使用される式記号
select 列の抽出 <-
rename 列名の変更 2-3.に内容を記載 <-
filter 指定列の条件で検索 == != < > in notin
mutate 列の追加と計算を行う ~
transmute 浮動小数点演算を行う ~
arrange 指定列で昇順ソート
summarize 集計 <<
group_by 指定列内の値で分類 6-6.参照
inner_join 集約
setDiff DataFrame同士の異なる値を抽出
count 列内の同一件数を抽出
bind_rows 行の追加 3-2.に内容を記載
gather 列名(カラム名)毎に、値を行表示
spread 指定列によって分類される
unique 同一値の集約を行う

6-2. select関数(項目の抽出)

select関数(項目の抽出)
mport datamancer

let data = """
   name     height     age
   John     178.9      13
   ben      182.0      11
   alice    162.5      15
   mark     201.5      29
   zion     198.0      20
"""
var df = parseCsvString(data, sep = ' ')

# selectはDataFrameから項目を抽出
# fマクロは1項目だけなので、カンマで繋げる またカラム名の変更も可能
var df2 = df.select(f{"name"}, f{"rename_age" <- "age"})
echo df2

# var df3 = df.select(@["name", "age"])
# 上記例のように、seqやarrayも可能
var df3 = df.select("height", "age")
echo df3
結果
DataFrame with 2 columns and 5 rows:
     Idx         name   rename_age
  dtype:       string          int
       0         John           13
       1          ben           11
       2        alice           15
       3         mark           29
       4         zion           20

DataFrame with 2 columns and 5 rows:
     Idx       height          age
  dtype:        float          int
       0        178.9           13
       1          182           11
       2        162.5           15
       3        201.5           29
       4          198           20

6-3. filter関数(指定カラムの条件検索)

filter関数(指定カラムの条件検索)
import datamancer
import std/times

# 今日までの日数
proc to_days(d: string): int =
  var dd = d.parse("yyyy/MM/dd")
  var i = (now() - dd).inDays
  return (i.int)

let data = """
  x           y            z        d
  1           10          "5"   2024/03/28
  2           11          "6"   2023/04/02
  3           12          "7"   2022/07/22
"""
var df = parseCsvString(data, sep = ' ')

let expDf = df
  .filter(f{c"x" > 1})       # .filter(f{c"x" > 1 and c"y" > 11}) でも結果は同じ
  .filter(f{c"y" > 11})
echo expDf

# to_days関数で現在日付からの日数を算出し、700日より低いカラムを抽出
let expDf2 = df
  .filter(f{to_days(c"d") < 700})
echo expDf2
結果
DataFrame with 4 columns and 1 rows:
     Idx            x            y            z            d
  dtype:          int          int          int       string
       0            3           12            7   2022/07/22

DataFrame with 4 columns and 2 rows:
     Idx            x            y            z            d
  dtype:          int          int          int       string
       0            1           10            5   2024/03/28
       1            2           11            6   2023/04/02

6-4. mutate関数(列の追加と算術計算)

mutate関数は、行毎の算術を行う関数で、一番良く使われる関数である。
以下のプログラムでは、日付の加算と、値の加算を例に記載してみた。

mutate関数(列の追加と算術計算)
import datamancer
import std/times

# 10日加算
proc after_date(d: string): string =
  return (d.parse("yyyy/MM/dd") + 10.days).format("yyyy/MM/dd")

let data = """
      x           y            z        d
      1           10          "5"   2022/03/28
      2           11          "6"   2022/04/02
      3           12          "7"   2022/07/22
"""
var df = parseCsvString(data, sep = ' ')

# mutate算術計算関数で行の値を計算し、列を追加
# また、after_date関数で日付を10日加算した結果を出力
let expDf = df
              .mutate(f{"x+y" ~ `x` + `y`})
              .mutate(f{"x*y" ~ `x` * `y`})
              .mutate(f{"d+10day" ~ after_date(c"d")})  # `d` == c"d"
echo expDf
結果
DataFrame with 7 columns and 3 rows:
     Idx            x            y            z            d          x+y          x*y      d+10day
  dtype:          int          int          int       string        float        float       string
       0            1           10            5   2022/03/28           11           10   2022/04/07
       1            2           11            6   2022/04/02           13           22   2022/04/12
       2            3           12            7   2022/07/22           15           36   2022/08/01

6-5. transmute関数(浮動小数点演算を行う)

transmute関数(浮動小数点演算を行う)
import datamancer
import std/[sequtils, strutils]

# transmuteは、浮動小数点演算処理に使用される
let x = toSeq(0 ..< 5)            # x = @[0, 1, 2, 3, 4]
let y = x.mapIt(sin(it.float))    # sequtilsのmapIt関数はarray/seq属性の値(it=x)を変換する
let y2 = x.mapIt(pow(sin(it.float), 2.0))

let df = toDf(x, y)
let df_tran1 = df.transmute(f{"x"}, f{"y2" ~ c"y" * c"y"})

let df2 = toDf(x, y2)             # 上記と同じ結果になる
echo df_tran1

let df_tran2 = df.transmute(f{"x"}, f{"yFloat" ~ c"y" / 3}) # yカラムを3で割ってるだけ
echo df_tran2
結果
DataFrame with 2 columns and 5 rows:
     Idx            x           y2
  dtype:          int        float
       0            0            0
       1            1       0.7081
       2            2       0.8268
       3            3      0.01991
       4            4       0.5728

DataFrame with 2 columns and 5 rows:
     Idx            x       yFloat
  dtype:          int        float
       0            0            0
       1            1       0.2805
       2            2       0.3031
       3            3      0.04704
       4            4      -0.2523

6-6. summarize関数(集計)

summarize関数(集計)
import datamancer

let data = """
   name     height     age     class
   John     178.9      13      A
   ben      182.0      11      C
   alice    162.5      15      A
   mark     201.5      29      B
   zion     198.0      20      C
   terry    175.2      18      A
   rock     205.1      42      B
   pate     179.4      17      B
   sims     164.8      34      C
"""
var df = parseCsvString(data, sep = ' ')

# 全体の集計処理を行う mean:平均 sum:合計
# ※f{float: "new_clmname"}で、new_clmname列が作成され、列の属性はfloat指定を意味する
var df_sum = df.summarize(f{float: "mean_height" << mean(c"height")},
                  f{float: "sum_age" << sum(c"age")})
echo df_sum

# class毎のmean:平均 sum:合計 max:最大値 min:最小値
# ※max/minを使用する時は、max(col("height"))でないとエラーになる
#   通常、列指定は"new_clmname",c"new_clmname",`new_clmname`で問題ない
var df_sum2 = df.group_by("class")
       .summarize(f{float: "mean_height" << mean(col("height"))},
                  f{float: "max_height" << max(col("height"))},
                  f{float: "min_height" << min(col("height"))},
                  f{float: "sum_age" << sum(`height`)})
echo df_sum2

summarize関数は、mutate関数のように複数回の呼び出しが出来ない。
その代わり、関数内で複数のカラムを追加する事は可能。

結果
DataFrame with 2 columns and 1 rows:
     Idx    mean_height        sum_age
  dtype:          float          float
       0            183            199

DataFrame with 5 columns and 3 rows:
     Idx          class    mean_height     max_height     min_height        sum_age
  dtype:         string          float          float          float          float
       0              A          172.2          178.9          162.5          516.6
       1              B          195.3          205.1          179.4            586
       2              C          181.6            198          164.8          544.8

6-7. arrange関数(指定列で昇順ソート)

arrange関数(指定列で昇順ソート)
let df = toDf({ "x" : @[4, 2, 7, 4], "y" : @[2.3, 7.1, 3.3, 1.0],
                    "z" : @["b", "c", "d", "a"]})
echo df.arrange("x")                               # x列を昇順にソート
echo df.arrange("x", order = SortOrder.Descending) # x列を降順にソート
echo df.arrange(["x", "z"])                        # x,y列でソート
結果
DataFrame with 3 columns and 4 rows:
     Idx            x            y            z
  dtype:          int        float       string
       0            2          7.1            c
       1            4          2.3            b
       2            4            1            a
       3            7          3.3            d

DataFrame with 3 columns and 4 rows:
     Idx            x            y            z
  dtype:          int        float       string
       0            7          3.3            d
       1            4          2.3            b
       2            4            1            a
       3            2          7.1            c

DataFrame with 3 columns and 4 rows:
     Idx            x            y            z
  dtype:          int        float       string
       0            2          7.1            c
       1            4            1            a
       2            4          2.3            b
       3            7          3.3            d

6-8. inner_join関数(集約)

inner_join関数(指定列で昇順ソート)
import datamancer

# 二つの異なるDataFrameから集約を行う
let data = """
   Ident     Ids
   A         1
   B         2
   C         3
   D         4
   E         5
   F         6
"""
var df1 = parseCsvString(data, sep = ' ')

let data2 = """
   Ident     Words
   A         suggest
   B         result
   D         from
   F         to
"""
var df2 = parseCsvString(data2, sep = ' ')

echo df1.innerJoin(df2, by = "Ident")   # Indentで集約
結果
DataFrame with 3 columns and 4 rows:
     Idx        Ident        Words          Ids
  dtype:       object       string          int
       0            A      suggest            1
       1            B       result            2
       2            D         from            4
       3            F           to            6

innerJoin関数によって、Ident列がそれぞれ同じ値が(A,B,D,F)の列を抽出

6-9. setDiff関数(DataFrame同士の異なる値を抽出)

setDiff関数(DataFrame同士の異なる値を抽出)
import datamancer

let data = """
   name     height     age     class
   John     178.9      13      A
   ben      182.0      11      C
   alice    162.5      15      A
   mark     201.5      29      B
   zion     198.0      20      C
   terry    175.2      18      A
   rock     205.1      42      B
   pate     179.4      17      B
   sims     164.8      34      C
"""
var df = parseCsvString(data)
let df2 = df[4 .. 6]  # 4行目から8行目までをコピー

# dfとdf2を比較し、dfにあって、df2ない値を表示する
let res = setDiff(df, df2)
echo res  # 重複しない部分を表示 (1-4,7-8行目が表示される)
結果
DataFrame with 1 columns and 6 rows:
     Idx    name     height     age     class
  dtype:                               string
       0    John     178.9      13      A
       1    ben      182.0      11      C
       2    alice    162.5      15      A
       3    mark     201.5      29      B
       4    pate     179.4      17      B
       5    sims     164.8      34      C

6-10. count関数(列内の同一件数を抽出)

count関数(列内の同一件数を抽出)
import datamancer

let data = """
   name     height     age    class
   John     178.9      13     B
   ben      182.0      11     A
   alice    162.5      15     C
   mark     201.5      11     A
   zion     198.0      20     C
"""
var df = parseCsvString(data, sep = ' ')

let df_class     = df.count("class")
echo df_class.len
echo df_class

let df_age_class = df.count(["age", "class"])
echo df_age_class.len
echo df_age_class
結果
3   <- df_class.len
DataFrame with 2 columns and 3 rows:
     Idx        class            n
  dtype:       string          int
       0            A            2
       1            B            1
       2            C            2

4   <- df_age_class.len
DataFrame with 3 columns and 4 rows:
     Idx          age        class            n
  dtype:          int       string          int
       0           11            A            2
       1           13            B            1
       2           15            C            1
       3           20            C            1

count関数で指定した行名(カラム名)とその合計値しか返さない
また、複数の行名を指定する場合は、array型で渡す。その場合、指定した行名と合計値が返される。

6-11. gather関数(列名毎に、値を行表示)

gather関数(列名毎に、値を行表示)
import datamancer

let data = """
      A       B      C      country
      1       3      5      america
      8       4      7      japan
      6       9      0      china
"""
# 列名(カラム名)毎に、値を行表示
let df = parseCsvString(data, sep = ' ')
let dfRes = df.gather(df.getKeys(),    # 全ての行名(カラム名)をseq表示
                      key = "Class",   # 行名を"Class"に設定し、全ての行名を表示
                      value = "Num")   # 行名を"Num"に設定し、全ての値を表示する
echo dfRes
echo df.getKeys()

※カラム名を限定した場合(["A","B"]だけを指定した場合)は、4カラムで表示される
Class Num C countryの4カラムが表示される

結果
DataFrame with 2 columns and 12 rows:
     Idx        Class          Num
  dtype:       string       object
       0            A            1
       1            A            8
       2            A            6
       3            B            3
       4            B            4
       5            B            9
       6            C            5
       7            C            7
       8            C            0
       9      country      america
      10      country        japan
      11      country        china
@["A", "B", "C", "country"] <- df.getKeys()の結果

Class行名にdf内の全ての行名が表示され、Num行名には、全ての値が表示される。

6-12. spread関数(指定列によって分類される)

gather の逆操作。長い形式のデータ フレームから広い形式のデータ フレームへの変換。
名前は spread ですが、APIはdplyrの新しい pivot_wider にさらに近づけています。

spread関数(指定列によって分類される)
import datamancer

# 3列以上は必須
let data = """
    Class      Num        v
      A        78         1
      A        41         8
      A        120        0
      B        31         3
      B        89         4
      B        68         0
      C        25         5
      C        92         7
      C        12         2
"""
let df = parseCsvString(data, sep = ' ')

# Class毎にカラムが分裂し、対応するNum列として値にはいる vカラムは先頭列になり、昇順にソートされる
# v=0にはAとBに値が入る
let df_sp = df.spread(namesFrom = "Class", valuesFrom = "Num")
echo df_sp

echo "clm size=", $df_sp.ncols # 4列 v A B C
echo "row size=", $df_sp.len
echo df_sp["A", int]
echo df_sp["B", int]
echo df_sp["C", int]
結果
DataFrame with 4 columns and 8 rows:
     Idx            v            A            B            C
  dtype:          int          int          int          int
       0            0          120           68            0
       1            1           78            0            0
       2            2            0            0           12
       3            3            0           31            0
       4            4            0           89            0
       5            5            0            0           25
       6            7            0            0           92
       7            8           41            0            0

clm size=4
row size=8
Tensor[system.int] of shape "[8]" on backend "Cpu"
    120     78      0      0      0      0      0     41
Tensor[system.int] of shape "[8]" on backend "Cpu"
    68     0     0    31    89     0     0     0
Tensor[system.int] of shape "[8]" on backend "Cpu"
    0     0    12     0     0    25    92     0

6-13. unique関数(同一値の集約を行う)

unique関数(同一値の集約を行う)
import datamancer

let df = toDf({ "x" : @[1, 2, 2, 2, 4], 
                "y" : @[5.0, 6.0, 7.0, 8.0, 9.0],
                "z" : @["a", "b", "b", "d", "e"]})

echo df.unique()           # 全ての値を出力
echo df.unique("x")        # x列の値で集約する
echo df.unique(["x", "z"]) # x,y列の値で集約を行う
結果
DataFrame with 3 columns and 5 rows:
     Idx            x            y            z
  dtype:          int        float       string
       0            1            5            a
       1            2            6            b
       2            2            7            b
       3            2            8            d
       4            4            9            e

DataFrame with 3 columns and 3 rows:
     Idx            x            y            z
  dtype:          int        float       string
       0            1            5            a
       1            2            6            b
       2            4            9            e

DataFrame with 3 columns and 4 rows:
     Idx            x            y            z
  dtype:          int        float       string
       0            1            5            a
       1            2            6            b
       2            2            8            d
       3            4            9            e

おわりに

Nim言語も知らない頃から、DataFrameライブラリの記事を書いてしまった事もあり、流石に内容が分かりにくかったので、今回は、少しは読みやすく書き直したつもりです。

Nim言語でも、基本的なDataFrameの利用は可能です。
Python+pandasやRust+polarsのような高機能な部分は足りていませんが、業務で利用するには、事足りるんじゃないかなとは思います。何よりpandasより早いし、polarsよりわかりやすい!

Discussion