The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

R2camtrapdp: スキーマ駆動ワークフロー(日本語)

library(R2camtrapdp)

概要

R2camtrapdp は、任意のスプレッドシート形式で保持したカメラトラップデータを Camera Trap Data Package (Camtrap DP) へ変換します。

本バージョンは スキーマ駆動 です。出力テーブルの構造・型・制約は、指定した Camtrap DP バージョンの公式 Frictionless table schema から読み取られます。 その結果、本パッケージは

旧来のヘルパ関数(create_deployments(), create_media(), create_observations())と R6_CamtrapDP クラスは名前・引数とも従来どおりで、 既存スクリプトはそのまま動作します。スキーマ駆動の挙動はその上に追加されています。

ネットワークについて。 テーブルを登録する(set_deployments() 等)と、 指定バージョンの table schema を初回に GitHub から取得し、以後はキャッシュします。 オフライン環境では local_schema = 引数でローカルのスキーマファイルを渡せます。

データ

本パッケージには、画像記録を伴う複数デプロイメントの例データが同梱されています。

# 複数デプロイメント・画像データ
data("Idep")   # デプロイメント表
data("Iobs")   # 観察表

Idep はデプロイメント(カメラ設置)1 件につき 1 行で、deploymentID, longitude, latitude, locationID, startDate/startTime, endDate/endTime, cameraID, cameraModel, Delay, Height, bait, setupBy などの列を持ちます。Iobs は観察 1 件につき 1 行で、機関/コレクション コード、filename, deploymentID, date/time, obsID, eventID, eventStart/eventEnd, object, genus, species, class, individualCount を持ちます。

1. バージョンの選択とスキーマの確認(任意)

パイプライン全体は選択したバージョンのスキーマで駆動されます。Camtrap DP の 1.0 / 1.0.1 / 1.0.2 はすべて対応しており、これらの table schema は列名・型・ 制約が共通であるため、実質的な差は 1.0.2 が欠損値トークン(NA, NaN, nan) を追加で認識する点のみです。任意バージョンのスキーマは TableSchema で直接確認 できます。

(注: 公式の 1.0 profile(メタデータの JSON Schema)には上流のバグ(不正な内部 $ref)があり、新しい Frictionless が拒否します。このため version = "1.0" を指定 すると警告が表示されます。validate_frictionless() が自動で回避しますが、1.0.1 以降の利用を推奨します。)

version <- "1.0.1"

dep_schema <- TableSchema$new("deployments", version = version)
dep_schema$field_names()           # スキーマが定義する全列
dep_schema$required_field_names()  # 必須(存在かつ非欠損)の列
dep_schema$empty_table()           # 型付き 0 行の「殻」テーブル

通常は手作業で確認する必要はありません(R6_CamtrapDP が適切なスキーマを自動取得・ キャッシュします)が、当該バージョンが何を要求するかの把握に役立ちます。

check_schema() は、スキーマ自体が well-formed な Frictionless Table Schema か (サポートされる type、型ごとに妥当な制約、定義済みフィールドを指す主キー・外部 キー)を確認します。新規・手編集のスキーマを採用する前の点検に有用です。

dep_schema$check_schema()

スキーマ内の外部(URL)参照

Camtrap DP の情報の一部は、機械検証可能な制約ではなく URL で指定されます。 具体的には意味マッピング(skos:exactMatch / broadMatch / narrowMatch による Darwin Core, Audubon Core 等の用語への対応)や、フィールドの description 内の参照 URL(例: fileMediatype の IANA メディアタイプ登録簿、individualSpeed の手法 DOI) です。本パッケージが強制するのは構造化された制約のみで、URL で参照される意味は 検証されません。バージョンや新フレーバーを採用する際にこうした指定を見落とさない よう、次で一覧化できます。

dep_schema$external_references()   # スキーマが宣言する全 URL(skos・description・schema URL)
dep_schema$semantic_only_fields()  # 意味が URL でのみ定義され、値域を機械検証できない列

external_references() は整形済みの表(resource, field, key, category, url)を返します。semantic_only_fields() は、参照先の権威に照らして手動確認が 必要な列を示します。パッケージ全体は datapackage$external_references() で一括 スキャンできます。

2. 3 つの中核テーブルの作成

デプロイメントの作成

デプロイメントデータ(Idep)を用い、従来どおりに作成します。 create_deployments() は結合済みの日時、または日付列・時刻列の別指定の両方を 受け付けます。

deployments <- create_deployments(
  deploymentID         = Idep$deploymentID,
  longitude            = Idep$longitude,
  latitude             = Idep$latitude,
  locationID           = Idep$locationID,
  deploymentStart_date = Idep$startDate,
  deploymentStart_time = Idep$startTime,
  deploymentEnd_date   = Idep$endDate,
  deploymentEnd_time   = Idep$endTime,
  cameraID             = Idep$cameraID,
  cameraModel          = Idep$cameraModel,
  cameraDelay          = Idep$Delay,
  cameraHeight         = Idep$Height,
  baitUse              = Idep$bait,
  setupBy              = Idep$setupBy)

create_deployments() は上記以外に次の引数も受け付けます: deploymentStart / deploymentEnd(結合済み日時。*_date / *_time の組の代わりに使用)、 locationNamecoordinateUncertaintycameraDepthcameraHeight と同時指定 不可)、cameraTiltcameraHeadingdetectionDistancetimestampIssuesfeatureTypehabitatdeploymentGroupsdeploymentTagsdeploymentCommentstz(タイムゾーン、既定 "Asia/Tokyo")。

メディアの作成

# メディア ID
mediaIDi <- paste(Iobs$institutionCode,
                  Iobs$collectionCode,
                  Iobs$locationID,
                  as.numeric(factor(Iobs$filename)),
                  sep = "_")

# ファイル情報
fileName      <- Iobs$filename
filetype      <- tolower(unlist(lapply(strsplit(fileName, "\\."), "[", 2)))
fileMediatype <- paste("image", filetype, sep = "/")
filePublic    <- !grepl("ヒト", fileName)   # ヒト画像は非公開にする

media <- create_media(
  mediaID        = mediaIDi,
  deploymentID   = Iobs$deploymentID,
  timestamp_date = Iobs$date,
  timestamp_time = Iobs$time,
  filePath       = "Image",
  filePublic     = filePublic,
  fileMediatype  = fileMediatype,
  captureMethod  = "activityDetection",
  fileName       = fileName)

create_media() は上記以外に次の引数も受け付けます: timestamp(結合済み日時。 timestamp_date / timestamp_time の代わり)、exifDatafavoritemediaCommentstzomitduplicate(重複 mediaID を除外。既定 TRUE)。

observationの作成

# イベント単位の観察
observationLevel <- "event"

# observationType はスキーマの enum 値のいずれか
observationType <- ifelse(Iobs$object == "hito", "human",
                   ifelse(Iobs$object == "none", "blank",
                   ifelse(Iobs$object == "unidentifiable", "unknown", "animal")))

# 学名
scientificName <- ifelse(is.na(Iobs$genus), Iobs$class, paste(Iobs$genus, Iobs$species))

# 一意の観察 ID
observationID <- paste(mediaIDi, Iobs$obsID, sep = "_")

observations <- create_observations(
  observationID             = observationID,
  deploymentID              = Iobs$deploymentID,
  eventID                   = Iobs$eventID,
  eventStart                = Iobs$eventStart,
  eventEnd                  = Iobs$eventEnd,
  observationLevel          = observationLevel,
  observationType           = observationType,
  scientificName            = scientificName,
  count                     = Iobs$individualCount,
  classificationMethod      = "human",
  classificationProbability = 1)

create_observations() は上記以外に次の引数も受け付けます: mediaIDeventStart_date / eventStart_timeeventEnd_date / eventEnd_time の組 (結合済み eventStart / eventEnd の代わり)、cameraSetupTypelifeStagesexbehaviorindividualIDindividualPositionRadiusindividualPositionAngleindividualSpeedbboxXbboxYbboxWidthbboxHeightclassifiedByclassificationTimestampobservationTagsobservationCommentstzomitduplicate

3. データパッケージの組み立て

R6 オブジェクトの作成(バージョン指定)

datapackage <- R6_CamtrapDP$new(version = "1.0.1")

ここで与える version が、検証および datapackage.json に使われるスキーマを 選択します。別の Camtrap DP リリースを対象にする場合は変更してください。

テーブルの登録(スキーマ検証付き)

set_deployments(), set_media(), set_observations() は名前を維持しつつ、 各々がスキーマ型への変換と、選択バージョンのスキーマに対する検証を行います。 問題は要約として表示され、validate = FALSE で表示を抑制できます。

datapackage$set_deployments(deployments)
datapackage$set_media(media)
datapackage$set_observations(observations)

(スキーマの取得、ファイル書き出し、分類検索、Python 呼び出しを伴うチャンクは、 本 vignette のビルド時には表示のみで実行されません。そのため出力は表示されません。)

検証の要約は、各問題について、ファイル・列・行・違反した規則・メッセージを示します。 例えば enum に反する値、minimum/maximum の範囲外の数値、必要な書式に一致しない 日時などです。列の型にすら適合しない値(例: number 列の非数値文字列)は、無言で NA に変換されるのではなく type エラーとして報告されます。

テーブル間リレーションの検査

外部キー(例: media.deploymentIDdeployments に、observations.mediaIDmedia に存在する必要がある)と主キーの一意性は、各テーブルのスキーマから読み取られ、 登録済みのテーブル間で検査されます。

datapackage$check_relations()

主キーまたは必須の外部キー列が、格納後のテーブルでまるごと欠損している場合 (多くは列名不一致で型変換が NA 補完したケース)、check_relations() は警告を出し データを指し示します(例: datapackage$data$observations has 'deploymentID' entirely missing ...)。 datapackage$data$<resource> を直接確認できます。

4. メタデータ

Camtrap DP は 5 つの必須メタデータ(contributors, project, spatial, temporal, taxonomic)と created を必要とします。さらに 6 つの任意プロパティがあります。 メタデータ関数は従来版から変更ありません。

どのメタデータが必須かを profile で確認する

必須メタデータ自体は、パッケージ profile(JSON Schema)から読み取られます。 metadata_requirements() は必須トップレベルプロパティ・それを設定するメソッド・ 現在の設定状況を一覧化し、check_metadata() は現在のオブジェクトを profile に 照合して不足(project.samplingDesign のようなネストしたキーを含む)を報告します。

datapackage$metadata_requirements()   # チェックリスト: property, required, set_with, currently_set
datapackage$check_metadata()          # 不足している必須メタデータを報告

これは Frictionless が行うメタデータ(profile)検証(§6)に相当する R 側の事前 チェックで、パッケージを書き出して Python を呼ぶ前に必須構造を確認できます。

必須メタデータ

貢献者(Contributors)

add_contributors()title, email, path, role, organization 列を持つ データフレームを取り込みます。rolecontact, principalInvestigator, rightsHolder, publisher, contributor のいずれかです。

cd <- data.frame(
  title        = c("Keita Fukasawa", "Kana Terayama"),
  email        = c("fukasawa@nies.go.jp", "terayama.kana@nies.go.jp"),
  path         = c("https://orcid.org/0000-0003-0272-9180",
                   "https://orcid.org/0000-0001-6935-7233"),
  role         = c("contact", "principalInvestigator"),
  organization = c("National Institute for Environmental Studies (NIES)",
                   "National Institute for Environmental Studies (NIES)"))
datapackage$add_contributors(cd)

プロジェクト(Project)

datapackage$set_project(
  title            = "DummyData",
  samplingDesign   = "simpleRandom",
  captureMethod    = "activityDetection",
  individualAnimals = FALSE,
  observationLevel = "event")

samplingDesignsimpleRandom, systematicRandom, clusteredRandom, experimental, targeted, opportunistic のいずれか、captureMethodactivityDetection または timeLapseobservationLevelmedia または event です。任意で id, acronym, description, path も指定できます。

空間・時間(Spatial and temporal)

set_st() はデプロイメントから空間・時間範囲を導出するため、 set_deployments() の後に呼び出します。

datapackage$set_st()

分類(Taxonomic)

set_taxon() は観察の一意な scientificName を列挙し、taxonID, taxonRank と 上位分類を分類データベース(既定は gbifitis / ncbi も可。taxadb::get_ids 参照)から取得します。CamtrapDP の taxonomic には taxonID(GBIF/IUCN 等の識別子・ URI)が必要なため、taxadb は R2camtrapdp の必須依存(パッケージと共に導入)です。 本処理にはインターネット接続も必要です。

datapackage$set_taxon()

マッチできない名前は taxonID = NA(出力では省略。<uri>NA のような不正値にはなりません)。 set_taxon() は、scientificName に不要な空白がある場合と、選択した DB に taxonID が 無い場合に警告を出すので、対象の名前を修正・確認できます。

作成日時(Created)

datapackage$update_created(tz = "Asia/Tokyo")

任意メタデータ

ライセンス(Licenses)

Camtrap DP はデータ用とメディア用に各 1 つ以上のライセンスを想定します。

datapackage$add_license(name = "CC-BY-4.0",
                        path = "http://creativecommons.org/licenses/by/4.0/",
                        scope = "data")
datapackage$add_license(name = "CC-BY-4.0",
                        path = "http://creativecommons.org/licenses/by/4.0/",
                        scope = "media")

関連識別子(Related identifiers)

datapackage$add_relatedIdentifiers(
  relationType          = "IsSupplementTo",
  relatedIdentifier     = "https://doi.org/xxxx",
  relatedIdentifierType = "DOI",
  resourceTypeGeneral   = "JournalArticle")

プロパティ・ソース・参考文献

datapackage$set_properties(
  name     = "dummy-nies",
  homepage = "https://www.nies.go.jp/biology/snapshot_japan/index.html")
datapackage$add_sources(title = "DummyData")
datapackage$add_references(reference = "DummyNIES https://doi.org/xxxxx")

カスタムリソース

set_custom() は追加のリソース(例: 個体数推定に使うデータ)をメタデータとして 付加します。3 つの中核テーブルを登録した後に呼び出します。

RD <- data.frame(id = seq_len(388), Time = sample(1:29, 388, replace = TRUE))
datapackage$set_custom(name = "rest",
                       description = "data for the REST method",
                       data = RD)

5. データパッケージの出力

# camtrapdp オブジェクトを返す
data_camtrapdp <- datapackage$out_camtrapdp()

# あるいは deployments.csv / media.csv / observations.csv + datapackage.json を書き出す
datapackage$out_camtrapdp(write = TRUE, directory = path)

書き出し時、CSV は全スキーマ列を含み、boolean は true/false(小文字)で出力され、 未設定のメタデータは省略されるため、空のプレースホルダが偽の検証エラーを引き起こす ことはありません。

6. Frictionless による検証

適合の事前チェック(Python を呼ぶ前に)

Python を実行する前に、パッケージがそもそも well-formed な Frictionless データ パッケージか、また CamtrapDP 形式かを R 側で確認できます。Frictionless が行う構造 チェックを R で再現するもので、新規・特殊なスキーマの問題を早期に発見できます。

datapackage$check_descriptor()        # 記述子+各 table schema の構造(Frictionless 仕様)
datapackage$check_camtrap_profile()   # profile が CamtrapDP でなければ警告

パッケージは Frictionless として妥当でも、CamtrapDP 形式とは限りません。 それは profile が CamtrapDP プロファイル(既定)かどうかで決まります。GeoJSON 妥当性や物理ファイル構造を含む権威ある判定は、下記の Frictionless 実行が担います。

Frictionless の実行

書き出したパッケージを公式スキーマに照らして、Python の Frictionless で確認できます。frictionless を導入した Python が必要です (pip install frictionless)。

issues <- datapackage$validate_frictionless(directory = path, python = "python")
ctdp_is_valid(issues)   # error が無ければ TRUE

注意 — path を上書きします。 validate_frictionless() は既定で write = TRUE のため、検証前に out_camtrapdp() を呼んで directory 内の datapackage.json と CSV を現在のオブジェクトの内容で上書きします。すでに ディスク上にある datapackage を上書きせず検証だけしたい場合は write = FALSE、 または R6 オブジェクト不要の検証専用関数を使ってください:

ctdp_validate_frictionless("path/to/existing/package", python = "python")

issues は問題 1 件につき 1 行の整形済み表で、source(ファイル)、field(列/ プロパティパス)、row(行)、違反した constraint、問題の value(実値)、 message を与えるため、エラー箇所を正確に把握できます。セルエラーの value は不正な セル値、メタデータ(profile)エラーの value は note 内のプロパティパス(例 contributors[].email)で datapackage.json を辿って解決した実値です。R 側のスキーマ検査・リレーション検査・メタデータ(profile)検査・ 適合事前チェック・(任意で)Frictionless レポートは、1 回の呼び出しで集約する こともできます。

datapackage$validate(relations = TRUE, metadata = TRUE, conformance = TRUE,
                     frictionless = TRUE, directory = path, python = "python")

7. 任意のスプレッドシートを直接変換する

上記のヘルパは変数を事前に命名済みであることを前提とします。独自の列名を持つ生の スプレッドシートがある場合は、ctdp_build_table() でマッピング・日時結合・型変換・ 検証を一括実行できます(任意バージョン対応)。

version    <- "1.0.1"
dep_schema <- TableSchema$new("deployments", version = version)

# 任意の列名+独自列を持つ生データの例
raw <- data.frame(
  station   = c("A01", "A02"),
  lat       = c(35.1, 36.2),
  lon       = c(139.5, 140.1),
  start_day = c("2023-04-01", "2023-04-02"),
  start_clk = c("09:00:00", "10:30:00"),
  end_day   = c("2023-05-01", "2023-05-02"),
  end_clk   = c("09:00:00", "10:30:00"),
  myNote    = c("独自列として保持", "これも保持"),
  stringsAsFactors = FALSE)

# マッピング:生データで使用されている列名とCamtrapDPのフィールド名との関係(マッピング)を作成
mapping <- c(station = "deploymentID", lat = "latitude", lon = "longitude")

built <- ctdp_build_table(
  dep_schema, raw, mapping = mapping,
  datetime_merges = list(
    list(date_col = "start_day", time_col = "start_clk", target = "deploymentStart"),
    list(date_col = "end_day",   time_col = "end_clk",   target = "deploymentEnd")))

ctdp_summarize_validation(built$issues)   # スキーマ上の問題
datapackage$set_deployments(built$data)   # 結果をパッケージへ投入

myNote のような独自列は保持されます。パッケージ書き出し時、独自列は datapackage.json 内のインライン拡張スキーマとして宣言されるため、Frictionless が 受理します。

8. 別のスキーマフレーバー(例: bioacoustics)

各テーブルは指し示したスキーマで駆動されるため、本パッケージは TDWG がホストする カメラトラップのスキーマに限定されません。別フレーバー——例えば Camtrap DP の bioacoustics 拡張——を対象にする には、テーブルおよび profile の URL を明示します。これらのスキーマは別リポジトリに あり、独自の列構成(例: cameraID の代わりに deviceID、さらに samplingFrequency, frequencyLow/frequencyHigh など)と、テーブルごとに異なる 日時書式(media / observations のイベント時刻は小数秒付き %Y-%m-%dT%H:%M:%S.%f%zdeployments の時刻は小数秒なし)を用います。スキーマ駆動 の検証はこれらすべてに自動的に適応します。生データの media / observations の タイムスタンプに小数秒が無い場合は、スキーマの %f 書式に合うよう .000 を自動 付与します。

set_properties() でフレーバーを一度指定すれば、あとは通常どおりテーブルを追加 できます(set_*() は設定済みの schema_urls を使うため、各呼び出しに schema = を渡す必要はありません)。

ba <- "https://raw.githubusercontent.com/camera-traps/bioacoustics/main/camtrap-dp/1.0.2/%s"

dp <- R6_CamtrapDP$new(version = "1.0.2")
dp$set_properties(
  version     = "1.0.2",
  profile     = sprintf(ba, "camtrap-dp-profile-acoustic.json"),
  schema_urls = list(
    deployments  = sprintf(ba, "deployments-table-schema-acoustic.json"),
    media        = sprintf(ba, "media-table-schema-acoustic.json"),
    observations = sprintf(ba, "observations-table-schema-acoustic.json")))

# 音響のタイムスタンプは、音響スキーマの書式に合わせて小数秒を付与する
dp$set_media(data.frame(
  mediaID = "m1", deploymentID = "D1",
  timestamp = "2023-04-01T09:05:00.000+0900",
  filePath = "audio/m1.wav", filePublic = TRUE, fileMediatype = "audio/wav",
  samplingFrequency = 48000L, channels = 1L,
  stringsAsFactors = FALSE))

カメラトラップの列を音響フレーバーへ対応づける

マッピングが必要なのは、名前が音響フィールドと異なる列だけです。すでに音響 フィールド名と同じ列(deploymentID, latitude, deploymentStart など)は自動的に 対応づけられ、マッピングは不要です。deployments では、カメラトラップの camera* 列が device* に改名され、カメラ専用列は音響に対応が無いため除外し、音響固有の列は データがあれば設定します。

library(dplyr)

# カメラトラップ deployments -> 音響 deployments(改名が必要な列だけ)
mapping <- c(
  cameraID      = "deviceID",
  cameraModel   = "deviceModel",
  cameraDelay   = "deviceDelay",
  cameraHeight  = "deviceHeight",
  cameraDepth   = "deviceDepth",
  cameraTilt    = "deviceTilt",
  cameraHeading = "deviceHeading")

dep_acoustic <- camtrap_deployments %>%
  select(-any_of(c("featureType", "timestampIssues")))   # カメラ専用: 音響に対応列なし

dp$set_deployments(dep_acoustic, mapping = mapping)

列の対応 — deployments:

カメラトラップの列 音響の列 対応
deploymentID, locationID, locationName, latitude, longitude, coordinateUncertainty, deploymentStart, deploymentEnd, setupBy, detectionDistance, baitUse, habitat, deploymentGroups, deploymentTags, deploymentComments 同名 マッピング不要
cameraID / cameraModel / cameraDelay / cameraHeight / cameraDepth / cameraTilt / cameraHeading deviceID / deviceModel / deviceDelay / deviceHeight / deviceDepth / deviceTilt / deviceHeading マップ
featureType, timestampIssues 除外
elevation, devicePlatform, recordingSchedule, locationType 音響固有(あれば設定)

observations で改名が必要なのは cameraSetupTypedeviceSetupType のみです (音響は frequencyLow / frequencyHigh / classificationConfirmation も追加)。 media に改名はなく、追加フィールド(duration, bitDepth, samplingFrequency, gain, channels)のみです。

フレーバーのスキーマも、他のスキーマと同様に確認できます。なお TableSchema$new("deployments", version = "1.0.2")url_template なしで 呼ぶとカメラトラップ用の deployments スキーマが読み込まれます。音響の要件を確認 するには音響 URL を渡してください。requirements() は各フィールドの型・書式・制約を 表で返します。

acoustic_dep <- TableSchema$new(
  "deployments", version = "1.0.2",
  url_template = sprintf(ba, "deployments-table-schema-acoustic.json"))
acoustic_dep$field_names()
acoustic_dep$required_field_names()
acoustic_dep$requirements()        # field / type / format / required / enum / min / max / pattern
acoustic_dep$external_references()

create_deployments(), create_media(), create_observations() はカメラ トラップのスキーマに合わせて作られています。別フレーバー(または将来版の新規列) では、create_*() ではなくスキーマ駆動の経路(ctdp_build_table() か、カスタム schema = を渡した set_*() メソッド)でテーブルを構築してください。

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.