【问题标题】:How to store data like Freebase does?如何像 Freebase 一样存储数据?
【发布时间】:2012-01-28 05:17:17
【问题描述】:

我承认这基本上是Use freebase data on local server? 的重复问题,但我需要比那里已经给出的更详细的答案

我已经完全爱上了 Freebase。我现在想要的是创建一个非常简单的 Freebase 克隆,用于存储可能不属于 Freebase 本身但可以使用 Freebase 模式描述的内容。本质上,我想要的是一种简单而优雅的方式来存储像 Freebase 本身那样的数据,并且能够在 Python (CherryPy) Web 应用程序中轻松使用该数据。

MQL ​​参考指南第 2 章指出:

作为 Metaweb 基础的数据库与您可能熟悉的关系数据库根本不同。关系型数据库以表格的形式存储数据,但Metaweb 数据库将数据存储为节点图以及这些节点之间的关系。

我猜这意味着我应该使用三元存储或图形数据库,例如 Neo4j?这里有没有人有使用 Python 环境中的其中一种的经验?

(到目前为止,我实际上尝试的是创建一个能够轻松存储 Freebase 主题的关系数据库模式,但我在配置 SQLAlchemy 中的映射时遇到了问题)。

我正在调查的事情

更新 [28/12/2011]:

我在 Freebase 博客上找到了一篇文章,描述了 Freebase 自己使用的专有元组存储/数据库(图表):http://blog.freebase.com/2008/04/09/a-brief-tour-of-graphd/

【问题讨论】:

  • 鉴于第一句话,正确的做法是在原始问题上悬赏。
  • 好吧,AFAIK 作为第三人,您无法创建赏金并详细说明迄今为止在现有答案中缺少哪些信息。所以只是把赏金放在那里不会很有用。

标签: python database-design rdf freebase triplestore


【解决方案1】:

看看https://cayley.io。我相信它是由同一作者编写的,并且使用与 Freebase 的后端 graphd 相同的原理,在 Google 杀死它之前。

关于数据,您可能需要运行 this 之类的东西来清理 the Freebase DB dumps 或使用 datahub。

【讨论】:

    【解决方案2】:

    对于 freebase 转储用户来说,一个好消息是 Freebase 现在提供 RDF 转储:http://wiki.freebase.com/wiki/Data_dumps。它是海龟格式,因此可以非常方便地使用为 RDF 设计的任何图形数据库。

    我的建议也是 4store: http://4store.org/ 。它简单易用。 您可以使用 http 请求来执行 SPARQL 操作。

    我的项目中有一个棘手的问题是“。” Freebase 转储中使用的(表示缩短 URL)无法被 4store 识别。所以我在所有包含“。”的列中添加一个括号“”。并自己处理缩短的 URL。

    【讨论】:

      【解决方案3】:

      这是我另一个答案的额外代码。肉在 edb.py 中。从 Python 控制台运行并按照示例进行操作。或者使用 web2py 控制器并在浏览器中运行。

      将此保存为 edb.py:

      import MySQLdb
      import sys
      
      connection = MySQLdb.connect (host   = "localhost",
                                    user   = "root",
                                    passwd = "x",
                                    db     = "y")
      cursor = connection.cursor()
      
      query_counter = 0
      print_queries = False
      limit         = 1000
      
      def fetch_one( query ):
          global query_counter, print_queries
          query = query + ' LIMIT ' + str(limit)
          if print_queries:
              print query
          cursor = connection.cursor()
          cursor.execute( query )
          query_counter += 1
          result = cursor.fetchone() 
          if result:
              return result[0]
          else:
              return None
      
      def fetch_all( query ):
          global query_counter, print_queries
          query = query + ' LIMIT ' + str(limit)
          if print_queries:
              print query
          cursor = connection.cursor()
          cursor.execute( query )
          query_counter += 1
          return cursor.fetchall()
      
      def _flatten( list_of_lists ):
          import itertools
          return list(itertools.chain(*list_of_lists))
      
      #Example: e._search_by_name('steve martin')
      def _search_by_name( name, operator = '=' ):
          typed, ranked = {}, []
          if name:
              name = name.strip()
          if not name:
              return ( typed, ranked )
      
          filler = '' if operator == '=' else '%'
          ranks = {}
      
          #to filter meaningful stuff for every mid returned order by the number of types they have
      
          #search for value text if prop. is 
          #select * from ns where value = 'the king' and (property = '/m/01gr' or property = '/m/06b');
      
          name_mid  = _mid( '/type/object/name'   )
          alias_mid = _mid( '/common/topic/alias' )
      
          query = "select ns.source from ns where ns.value %s '%s%s' and ns.property in ('%s', '%s')" % ( operator, name, filler, name_mid, alias_mid )
      
          for i in fetch_all( query ):
              typed[ i[0] ] = _types( i[0] )
      
          import operator
          ranked = [ ( len( typed[i] ), i ) for i in typed ]
          ranked = [ e[1] for e in sorted( ranked, key=operator.itemgetter(0), reverse = True ) ]
      
          return (typed, ranked)
      
      
      #Example: e._children('')               <---will get the top level domains
      #         e._children('/film')          <---get all types from the domain
      #         e._children('/film/film')     <---get all properties for the type
      def _children( parent, expand = False, raw = False ):
          query = "select t.source, t.value from types t where t.destination = '%s'" % (parent)
          res = fetch_all( query )
          if raw:
              return [ row[0] for row in res ]
          if expand: prefix = parent
          else:      prefix = ''
          return [ prefix + '/' + row[1] for row in fetch_all(query) ]
      
      #Example: e._parent('/film/film/songs')
      def _parent( child ):                                       # '/people/marriage/to' -> '/people/marriage'
          #if not isinstance( child, str ): return None          # what kind of safety mechanisms do we need here?
          return '/'.join(child.split('/')[:-1])
      
      #Example: e._domains()
      def _domains():
          return _children('')
      
      #Example: e._top_level_types()
      def _top_level_types():
          return _children('/type')
      
      #TODO get all primitive types
      
      #Example: e._mid('/type/object')
      #         e._mid('/authority/imdb/name/nm0000188')
      def _mid( key ):
          if key == '':
              return None
          elif key == '/':
              key = '/boot/root_namespace'
          parts = key.split('/')
          if parts[1] == 'm':            #already a mid
              return key
          namespace = '/'.join(parts[:-1])
          key = parts[-1]
          return fetch_one( "select source from types t where t.destination = '%s' and t.value = '%s'" % (namespace, key) )
      
      #Example: e._key('/type')
      def _key( mid ):
          if isinstance( mid, str):
              res = _keys( mid )
              if not res:
                  return None
              rt  = [ r for r in res if r.startswith( '/type' ) ]
              if rt:
                  return rt[0]
              else:
                  return res[0]
          elif isinstance( mid, list ) or isinstance( mid, tuple ):
              res = [ _key( e ) for e in mid ]
              return [ r for r in res if r is not None ]
          else:
              return None
      
      def _keys( mid ):
          # check for '/type/object/key' as well?
          query = "select t.destination, t.value from types t where t.source = '%s'" % mid
          return [ row[0]+'/'+row[1] for row in fetch_all( query ) ]
      
      #Example: e._types('/m/0p_47')
      def _types( mid ):
          tm = _mid( '/type/object/type' )
          query = "select l.destination from links l where l.source = '%s' and l.property = '%s'" % (mid, tm)
          return [ row[0] for row in fetch_all( query ) ]
      
      #Example: e._props_n('/m/0p_47')   <---Named immediate properties (like name, etc.)
      def _props_n( mid ):  #the same property can be set more than once per topic!
          query = "select ns.property from ns where ns.source = '%s'" % (mid)
          return list( set( [ row[0] for row in fetch_all( query ) ] ) )
      
      #Example: e._props_l('/m/0p_47')   <---All remote properties, some are named, some are anonymous
      def _props_l( mid ):  #the same property can be set more than once per topic!
          tm = _mid( '/type/object/type' )    #exclude types, they have tons of instance links
          res = fetch_all( "select l.property, l.destination from links l where l.source = '%s' and property <> '%s'" % (mid, tm) )
          output = {}
          for r in res:
              dests = output.get( r[0], False )
              if dests:
                  dests.append( r[1] )
              else:
                  output[ r[0] ] = [ r[1] ]
          return output
      
      #Example: e._props_ln('/m/0p_47')  <---All remote named properties
      def _props_ln( mid ): #named properties
          result = []
          ps = _props_l( mid )
          common_topic = _mid( '/common/topic' )
          for p in ps:
              ts = _types( ps[p][0] )
              if common_topic in ts:               #it's a common topic
                  result.append( p )
          return result
      
      #Example: e._props_la('/m/0p_47')  <---All remote anonymous properties, these actually belong to the children!
      #instead of has type /common/topic we used to check if it has name
      def _props_la( mid, raw = True ): #anonymous properties (blank nodes in RDF?)
          result = []
          ps = _props_l( mid )
          common_topic = _mid( '/common/topic' )
          for p in ps:
              ts = _types( ps[p][0] )
              if common_topic not in ts:                                       #it is not a common topic
                  t = _key( _types( ps[p][0] ) )
                  if t and '/type/type' not in t:                              #FIXME: hack not to go into types, could be done better
                      result.append( _children( t[0], expand=True, raw=raw ) ) #get the first, is this correct?
          return _flatten( result )                                            #it is a list of lists
      
      #FIXME: try to get '/film/actor/film' -> '/type/property/expected_type' -> '/film/performance' -> properties/children 
      #instead of trying is something has name
      
      #Example: e._get_n('/m/0p_47', e._props_n('/m/0p_47')[0])['/lang/en'] <---These come with a namespace
      def _get_n( mid, prop ):  #the same property can be set more than once per topic!
          p = _mid( prop )
          query = "select ns.value from ns where ns.source = '%s' and ns.property = '%s'" % (mid, p)
          return [ r[0] for r in fetch_all( query ) ]
      
      #Example: e._get_l('/m/0p_47', e._props_l('/m/0p_47')[0])  <---returns a list of mids coresponding to that prop.
      #         e._name(e._get_l('/m/0p_47', '/film/writer/film'))          
      def _get_l( mid, prop ):  #the same property can be set more than once per topic!
          p = _mid( prop )
          query = "select l.destination from links l where l.source = '%s' and l.property = '%s'" % (mid, p)
          return  [ row[0] for row in fetch_all( query ) ]           
      
      #Example: e._name(e._get_ln('/m/0p_47', e._props_ln('/m/0p_47')[0]))
      def _get_ln( mid, p ):        #just alias for _get_l, keeping for consistency
          return _get_l( mid, p )                                 
      
      #Example: e._name(e._get_la('/m/0p_47', '/film/performance/film'))
      def _get_la( mid, prop ):
          result = []
          ps = _props_l( mid )
          for p in ps:
              es = _get_l( mid, p )                       #get the destinations
              if not es: continue
              ts = set( _types( es[0] ) )
              if _mid(_parent(_key(_mid(prop)))) in ts:   #should be able to do this more efficiently!!!
                  for e in es:
                      result.append( _get_l( e, prop ) )
                  return _flatten( result )               #return after the first result 
      
      #How do we determine properties with multiple values vs those with singular (i.e. place of birth)?
      #is this in the ontology?
      #Ans: yes, /type/property/unique
      
      #Example: e._all_names_ln('/m/0p_47')  <---gets all of object's remote named properties
      def _all_names_ln( mid ):
          result = {}
          for p in _props_ln( mid ):
              result[ _key(p) ] = _name( _get_ln( mid, p ) )
          return result 
      
      #Example: e._all_names_la('/m/0p_47')  <---gets all of object's remote anonymous properties
      def _all_names_la( mid ):       #TODO: prevent loops, run e.all_names_la('/m/0p_47')
          result = {}
          for p in _props_la( mid ):
              result[ _key( p ) ] = _name ( _get_la( mid, p ) )
          return result 
      
      #FIXME: _all_names_la is going into destinations which are types and have a ton of instance links...
      
      
      #Example: e._name('/m/0p_47')   <---the name of a topic
      #
      def _name( mid ):
          if isinstance( mid, str ):
              nm = _mid( '/type/object/name' )
              return _get_n( mid, nm )
          elif isinstance( mid, list ) or isinstance( mid, tuple ) or isinstance( mid, set ):
              return [ _name( e ) for e in mid ]
          else:
              return None
      
      #for internal use only
      def _get_linked( mid ):
          tm = _mid( '/type/object/type' )    #exclude types, they have tons of instance links
          query = "select destination from links where source = '%s' and property <> '%s' " % ( mid, tm )
          return set( [ r[0] for r in fetch_all( query ) ] )
      
      #for internal use only
      def _get_connections_internal( entity1, target, path, all_paths, depth, max_depth):
          import copy
      
          if depth > max_depth:
              return
      
          if True:
              print
              print str(entity1) + ', ' + str(target)
              print str( path )
              print str( all_paths )
              print depth
      
          path.append( entity1 )
      
          linked1 = _get_linked( entity1 )
      
          if target in linked1 or entity1 == target:
              path.append( target )
              all_paths.append( path )
              #print str( path )
              return
      
          for l1 in linked1:
              if l1 in path:
                  continue
              _get_connections_internal( l1, 
                                         target, 
                                         copy.copy( path ),
                                         all_paths,
                                         depth+1, 
                                         max_depth )
      
      #Example: e._name(e._get_connections('/m/0p_47', '/m/0cwtm'))  <---find path in the graph between the two entities    
      def _get_connections( entity1, target ):
          result = []
          _get_connections_internal( entity1, target, [], result, 0, 2 )
          return result
      
      #for internal use only
      def _get_connections_internal2( entity1, entity2, path1, path2, all_paths, depth, max_depth, level ):
          import copy
      
          if depth > max_depth:
              return
      
          if level < 0: level = 0
      
          path1.append( entity1 )
          path2.append( entity2 )
      
          if entity1 == entity2 and level == 0:
              all_paths.append( ( path1, path2 ) ) #no need to append entity1 or entity2 to the paths
              return
      
          linked1 = _get_linked( entity1 )
          if entity2 in linked1 and entity2 not in path1 and level == 0:
              path1.append( entity2 )
              all_paths.append( ( path1, path2 ) )
              return
      
          linked2 = _get_linked( entity2 )
          if entity1 in linked2 and entity1 not in path2 and level == 0:
              path2.append( entity1 )
              all_paths.append( ( path1, path2 ) )
              return
      
          inters = linked1.intersection( linked2 )
          inters = inters.difference( set( path1 ) )
          inters = inters.difference( set( path2 ) )
      
          if inters and level == 0:
              for e in inters:               #these are many paths, have to clone
                  p1 = copy.copy( path1 )
                  p1.append( e )
                  p2 = copy.copy( path2 )
                  p2.append( e )
                  all_paths.append( ( p1,p2 ) )
              return
      
          for l1 in linked1:
              if l1 in path1 or l1 in path2:
                  continue
              for l2 in linked2:
                  if l2 in path1 or l2 in path2:
                      continue
                  _get_connections_internal2( l1,                 l2, 
                                              copy.copy( path1 ), copy.copy( path2 ),
                                              all_paths,
                                              depth+1, 
                                              max_depth,
                                              level - 1 )
      
      #Example: e._name(e._get_connections2('/m/0p_47', '/m/0cwtm'))          <---returns two meeting paths starting from both entities
      #         e._name(e._get_connections('/m/0p_47', '/m/0cwtm', level=1))  <---search deeper
      #         e._name(e._get_connections('/m/0p_47', '/m/0cwtm', level=2))  <---even deeper
      def _get_connections2( entity1, entity2, level = 0 ):
          result = []
          _get_connections_internal2( entity1, entity2, [], [], result, 0, 15, level )
          return result
      

      这是一个示例 web2py 控制器(只需复制 web2py 模型目录中的 edb.py):

      # -*- coding: utf-8 -*-
      
      def mid_to_url( mid ):
          return mid.split('/')[2]
      
      def index():
      
          form = FORM( TABLE( TR( INPUT(_name='term', _value=request.vars.term ) ),
                              TR(INPUT(_type='submit', _value='Search') ) ),
                       _method='get')
      
          typed, ranked = _search_by_name( request.vars.term )
      
          rows = []
      
          for r in ranked:
              keys = []
              for t in typed[r]:
                  k = _key( t )
                  if k:
                      keys.append( k )
              rows.append( TR( TD( A(_name( r ), 
                                     _href = URL('result', args = [mid_to_url(r)]))), 
                               TD( XML( '<br/>'.join( keys ) ) ) ) )
      
          result = TABLE( *rows )
      
          return { 
                   'form': form, 
                   'result' : result 
                   }
      
      def result():
          path, data = '', ''
          if not request.args:
              return { 'path':path, 'data':data}
      
          path_rows = []
          for ra in range(len(request.args)):
              if ra%2:
                  arrow_url = URL( 'static', 'images/blue_arr.png' )
                  display_name = _key('/m/'+request.args[ra])        #it's a property
              else:
                  arrow_url = URL( 'static', 'images/red_arr.png' )
                  display_name = _name('/m/'+request.args[ra])       #it's a topic
              path_rows.append( TD( A( display_name, _href=URL( args = request.args[0:ra+1] ) ) ) )
              path_rows.append( TD( IMG( _src = arrow_url ) ) )
          path = TABLE( *path_rows )
      
          elems = [ '/m/'+a  for a in request.args ]
          if _mid( '/type/property' ) in _types( elems[-1] ): #we are rendering a property
              objects = _get_ln( elems[-2], elems[-1] )
              if not objects: #there should be a better way to see if this is anonymous
                  objects = _get_la( elems[-2], elems[-1] )
      
              data = TABLE( *[ TR( TD( A(_name(o), _href = URL( args = request.args+[mid_to_url(o)])))) for o in objects ] )
      
          else:                                               #we are rendering a topic
              direct_props = TABLE(*[TR(TD(_key(p)), TD(', '.join(_get_n( elems[-1], p)))) for p in _props_n( elems[-1] )])
              linked_named_props = TABLE(*[TR(TD(A(_key(p), 
                                                   _href = URL(args = request.args+[mid_to_url(p)])))) for p in _props_ln( elems[-1] ) ] )
              linked_anon_props = TABLE(*[TR(TD(A(_key(p), 
                                                  _href = URL(args = request.args+[mid_to_url(p)])))) for p in _props_la( elems[-1] ) ] )
      
              data = TABLE( TR( TH( 'Linked named data:'), TH( 'Linked anonymous data:' ), TH( 'Direct data:' ) ), 
                            TR( TD( linked_named_props ), TD( linked_anon_props ), TD( direct_props ) ) )
      
          return { 'path': path, 'data':data }
      

      【讨论】:

        【解决方案4】:

        这对我有用。它允许您在不到 100GB 的磁盘上加载标准 MySQL 安装中的所有 Freebase 转储。关键是了解转储中的数据布局,然后对其进行转换(针对空间和速度进行优化)。

        Freebase 概念在尝试使用它之前您应该了解(全部来自文档):

        • 主题 - '/common/topic' 类型的任何内容,注意您在 Freebase 中可能遇到的不同类型的 id - 'id'、'mid'、'guid'、'webid' 等。
        • 域
        • 类型 - “是”关系
        • 属性 - “有”关系
        • 架构
        • 命名空间
        • 密钥 - 在“/en”命名空间中可读

        其他一些重要的 Freebase 细节:

        • query editor 是你的朋友
        • 了解here 中描述的“源”、“属性”、“目标”和“价值”概念
        • 一切都有一个中间,甚至像'/'、'/m'、'/en'、'/lang'、'/m/0bnqs_5'等;使用查询编辑器进行测试:[{'id':'/','mid':null}]​
        • 您不知道数据转储中的任何实体(即行)是什么,您必须了解其类型才能做到这一点(例如,我怎么知道 '/m/0cwtm' 是人类);
        • 每个实体至少有一种类型(但通常更多)
        • 每个实体至少有一个 id/key(但通常更多)
        • ontology(即元数据)嵌入到与数据相同的转储和相同格式中(与 DBPedia 等其他发行版不同)
        • 转储中的“目标”列令人困惑,它可能包含中间值或键(请参阅下面的转换如何处理此问题)
        • 域、类型、属性同时是命名空间级别(无论是谁想出这个都是天才恕我直言);
        • 了解什么是主题,什么不是主题(绝对至关重要),例如'/film/performance' 类型的实体'/m/03lmb2f' 不是主题(我选择将这些视为RDF 中的Blank Nodes虽然这在哲学上可能不准确),而 '/film/director' 类型的 '/m/04y78wb'(以及其他)是;

        变换

        (见底部的 Python 代码)

        TRANSFORM 1(来自 shell,从忽略 notable_for 和非 /lang/en 文本的命名空间拆分链接):

        python parse.py freebase.tsv  #end up with freebase_links.tsv and freebase_ns.tsv
        

        TRANSFORM 2(从 Python 控制台,将 freebase_ns.tsv 拆分到 freebase_ns_types.tsv、freebase_ns_props.tsv 以及其他 15 个我们暂时忽略的)

        import e
        e.split_external_keys( 'freebase_ns.tsv' )
        

        TRANSFORM 3(从 Python 控制台,将属性和目标转换为中间值)

        import e
        ns = e.get_namespaced_data( 'freebase_ns_types.tsv' )
        e.replace_property_and_destination_with_mid( 'freebase_links.tsv', ns )    #produces freebase_links_pdmids.tsv
        e.replace_property_with_mid( 'freebase_ns_props.tsv', ns ) #produces freebase_ns_props_pmids.tsv
        

        TRANSFORM 4(从 MySQL 控制台,加载数据库中的 freebase_links_mids.tsv、freebase_ns_props_mids.tsv 和 freebase_ns_types.tsv):

        CREATE TABLE links(
        source      VARCHAR(20), 
        property    VARCHAR(20), 
        destination VARCHAR(20), 
        value       VARCHAR(1)
        ) ENGINE=MyISAM CHARACTER SET utf8;
        
        CREATE TABLE ns(
        source      VARCHAR(20), 
        property    VARCHAR(20), 
        destination VARCHAR(40), 
        value       VARCHAR(255)
        ) ENGINE=MyISAM CHARACTER SET utf8;
        
        CREATE TABLE types(
        source      VARCHAR(20), 
        property    VARCHAR(40), 
        destination VARCHAR(40), 
        value       VARCHAR(40)
        ) ENGINE=MyISAM CHARACTER SET utf8;
        
        LOAD DATA LOCAL INFILE "/data/freebase_links_pdmids.tsv" INTO TABLE links FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n';
        LOAD DATA LOCAL INFILE "/data/freebase_ns_props_pmids.tsv" INTO TABLE ns FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n';
        LOAD DATA LOCAL INFILE "/data/freebase_ns_base_plus_types.tsv" INTO TABLE types FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n';
        
        CREATE INDEX links_source            ON links (source)             USING BTREE;
        CREATE INDEX ns_source               ON ns    (source)             USING BTREE;
        CREATE INDEX ns_value                ON ns    (value)              USING BTREE;
        CREATE INDEX types_source            ON types (source)             USING BTREE;
        CREATE INDEX types_destination_value ON types (destination, value) USING BTREE;
        

        代码

        将此保存为 e.py:

        import sys
        
        #returns a dict to be used by mid(...), replace_property_and_destination_with_mid(...) bellow
        def get_namespaced_data( file_name ):
            f = open( file_name )
            result = {}
        
            for line in f:
                elements = line[:-1].split('\t')
        
                if len( elements ) < 4:
                    print 'Skip...'
                    continue
        
                result[(elements[2], elements[3])] = elements[0]
        
            return result
        
        #runs out of memory
        def load_links( file_name ):
            f = open( file_name )
            result = {}
        
            for line in f:
                if len( result ) % 1000000 == 0:
                    print len(result)
                elements = line[:-1].split('\t')
                src, prop, dest = elements[0], elements[1], elements[2]
                if result.get( src, False ):
                    if result[ src ].get( prop, False ):
                        result[ src ][ prop ].append( dest )
                    else:
                        result[ src ][ prop ] = [dest]
                else:
                    result[ src ] = dict([( prop, [dest] )])
        
            return result
        
        #same as load_links but for the namespaced data
        def load_ns( file_name ):
            f = open( file_name )
            result = {}
        
            for line in f:
                if len( result ) % 1000000 == 0:
                    print len(result)
                elements = line[:-1].split('\t')
                src, prop, value = elements[0], elements[1], elements[3]
                if result.get( src, False ):
                    if result[ src ].get( prop, False ):
                        result[ src ][ prop ].append( value )
                    else:
                        result[ src ][ prop ] = [value]
                else:
                    result[ src ] = dict([( prop, [value] )])
        
            return result
        
        def links_in_set( file_name ):
            f = open( file_name )
            result = set()
        
            for line in f:
                elements = line[:-1].split('\t')
                result.add( elements[0] )
            return result
        
        def mid( key, ns ):
            if key == '':
                return False
            elif key == '/':
                key = '/boot/root_namespace'
            parts = key.split('/')
            if len(parts) == 1:           #cover the case of something which doesn't start with '/'
                print key
                return False
            if parts[1] == 'm':           #already a mid
                return key
            namespace = '/'.join(parts[:-1])
            key = parts[-1]
            return ns.get( (namespace, key), False )
        
        def replace_property_and_destination_with_mid( file_name, ns ):
            fn = file_name.split('.')[0]
            f = open( file_name )
            f_out_mids = open(fn+'_pdmids'+'.tsv', 'w')
        
            def convert_to_mid_if_possible( value ):
                m = mid( value, ns )
                if m: return m
                else: return None
        
            counter = 0
        
            for line in f:
                elements = line[:-1].split('\t')
                md   = convert_to_mid_if_possible(elements[1])
                dest = convert_to_mid_if_possible(elements[2])
                if md and dest:
                    elements[1] = md
                    elements[2] = dest
                    f_out_mids.write( '\t'.join(elements)+'\n' )
                else:
                    counter += 1
        
            print 'Skipped: ' + str( counter )
        
        def replace_property_with_mid( file_name, ns ):
            fn = file_name.split('.')[0]
            f = open( file_name )
            f_out_mids = open(fn+'_pmids'+'.tsv', 'w')
        
            def convert_to_mid_if_possible( value ):
                m = mid( value, ns )
                if m: return m
                else: return None
        
            for line in f:
                elements = line[:-1].split('\t')
                md = convert_to_mid_if_possible(elements[1])
                if md:
                    elements[1]=md
                    f_out_mids.write( '\t'.join(elements)+'\n' )
                else:
                    #print 'Skipping ' + elements[1]
                    pass
        
        #cPickle
        #ns=e.get_namespaced_data('freebase_2.tsv')
        #import cPickle
        #cPickle.dump( ns, open('ttt.dump','wb'), protocol=2 )
        #ns=cPickle.load( open('ttt.dump','rb') )
        
        #fn='/m/0'
        #n=fn.split('/')[2]
        #dir = n[:-1]
        
        
        def is_mid( value ):
            parts = value.split('/')
            if len(parts) == 1:   #it doesn't start with '/'
                return False
            if parts[1] == 'm':
                return True
            return False
        
        def check_if_property_or_destination_are_mid( file_name ):
            f = open( file_name )
        
            for line in f:
                elements = line[:-1].split('\t')
                #if is_mid( elements[1] ) or is_mid( elements[2] ):
                if is_mid( elements[1] ):
                    print line
        
        #
        def split_external_keys( file_name ):
            fn = file_name.split('.')[0]
            f = open( file_name )
            f_out_extkeys  = open(fn+'_extkeys' + '.tsv', 'w')
            f_out_intkeys  = open(fn+'_intkeys' + '.tsv', 'w')
            f_out_props    = open(fn+'_props'   + '.tsv', 'w')
            f_out_types    = open(fn+'_types'   + '.tsv', 'w')
            f_out_m        = open(fn+'_m'       + '.tsv', 'w')
            f_out_src      = open(fn+'_src'     + '.tsv', 'w')
            f_out_usr      = open(fn+'_usr'     + '.tsv', 'w')
            f_out_base     = open(fn+'_base'    + '.tsv', 'w')
            f_out_blg      = open(fn+'_blg'     + '.tsv', 'w')
            f_out_bus      = open(fn+'_bus'     + '.tsv', 'w')
            f_out_soft     = open(fn+'_soft'    + '.tsv', 'w')
            f_out_uri      = open(fn+'_uri'     + '.tsv', 'w')
            f_out_quot     = open(fn+'_quot'    + '.tsv', 'w')
            f_out_frb      = open(fn+'_frb'     + '.tsv', 'w')
            f_out_tag      = open(fn+'_tag'     + '.tsv', 'w')
            f_out_guid     = open(fn+'_guid'    + '.tsv', 'w')
            f_out_dtwrld   = open(fn+'_dtwrld'  + '.tsv', 'w')
        
            for line in f:
                elements = line[:-1].split('\t')
                parts_2 = elements[2].split('/')
                if len(parts_2) == 1:                 #the blank destination elements - '', plus the root domain ones
                    if elements[1] == '/type/object/key':
                        f_out_types.write( line )
                    else:
                        f_out_props.write( line )
        
                elif elements[2] == '/lang/en':
                    f_out_props.write( line )
        
                elif (parts_2[1] == 'wikipedia' or parts_2[1] == 'authority') and len( parts_2 ) > 2:
                    f_out_extkeys.write( line )
        
                elif parts_2[1] == 'm':
                    f_out_m.write( line )
        
                elif parts_2[1] == 'en':
                    f_out_intkeys.write( line )
        
                elif parts_2[1] == 'source' and len( parts_2 ) > 2:
                    f_out_src.write( line )
        
                elif parts_2[1] == 'user':
                    f_out_usr.write( line )
        
                elif parts_2[1] == 'base' and len( parts_2 ) > 2:
                    if elements[1] == '/type/object/key':
                        f_out_types.write( line )
                    else:
                        f_out_base.write( line )
        
                elif parts_2[1] == 'biology' and len( parts_2 ) > 2:
                    f_out_blg.write( line )
        
                elif parts_2[1] == 'business' and len( parts_2 ) > 2:
                    f_out_bus.write( line )
        
                elif parts_2[1] == 'soft' and len( parts_2 ) > 2:
                    f_out_soft.write( line )
        
                elif parts_2[1] == 'uri':
                    f_out_uri.write( line )
        
                elif parts_2[1] == 'quotationsbook' and len( parts_2 ) > 2:
                    f_out_quot.write( line )
        
                elif parts_2[1] == 'freebase' and len( parts_2 ) > 2:
                    f_out_frb.write( line )
        
                elif parts_2[1] == 'tag' and len( parts_2 ) > 2:
                    f_out_tag.write( line )
        
                elif parts_2[1] == 'guid' and len( parts_2 ) > 2:
                    f_out_guid.write( line )
        
                elif parts_2[1] == 'dataworld' and len( parts_2 ) > 2:
                    f_out_dtwrld.write( line )
        
                else:
                    f_out_types.write( line )
        

        将此保存为 parse.py:

        import sys
        
        def parse_freebase_quadruple_tsv_file( file_name ):
            fn = file_name.split('.')[0]
            f = open( file_name )
            f_out_links = open(fn+'_links'+'.tsv', 'w')
            f_out_ns    = open(fn+'_ns'   +'.tsv', 'w')
        
            for line in f:
                elements = line[:-1].split('\t')
        
                if len( elements ) < 4:
                    print 'Skip...'
                    continue
        
                #print 'Processing ' + str( elements )                                                                                                                  
        
                #cases described here http://wiki.freebase.com/wiki/Data_dumps                                                                                          
                if elements[1].endswith('/notable_for'):                               #ignore notable_for, it has JSON in it                                           
                    continue
        
                elif elements[2] and not elements[3]:                                  #case 1, linked                                                                  
                    f_out_links.write( line )
        
                elif not (elements[2].startswith('/lang/') and elements[2] != '/lang/en'):   #ignore languages other than English                                       
                    f_out_ns.write( line )
        
        if len(sys.argv[1:]) == 0:
            print 'Pass a list of .tsv filenames'
        
        for file_name in sys.argv[1:]:
            parse_freebase_quadruple_tsv_file( file_name )
        

        注意事项:

        • 根据机器的不同,创建索引可能需要几个小时到 12 多个小时不等(但请考虑您正在处理的数据量)。
        • 为了能够在两个方向上遍历数据,您还需要在 links.destination 上建立一个索引,我发现这在时间上很昂贵,而且从未完成。
        • 这里还可以进行许多其他优化。例如,“类型”表足够小,可以在 Python 字典中加载到内存中(参见 e.get_namespaced_data( 'freebase_ns_types.tsv' ))

        这里是标准免责声明。自从我这样做以来已经有几个月了。我相信这大部分是正确的,但如果我的笔记遗漏了什么,我深表歉意。不幸的是,我需要它的项目失败了,但希望这对其他人有所帮助。如果有不清楚的地方,请在此处发表评论。

        【讨论】:

          【解决方案5】:

          我的 2 美分...

          我使用一点 Java 代码将 Freebase 数据转储转换为 RDF:https://github.com/castagna/freebase2rdf

          我使用 Apache Jena 的 TDB 存储加载 RDF 数据,并使用 Fuseki 通过 HTTP 上的 SPARQL 协议提供数据。

          另见:

          【讨论】:

            【解决方案6】:

            SPARQL 是查询 RDF 的查询语言,它允许编写类似 SQL 的查询。大多数 RDF 数据库都实现了 SPARQL 接口。此外,Freebase 允许您在 RDF 中导出数据,因此您可以直接在 RDF 数据库中使用该数据并使用 SPARQL 进行查询。

            我会看看 this tutorial 以更好地了解 SPARQL。

            如果您要处理大型数据集,例如 freebase,我会使用 4store 和任何 Python clients。 4store 通过 HTTP 公开 SPARQL,您可以发出 HTTP 请求来断言、删除和查询数据。它还处理 JSON 格式的结果集,这对于 Python 来说非常方便。我在几个项目中使用过这个基础设施,不是用 CherryPy,而是用 Django,但我想这个区别并不重要。

            【讨论】:

            • Freebase 不提供批量 RDF 下载,因此您必须爬取整个站点(即使这样也不完整)。使用 freebase-quad-rdfize 或等效项,如对这个重复的问题的答案中所述,是处理 RDF 存储和批量数据下载的方法。我很惊讶您使用 Freebase RDF 爬网和 4store 在“几个项目”上获得了可接受的结果。
            • 他没有明确说他正在使用 Freebase 数据转储。我个人对复制数据转储不感兴趣,我只想在本地保留一些我不想推送到 Freebase 的东西。
            • 布鲁斯是对的。我还没有在 RDF 中使用过完整的 Freebase 转储。我使用了从sameAs.org 获得的部分Freebase。无论如何,底线是我对大型 RDF 数据集的推荐是 4store。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-04
            • 2018-01-01
            • 2013-05-11
            • 1970-01-01
            相关资源
            最近更新 更多