F# Data


F# Data: Freebase プロバイダー

Freebase グラフデータベース には 2,300万件以上の情報が格納されています。 この中からは書籍や映画、歴史的人物や出来事、化学元素など、 ありとあらゆる情報が互いに関連を持った形で見つけられます。

Freebase 型プロバイダーを使うと、こういった情報を手軽に取ってきて、 厳密に型付けされた状態でデータの山から宝物を見つけ出すことができます。

この型プロバイダーは Try F# の サイトにある「Data Science」のチュートリアルでも使われています。 そのためそちらも是非参考にしてみてください。 Visual Studio F#チームのブログではこの型プロバイダーについて 4つの記事が こちら に掲載されています。 また、Don Syme氏による動画デモが こちら で見られます。

プロバイダーの基本

以下のコードでは FSharp.Data.dll ライブラリを(F# Interactive上で)読み込み、 GetDataContext メソッドを使ってFreebaseへの接続を初期化しています:

1: 
2: 
3: 
4: 
#r "../../../../bin/FSharp.Data.dll"
open FSharp.Data

let data = FreebaseData.GetDataContext()

Freebaseデータの探索

さてこれで data. と入力して表示される自動コンプリートの リストをチェックするとFreebaseのデータスキーマを探索することができます。 たとえば以下のコードでは化学元素(Chemical Elements)のデータを取得して 水素(Hydrogen)の詳細情報を表示させています:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let elements = data.``Science and Technology``.Chemistry.``Chemical Elements``

let all = elements |> Seq.toList
printfn "見つかった元素の数: %d" (Seq.length all)

let hydrogen = elements.Individuals.Hydrogen
printfn "原子番号: %A" hydrogen.``Atomic number``

テストケースの生成

Freebaseには非常に多様なデータがあるため、 ありとあらゆる用途にこのデータベースを利用できます。 以下のコードではデータベース上の婚姻データを使って それらしい名前をテスト用に生成しています。 まず2つの配列を用意します。 1つは(婚姻データを元にした)100件の名、そしてもう1つは (Freebaseに登録されている名字リストを元にした)100件の姓を含んでいます:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
let firstnames = 
    data.Society.Celebrities.Celebrities
    |> Seq.truncate 100
    |> Seq.map (fun celeb -> celeb.Name.Split([|' '|]).[0])
    |> Array.ofSeq

let surnames = 
    data.Society.People.``Family names``
    |> Seq.truncate 100
    |> Seq.map (fun name -> name.Name)
    |> Array.ofSeq

それらしいテストケース用のデータを生成するためには これらの配列からランダムに要素を抽出するヘルパ関数を用意して、 ランダム取り出した名と姓を連結します:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let randomElement = 
    let random = new System.Random()
    fun (arr : string[]) -> arr.[random.Next(arr.Length)]

for i in 0 .. 10 do
  let name = 
    (randomElement firstnames) + " " +
    (randomElement surnames)
  printfn "%s" name

Freebaseのデータにクエリを投げる

先ほどの例ではFreebase 型プロバイダーから返されたコレクションを処理するために Seq 関数を使っていました。 単純な場合にはこれでいいのですが、そうではなくデータをフィルタしたり クエリを投げたりする必要がある場合にはこのままではうまくいきません。

そこでFreebase プロバイダーにはクエリをサポートするための機能が用意されています。 F# 3.0のLINQシンタックスで記述されたクエリは (Freebaseで使われているクエリ言語である)MQLに変換されます。 これはつまりF# 3.0であれば自動補完のサポートも有効で、 厳密に型付けされた方法でクエリを作成できる上に、 少なくともMQLへと変換されたクエリはFreebaseのサーバー上で 効率的に処理されるというわけです。

以下では地球からの距離とあわせて恒星の名前を取得しています (距離が不定のデータは除きます):

1: 
2: 
3: 
4: 
5: 
6: 
let astronomy = data.``Science and Technology``.Astronomy

query { for e in astronomy.Stars do 
        where e.Distance.HasValue
        select (e.Name, e.Distance) } 
      |> Seq.toList

例では簡単のために、まず恒星に関するデータを astronomy という名前で定義しています。 また、クエリを実際に実行するために、最後に Seq.toList を呼び出す必要もあります。

以下のクエリは距離が分かっていて、地球に近い恒星のデータを返します:

1: 
2: 
3: 
4: 
query { for e in astronomy.Stars do 
        where (e.Distance.Value < 4.011384e+18<_>)
        select e } 
      |> Seq.toList

クエリ言語では単純な whereselect 以外にも、 様々な演算子がサポートされています。 たとえば地球からの距離でソートした後、近い距離にある恒星を 上位10個取得することもできます:

1: 
2: 
3: 
4: 
5: 
query { for e in astronomy.Stars do 
        sortBy e.Distance.Value
        take 10
        select e } 
      |> Seq.toList

Freebase クエリ演算子

F# 3.0のクエリ演算子の他に、 FSharp.Data.FreebaseOperators 名前空間には ApproximatelyMatches ApproximatelyOneOf ApproximateCount Count といったFreebase固有の演算子が定義されています。 これらはそれぞれ固有のMQL演算子に変換されます。

たとえば以下のコードでは CountApproximateCount を使って 歴代のアメリカ大統領の人数を数えています (今回の場合、正確な人数を数えれば十分なので ApproximateCount は あまり有効ではありません):

1: 
2: 
3: 
4: 
open FSharp.Data.FreebaseOperators

data.Society.Government.``US Presidents``.Count()
data.Society.Government.``US Presidents``.ApproximateCount()

たとえば文字列を扱う場合には ApproximatelyMatches を使うとよいでしょう。 以下では特定の文字列に およそ一致する 書籍を検索しています:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let topBooksWithNameContaining (s:string) = 
    query { for book in data.``Arts and Entertainment``.Books.Books do
            where (book.Name.ApproximatelyMatches s)
            take 10 
            select book.Name }
 
topBooksWithNameContaining "1984" |> Seq.toList

プロバイダーの詳細機能

Freebase 型プロバイダーには非常に多くの機能があるため、 それらのすべてをここで紹介することはできません。 一部の機能については既に紹介しましたが、より詳しいドキュメントについては このページからリンクしてある記事を参照してください。 簡単に紹介すると以下のような機能があります:

  • 多くのクエリは効率よくMQL言語へと変換されます。 これらはデフォルトではクライアントサイドでは実行できません。
  • 特定のサンプルについては Individuals 以下にあるオブジェクトの 各コレクションから取得できます。 この機能を使うとたとえば Hydrogen (水素)や Bob Dylan といった 厳密な名前をつかって特定のデータをプログラム上で取得することができます。
  • Freebase上のデータに対して大まかな数を計算したり、 文字列を大まかに一致させるための独自の演算子がサポートされています。
  • 画像のURLは GetImages() メソッドで取得できます。 また、1番目の画像は MainImage プロパティで取得できます。
  • Freebaseの日付によるスナップショットをサポートしています。 つまり特定の日付におけるFreebaseデータベースの状態を参照できます (また、スキーマが変更されない限りはアプリケーションが 壊れないということでもあります)。
  • スキーマ情報をクライアントサイドでキャッシュするというオプションの機能を 使うことによって、型を迅速かつ効果的にチェックできます。
  • 測定単位をサポートします。 たとえば化学元素の Atomic mass (原子質量)プロパティは自動的にSI単位である キログラムへと変換されます。 この情報は測定単位を使ってF#の型システム上で静的に伝搬されます。
  • 大量のFreebaseデータに対してクエリを投げたい場合、 Googleで登録した後にカスタムAPIキーを取得します。 このキーは型プロバイダーのstatic引数で指定できます。

APIキーを指定する

FreebaseのAPIにはリクエスト数の制限があり、 当初はデバッグ用にある程度の割り当て分しか使うことが出来ません。 もしも(403) Forbiddenエラーが出たのであれば、それはつまり リクエスト数の上限に達したということです。 Freebaseサービスを有効化するためにはAPIキーが必要です。 そうすれば1日あたり100,000件のリクエストを送信できるようになります。 F# Data ライブラリには FreebaseDataProvider という型があり、 この型にはAPIキーなど、いくつかのstatic引数を指定することができます:

1: 
2: 
3: 
4: 
5: 
[<Literal>]
let FreebaseApiKey = "<freebaseを有効にするgoogle API キーをここに入力>"

//type FreebaseDataWithKey = FreebaseDataProvider<Key=FreebaseApiKey>
//let dataWithKey = FreebaseDataWithKey.GetDataContext()

MQLクエリのデバッグ

Freebase 型プロバイダーの仕組みを知りたい場合、 あるいはパフォーマンスの問題をデバッグしたい場合には プロバイダーがFreebaseに送信するリクエストを確認するとよいでしょう。 そのためには SendingRequest イベントに登録します:

1: 
2: 
3: 
4: 
5: 
data.DataContext.SendingRequest.Add (fun e -> 
  printfn "request: %A" e.RequestUri)

data.``Science and Technology``.Chemistry.
     ``Chemical Elements``.Individuals.Hydrogen.``Atomic mass``.Mass

関連する記事

namespace FSharp
namespace FSharp.Data
val data : FreebaseData.ServiceTypes.FreebaseService

Full name: Freebase.data
type FreebaseData =
  static member GetDataContext : unit -> FreebaseService
  nested type ServiceTypes

Full name: FSharp.Data.FreebaseData


<summary>Typed representation of Freebase data. See http://www.freebase.com for terms and conditions.</summary>
FreebaseData.GetDataContext() : FreebaseData.ServiceTypes.FreebaseService
val elements : FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementDataCollection

Full name: Freebase.elements
val all : FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementData list

Full name: Freebase.all
module Seq

from Microsoft.FSharp.Collections
val toList : source:seq<'T> -> 'T list

Full name: Microsoft.FSharp.Collections.Seq.toList
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val length : source:seq<'T> -> int

Full name: Microsoft.FSharp.Collections.Seq.length
val hydrogen : FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementDataIndividuals.Hydrogen Item

Full name: Freebase.hydrogen
property FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementDataCollection.Individuals: FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementDataIndividuals


<summary>A sample set of named individuals (up to 1000 in size) of type &apos;Chemical Element&apos; in the web data store</summary>
property FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementDataIndividuals.Hydrogen: FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementDataIndividuals.Hydrogen Item


<summary>Hydrogen ( /ˈhaɪdrɵdʒɨn/ HY-drə-jin) is the chemical element with atomic number 1. It is represented by the symbol H. With an average atomic weight of 1.00794 u (1.007825 u for hydrogen-1), hydrogen is the lightest element and its monatomic form (H1) is the most abundant chemical substance, constituting roughly 75% of the Universe&apos;s baryonic mass. Non-remnant stars are mainly composed of hydrogen in its plasma state.
At standard temperature and pressure, hydrogen is a colorless, odorless, tasteless, non-toxic, nonmetallic, highly combustible diatomic gas with the molecular formula H2. Naturally-occurring atomic hydrogen is rare on Earth because hydrogen readily forms covalent compounds with most elements and is present in the water molecule and in most organic compounds. Hydrogen plays a particularly important role in acid-base chemistry with many reactions exchanging protons between soluble molecules.
In ionic compounds, it can take a negative charge (an anion known as a hydride and written as H), or as a positively charged species H. The latter cation is written as though composed of a bare proton, but in reality, hydrogen cations in ionic compounds always occur as more complex </summary>
val firstnames : string []

Full name: Freebase.firstnames
property FreebaseData.ServiceTypes.FreebaseService.Society: FreebaseData.ServiceTypes.DomainObjects.Society


<summary>Contains the objects of the domain category &apos;Society&apos; defined in the web data store organized by type</summary>
property FreebaseData.ServiceTypes.DomainObjects.Society.Celebrities: FreebaseData.ServiceTypes.DomainObjects.CelebritiesDomain


<summary></summary>
property FreebaseData.ServiceTypes.DomainObjects.CelebritiesDomain.Celebrities: FreebaseData.ServiceTypes.Celebrities.Celebrities.CelebrityDataCollection


<summary>A celebrity is a widely-recognized or famous person who commands a high degree of public and media attention. A celebrity is a widely-recognized or famous person who commands a high degree of public and media attention. Do not include non-celebrities in this type. </summary>
val truncate : count:int -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.truncate
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val celeb : FreebaseData.ServiceTypes.Celebrities.Celebrities.CelebrityData
property Runtime.Freebase.IFreebaseObject.Name: string
System.String.Split(params separator: char []) : string []
System.String.Split(separator: string [], options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], count: int) : string []
System.String.Split(separator: string [], count: int, options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], count: int, options: System.StringSplitOptions) : string []
module Array

from Microsoft.FSharp.Collections
val ofSeq : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Array.ofSeq
val surnames : string []

Full name: Freebase.surnames
property FreebaseData.ServiceTypes.DomainObjects.Society.People: FreebaseData.ServiceTypes.DomainObjects.PeopleDomain


<summary> The people commons is a collection of common types that describe people in the system. The properties of person should be common across all people, i.e. birth date, birth place, relatives. People will often carry other types as well, such as author, basketball player, or recording artist. Properties that are particular to those pursuits should be included in those types, not in the person type. Similarly, it is recommend that &apos;birth date&apos; and properties of the person schema not be replicated in other Types. Instead, &apos;person&apos; should be an &apos;included type&apos; for these types, which means that when a topic is typed &apos;author&apos; it will also be automatically typed &apos;person&apos; and the properties of person will be added as well. </summary>
val name : FreebaseData.ServiceTypes.People.People.Family_nameData
val randomElement : (string [] -> string)

Full name: Freebase.randomElement
val random : System.Random
namespace System
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit
  member NextDouble : unit -> float

Full name: System.Random

--------------------
System.Random() : unit
System.Random(Seed: int) : unit
val arr : string []
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
System.Random.Next() : int
System.Random.Next(maxValue: int) : int
System.Random.Next(minValue: int, maxValue: int) : int
property System.Array.Length: int
val i : int32
val name : string
val astronomy : FreebaseData.ServiceTypes.DomainObjects.AstronomyDomain

Full name: Freebase.astronomy
val query : Linq.QueryBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.query
val e : FreebaseData.ServiceTypes.Astronomy.Astronomy.StarData
property FreebaseData.ServiceTypes.DomainObjects.AstronomyDomain.Stars: FreebaseData.ServiceTypes.Astronomy.Astronomy.StarDataCollection


<summary>A star is really meant to be a single stellar object, not just something that looks like a star from earth. However, in many cases, other objects, such as multi-star systems, were originally thought to be stars. Because people have historically believed these to be stars, they are type as such, but they are also typed as what we now know them to be. </summary>
custom operation: where (bool)

Calls Linq.QueryBuilder.Where
property FreebaseData.ServiceTypes.Astronomy.Astronomy.Celestial_objectData.Distance: System.Nullable<float<Data.UnitSystems.SI.UnitNames.metre>>


<summary>The best approximated measurement from the earth to the object in parsecs.</summary>
property System.Nullable.HasValue: bool
custom operation: select ('Result)

Calls Linq.QueryBuilder.Select
property System.Nullable.Value: float<Data.UnitSystems.SI.UnitNames.metre>
custom operation: sortBy ('Key)

Calls Linq.QueryBuilder.SortBy
custom operation: take (int)

Calls Linq.QueryBuilder.Take
module FreebaseOperators

from FSharp.Data
property FreebaseData.ServiceTypes.DomainObjects.Society.Government: FreebaseData.ServiceTypes.DomainObjects.GovernmentDomain


<summary>The government domain is for all things related to the people and institutions that make up a government. It includes political parties, politicians, elections, government offices, electoral districts, etc., and covers all levels of government, from local to national.The types in this domain have been designed so that they can be used for all types of government, including (but not limited to) US-style federal republics, Westminster-style parliamentary systems, Chinese-style communist governments, and monarchies. Please let us know if you come across a system that doesn&apos;t seem to fit the model, so that we can try to accomodate it.For information about entering data for politicians, elected officials, and other public servants, see Entering information about electioned officials and public servants. The government domain is for all things related to the people and institutions that make up a government. It includes political parties, politicians, elections, government offices, electoral districts, etc., and covers all levels of government, from local to national.The types in this domain have been designed so that they can be used for all types of government, including (but not limited to) US-style federal republics, Westminster-style parliamentary systems, Chinese-style communist governments, and monarchies. Please let us know if you come across a system that doesn&apos;t seem to fit the model, so that we can try to accomodate it.For information about entering data for politicians, elected officials, and other public servants, see Entering information about elected officials and public servants. </summary>
val topBooksWithNameContaining : s:string -> System.Linq.IQueryable<string>

Full name: Freebase.topBooksWithNameContaining
val s : string
val book : FreebaseData.ServiceTypes.Book.Book.BookData
member System.String.ApproximatelyMatches : _pat:string -> bool
Multiple items
type LiteralAttribute =
  inherit Attribute
  new : unit -> LiteralAttribute

Full name: Microsoft.FSharp.Core.LiteralAttribute

--------------------
new : unit -> LiteralAttribute
val FreebaseApiKey : string

Full name: Freebase.FreebaseApiKey
property Runtime.Freebase.FreebaseDataContext.DataContext: Runtime.Freebase.FreebaseDataContextSettings
event Runtime.Freebase.FreebaseDataContextSettings.SendingRequest: IEvent<Handler<Runtime.Freebase.FreebaseSendingRequestArgs>,Runtime.Freebase.FreebaseSendingRequestArgs>
member System.IObservable.Add : callback:('T -> unit) -> unit
val e : Runtime.Freebase.FreebaseSendingRequestArgs
property Runtime.Freebase.FreebaseSendingRequestArgs.RequestUri: System.Uri
Fork me on GitHub