معاينة مختبر آمنة
Family Ontology
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
Family Ontology
> **ملاحظة بيئة التشغيل المدمجة:** هالمعاينة تستخدم عيّنة صغيرة وثابتة وآمنة من ناحية الحقوق عشان تكون النتايج قابلة للتكرار. النتايج بالحجم الكامل تحتاج مجموعة البيانات أو النموذج الموثّق بالدرس داخل بيئة خارجية معتمدة.
# أنطولوجيا العلاقات العائلية
هالمثال جزء من منهج AI for Beginners، ومستوحى من [هالمقالة](https://habr.com/post/270857/).
العلاقات بين أفراد العائلة أحيانًا يصعب تذكرها. في هالمثال بناخذ أنطولوجيا (Ontology) تعرّف العلاقات العائلية، ومعها شجرة النسب الفعلية، ثم نشوف كيف نستخدم الاستدلال الآلي عشان نلقى كل الأقارب.
### جلب شجرة النسب
بنستخدم شجرة نسب [عائلة قياصرة رومانوف](https://en.wikipedia.org/wiki/House_of_Romanov) مثالًا. أكثر تنسيق شائع لوصف علاقات العائلة هو [GEDCOM](https://en.wikipedia.org/wiki/GEDCOM)، وبناخذ شجرة رومانوف بهالتنسيق:
# Course-authored miniature family data and ontology.
from pathlib import Path
course_data = Path("data")
course_data.mkdir(parents=True, exist_ok=True)
course_gedcom = '''0 HEAD
1 SOUR HALAACADEMY
1 GEDC
2 VERS 5.5.1
0 @I1@ INDI
1 NAME Alexander /Example/
1 SEX M
1 FAMS @F1@
1 FAMC @F0@
0 @I2@ INDI
1 NAME Boris /Example/
1 SEX M
1 FAMC @F0@
0 @I3@ INDI
1 NAME Clara /Example/
1 SEX F
1 FAMC @F1@
0 @I4@ INDI
1 NAME Diana /Example/
1 SEX F
1 FAMS @F1@
0 @I5@ INDI
1 NAME Elena /Example/
1 SEX F
1 FAMS @F0@
0 @I6@ INDI
1 NAME Mikhail /Example/
1 SEX M
1 FAMS @F0@
0 @F0@ FAM
1 HUSB @I6@
1 WIFE @I5@
1 CHIL @I1@
1 CHIL @I2@
0 @F1@ FAM
1 HUSB @I1@
1 WIFE @I4@
1 CHIL @I3@
0 TRLR
'''
course_ontology = '''@prefix fhkb: <http://example.org/family#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
fhkb:isFatherOf a owl:ObjectProperty .
fhkb:isMotherOf a owl:ObjectProperty .
fhkb:isBrotherOf a owl:ObjectProperty .
fhkb:isSisterOf a owl:ObjectProperty .
fhkb:hasFemalePartner a owl:ObjectProperty .
fhkb:hasMalePartner a owl:ObjectProperty .
fhkb:isUncleOf a owl:ObjectProperty ;
owl:propertyChainAxiom ( fhkb:isBrotherOf fhkb:isFatherOf ) ;
owl:propertyChainAxiom ( fhkb:isBrotherOf fhkb:isMotherOf ) .
'''
(course_data / "tsars.ged").write_text(course_gedcom, encoding="utf-8")
(course_data / "onto.ttl").write_text(course_ontology, encoding="utf-8")
print("\n".join(course_gedcom.splitlines()[:15]))عشان نتعامل مع ملف GEDCOM، نقدر نستخدم مكتبة `python-gedcom`:
import sys
# Dependencies are provisioned by this course edition's pinned runtime profile.المكتبة تتولى بعض التفاصيل التقنية لتحليل الملف، لكنها تعطينا وصولًا منخفض المستوى إلى كل الأشخاص والعائلات في الشجرة. بهالطريقة نحلل الملف ونعرض قائمة الأشخاص:
from gedcom.parser import Parser
from gedcom.element.individual import IndividualElement
from gedcom.element.family import FamilyElement
g = Parser()
g.parse_file('data/tsars.ged')d = g.get_element_dictionary()
[ (k,v.get_name()) for k,v in d.items() if isinstance(v,IndividualElement)]وهذي طريقة جلب معلومات العائلات. لاحظوا أن الناتج قائمة **معرّفات**، ونحتاج نحوّلها إلى أسماء إذا بغيناها أوضح:
d = g.get_element_dictionary()
[ (k,[x.get_value() for x in v.get_child_elements()]) for k,v in d.items() if isinstance(v,FamilyElement)]### جلب أنطولوجيا العائلة
الحين خلّونا نطالع [أنطولوجيا العائلة](https://raw.githubusercontent.com/blokhin/genealogical-trees/0f8c4389c9218caf85a58f2d4a37a2b3b4239d08/data/header.ttl)، وهي معرّفة كمجموعة من ثلاثيات الويب الدلالي. تعرّف الأنطولوجيا علاقات مثل `isUncleOf` و`isCousinOf` وغيرها. وكلها مبنية على محمولات أساسية مثل `isMotherOf` و`isFatherOf` و`isBrotherOf` و`isSisterOf`. وبنستخدم الاستدلال الآلي عشان نستنتج بقية العلاقات من الأنطولوجيا.
وهذا تعريف نموذجي للخاصية `isAuntOf`، بوصفها تركيبًا من `isSisterOf` و`isParentOf`؛ يعني *العمّة أو الخالة أخت أحد الوالدين*.
```
fhkb:isAuntOf a owl:ObjectProperty ;
rdfs:domain fhkb:Woman ;
rdfs:range fhkb:Person ;
owl:propertyChainAxiom ( fhkb:isSisterOf fhkb:isParentOf ) .
```
print("\n".join(Path("data/onto.ttl").read_text(encoding="utf-8").splitlines()[:20]))### بناء الأنطولوجيا للاستدلال
للتبسيط، بننشئ ملف أنطولوجيا واحدًا يجمع القواعد الأصلية من أنطولوجيا العائلة والحقائق عن الأشخاص من ملف GEDCOM. بنمر على ملف GEDCOM، ونستخرج معلومات العائلات والأشخاص، ثم نحوّلها إلى ثلاثيات.
from shutil import copyfile
copyfile("data/onto.ttl", "onto.ttl")
gedcom_dict = g.get_element_dictionary()
individuals, marriages = {}, {}
def term2id(el):
return "i" + el.get_pointer().replace('@', '').lower()
out = open("onto.ttl","a")
for k, v in gedcom_dict.items():
if isinstance(v,IndividualElement):
children, siblings = set(), set()
idx = term2id(v)
title = v.get_name()[0] + " " + v.get_name()[1]
title = title.replace('"', '').replace('[', '').replace(']', '').replace('(', '').replace(')', '').strip()
own_families = g.get_families(v, 'FAMS')
for fam in own_families:
children |= set(term2id(i) for i in g.get_family_members(fam, "CHIL"))
parent_families = g.get_families(v, 'FAMC')
if len(parent_families):
for member in g.get_family_members(parent_families[0], "CHIL"): # NB adoptive families i.e len(parent_families)>1 are not considered (TODO?)
if member.get_pointer() == v.get_pointer():
continue
siblings.add(term2id(member))
if idx in individuals:
children |= individuals[idx].get('children', set())
siblings |= individuals[idx].get('siblings', set())
individuals[idx] = {'sex': v.get_gender().lower(), 'children': children, 'siblings': siblings, 'title': title}
elif isinstance(v,FamilyElement):
wife, husb, children = None, None, set()
children = set(term2id(i) for i in g.get_family_members(v, "CHIL"))
try:
wife = g.get_family_members(v, "WIFE")[0]
wife = term2id(wife)
if wife in individuals: individuals[wife]['children'] |= children
else: individuals[wife] = {'children': children}
except IndexError: pass
try:
husb = g.get_family_members(v, "HUSB")[0]
husb = term2id(husb)
if husb in individuals: individuals[husb]['children'] |= children
else: individuals[husb] = {'children': children}
except IndexError: pass
if wife and husb: marriages[wife + husb] = (term2id(v), wife, husb)
for idx, val in individuals.items():
added_terms = ''
if val['sex'] == 'f':
parent_predicate, sibl_predicate = "isMotherOf", "isSisterOf"
else:
parent_predicate, sibl_predicate = "isFatherOf", "isBrotherOf"
if len(val['children']):
added_terms += " ;\n fhkb:" + parent_predicate + " " + ", ".join(["fhkb:" + i for i in val['children']])
if len(val['siblings']):
added_terms += " ;\n fhkb:" + sibl_predicate + " " + ", ".join(["fhkb:" + i for i in val['siblings']])
out.write("fhkb:%s a owl:NamedIndividual, owl:Thing%s ;\n rdfs:label \"%s\" .\n" % (idx, added_terms, val['title']))
for k, v in marriages.items():
out.write("fhkb:%s a owl:NamedIndividual, owl:Thing ;\n fhkb:hasFemalePartner fhkb:%s ;\n fhkb:hasMalePartner fhkb:%s .\n" % v)
out.write("[] a owl:AllDifferent ;\n owl:distinctMembers (")
for idx in individuals.keys():
out.write(" fhkb:" + idx)
for k, v in marriages.items():
out.write(" fhkb:" + v[0])
out.write(" ) .")
out.close()print("\n".join(Path("onto.ttl").read_text(encoding="utf-8").splitlines()[-12:]))### تنفيذ الاستدلال
الحين نبي نستخدم الأنطولوجيا للاستدلال والاستعلام. بنستخدم مكتبة [RDFLib](https://github.com/RDFLib)، وهي مكتبة تقرأ رسوم RDF البيانية بتنسيقات مختلفة وتستعلم منها.
وللاستدلال المنطقي بنستخدم مكتبة [OWL-RL](https://github.com/RDFLib/OWL-RL)، اللي تخلّينا نبني **الإغلاق** (Closure) لرسم RDF البياني؛ يعني نضيف كل المفاهيم والعلاقات الممكن استنتاجها.
# Dependencies are provisioned by this course edition's pinned runtime profile.
# Dependencies are provisioned by this course edition's pinned runtime profile.خلّونا نفتح ملف الأنطولوجيا ونشوف كم ثلاثية فيه:
import rdflib
from owlrl import DeductiveClosure, OWLRL_Extension
g = rdflib.Graph()
g.parse("onto.ttl", format="turtle")
print("Triplets found:%d" % len(g))الحين بنبني الإغلاق ونشوف كيف يزيد عدد الثلاثيات:
DeductiveClosure(OWLRL_Extension).expand(g)
print("Triplets after inference:%d" % len(g))### الاستعلام عن الأقارب
الحين نقدر نستعلم من الرسم البياني ونشوف العلاقات بين الأشخاص. بنستخدم لغة **SPARQL** مع الطريقة `query`. في مثالنا، خلّونا نطلع كل **الأعمام والأخوال** في شجرة العائلة:
qres = g.query(
"""SELECT DISTINCT ?aname ?bname
WHERE {
?a fhkb:isUncleOf ?b .
?a rdfs:label ?aname .
?b rdfs:label ?bname .
}""")
for row in qres:
print("%s is uncle of %s" % row)جرّبوا علاقات عائلية ثانية. مثلًا، طالعوا العلاقة `isAncestorOf`، اللي تعرّف كل أسلاف شخص معيّن بشكل تكراري.
وفي النهاية، خلّونا ننظف الملفات المؤقتة.
Path("onto.ttl").unlink(missing_ok=True)حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.