Tables: A Guide for the Perplexed |
Most user interaction with the Protege GUI is done via forms. Each class has a form associated to it, and each instance is simply a filled out form. For example, the Editor class has an associated form, and instances of Editor are displayed as filled-out forms. This approach facilitates structured data entry for instances. More generally, it simplifies local interaction with the knowledge base (e.g., looking at and editing small number of instances in great detail). Often, however, there is additional structure in the knowledge base, which the standard forms-based interface and the default widget set do not adequately represent.
Tables are complex widgets, designed to help elicit and visualize functional relationships in a knowledge base. Most widgets are entirely user-interface devices which don't make any ontological commitments. For example, you can choose to use a ContainsWidget instead of an InstanceListWidget without making any ontological changes or changing, in any way, the underlying semantics of the knowledge base. The only commitment implied by the use of ContainsWidget is that a particular slot has a value type of instance.
Table widgets, on the other hand, reflect an underlying functional relationship in the knowledge base. In order to use table widgets, you must structure your knowledge base in a certain way. The tables knowledge base, which is required by the table widgets, contains non-trivial conceptual definitions which you must use, and extend, in order to use table widgets. This means that the decision to use tables is not lightly made, and that using table widgets in projects and knowledge bases which weren't explciitly designed to be used with them might be problematic.
To make this more concrete, we're going to walk through an example, adding the ability to use a table widget to an existing project. In this case, we will be adapting the newspaper project.
Step 1: Load the existing project
We will use the newspaper project.
Step 2: Make sure the tables project is included
The next step is to make sure that the project being modified includes the tables project (the tables project contains a few key concept definitions the table widgets require). The tables project is included in your Protege-2000 distribution, as part of the examples directory. To see if the tables project is already included, you can use the Project menu and choose the Show Included Projects option. If the tables project is not included, you can include it by using the Include... option on the Project menu.
Step 3: Decide what functional relationships should exist in the knowledge base
We will use a table widget to visualize pricing structures. The cost of an advertisement depends upon the size of the ad and the section of the paper it is placed in. This means that cost is a function (of two variables) and therefore, once the knowledge-base structure is definedd, the ideal candidate for visualization in a table widget.
Step 4: Define the domain of the underlying function
The first step in using table widgets is to define the domain of the function you are representing. The domain is currently restricted to two axes (eventually we will allow more than two dimensions) and each axis must be an enumerable finite set. For example, both "the integers between 0 and 10" and "all subclasses of a particular class" are valid domain axes (they're finite sets, and can be enumerated). As is using a set of symbols (defined as the allowed values of a slot that has a value type of symbol). Sets like "floating point numbers between 0 and 10" or "strings" are not valid domain axes.
In our example, we've defined two term hierarchies inside the class hierachy, and we will use these to define axes. That is, one axis will be subclasses of Section. And the other will be subclasses of Size of advertisement.
Using the class hierarchy to define axes is a common design decision. The main reason for using the class hierarchy to do this, rather than simply defining a set of symbols, is that symbols are defined locally, on each slot. If more than one slot is referencing the same set of terms, using a class hierarchy simplifies maintenance. This is because a single change in terminology is enacted by a single change to term hierarchy. If the terms were encoded using symbols, the change would have to be made at each slot which took values from the symbol set.
Step 5: Define the range of the underlying function
The next step is to decide what the range of the underlying function is. If it is going to be instances of a class, you need to define that class. In our case, however, we're simply going to use floating point numbers to represent price. So our function will encode information like:
The standard price of a Single page advertisement in the Lifestyles
section is 450.0
The standard price of a Single page advertisement in the Science
section is 495.0
The standard price of a Single page advertisement in the Local News
section is 625.0
Step 6: Decide what type of entries to use
Defining the domain
axes, and deciding on a range value type, is tantamount to defining three slots.
That is, the above information could be viewed as
filling out the values for the slots size, section, and standard
price in three case.
The logical next question to consider is, therefore, what class or classes to attach these slots to. The decision boils down to this: is there a single conceptual entity containing both the domain and range values ? Or are the domain and range values conceptually distinct entities which happen to be in a functional relationship.
An example of the latter might be a table containing mortality rates. There is a functional relationship between a person's height and weight, and their expected lifespan. That is, there is a domain object (Person) with two slots that participate in the functional relationship. The domain, considered as a whole, is a conceptually distinct entity.
In our case, the domain consists of a section and a size. There is no clear conceptual reason to create a class (SectionAndSize ?) aside from the fact that it would useful as a domain for this functional relationship. So, we will create an entry class that has both domain and range slots attached to it. To do this, we need to subclass SingleObjectEntry (an abstract class contained in the tables knowledge base), making sure that our new class, BillingChartEntry, is an instance of EntryMetaclass.
We also add three own slots, corresponding to our domain and range slots, to the class we've just defined, and set their facets accordingly.
Side-note: If we were using separate domain and range objects, then our definition of BillingChartEntry would be slightly different. In this scenario, we would create a domain class, SectionAndSize and attach the slots section and size to it. After which, we would create a range class definition, Price, and attach the slot standard-price to it. BillingChartEntry would be a subclass of DomainRangeObjectPairEntry (instead of SingleObjectEntry) and we'd need to set the allowed-values facet of :domain-value and :range-value to reflect our domain and range classes. Finally, BillingChartEntry would still need to be an instance of EntryMetaclass. This scenario is pictured here.
Step 7: Annotate the Entry class definition by filling out the own slots
Instances of BillingChartEntry will embody individual statements of the type discussed in Step 5. That is, for each sentence like
The standard price of a Single page advertisement in the Lifestyles section is 450.0
there will be an instance of BillingChartEntry in the knowledge base. But we still have to tell the widget, and record in the knowledge base, which slots are domain slots and which slots are range slot. E.g., there is no apriori reason, so far, that the interpretation of an instance of BillingChartEntry couldn't be
The section of a 450.0 with size Single page is Lifestyles
The way we record this information is by using the own slots that BillingChartEntry acquires from being an instance of EntryMetaClass. There are two slots on BillingChartEntry: :domain-value-slots and :range-value-slot. :domain-value-slots stores pointers to two slots (which must be attached to BillingChartEntry) which hold the domain values. And :range-value-slot stores a pointer to the slot (which must be attached to BillingChartEntry) that holds the range value. The fully configured BillingChartEntry class can be viewed here.
Side-note: If we were using separate domain and range objects, we would still do much the same thing: :domain-value-slots and :range-value-slot would still be own slots attached to BillingChartEntry (since BillingChartEntry would still be an instance of EntryMetaclass). The only difference is that, instead of pointing to slots attached to BillingChartEntry, they would point to slots attached to the associated value objects (namely SectionAndSize and Price).
Step 8: Create the functional class, including a slot for "entries"
At this point, we've created enough ontological structure to accurately represent sentences such as
The standard price of a Single page advertisement in the Lifestyles section is 450.0
The next step is
to represent the entire function.That is, we need to define a class which holds
a lot of individual entries (the function is the set of all the entries). This
is relatively easy to do-- we simply create a subclass of BoundedDomain_EnumeratedFunction
(a class defined in the datastructues knowledge base) and attach a slot
which holds multiple instances of our entry class. In our
case, we define BillingChart and attach the slot rates.
Note that the class we define must also be an instance of EnumeratedRelationMetaclass
Step 9: Annotate the functional class definition by filling out the own slots
The final ontological
step is to say which slot holds the entries. The class BillingChart has
an own slot, :entry-slot, which it got by being an instance of EnumeratedRelationMetaclass.
We set this slot to point at rates.
Step 10: Choose which slots to display and select the table widget
After all this ontological work, which boiled down to deifning a functional relationship between concepts in our knowledge base, we still need to configure the forms. The first step in doing so is going to the form for the BillingChart and selecting the table widget for the slot rates.
Step 11: Configure the table widget
Double-clicking on the table widget brings up a panel with an extensive number of configuration options. These options allow the table widget to display different types of information in different ways and enable options such as in-place editing.
Step 12: Acquire instances
Finally, after all this hard work, knowledge-acquisition can actually take place. Here is a screenshot of the table widget in action.
Step 13: Send feedback to the Protege group
The table widgets
are some of the more complex, and cumbersome, widgets that ships with Protege.
As you use them, you will undoubtedly notice many shortcomings. We'd like to
hear about them. The appropriate venue for comments and discussions is the protege-discussion
mailing list.
The core conceptual
definitions for tables are contained in the tables knowledge base. There are
9 core classes which the table widgets use:
Class Name | What it Really Represents | Role in tables Knowledge Base | Role during Ontology Design | Role during Knowledge Acquisition |
TableEntity | Nothing. DataStructure has no conceptual content at all. | DataStructure is only used to help organize the diagrams knowledge base ontology-- all other classes in the diagrams knowledge base are subclasses of DataStructure. | None. | None. |
EnumeratedRelation | None. | EnumeratedRelation is an abstract superclass of EnumeratedFunction, and so plays some role in structuring the knowledge base. But it has no direct role in knowledge acquisition. | ||
EnumeratedRelationMetaclass | Subclasses of BoundedDomainEnumeratedFunction should be instances of EnumeratedRelationMetaclass (rather than :STANDARD-CLASS). :entry_slot, which will be an own slot on the subclass of BoundedDomain_EnumeratedFunction, must have its value set to a slot which is attached to the subclass of BoundedDomain_EnumeratedFunction being configured. | The value of entries is used by the various table widgets. From the user's point of view, once the ontology is defined, EnumeratedRelationMetaclasscan be ignored. | ||
EnumeratedFunction | None. | EnumeratedFunction is an abstract superclass of BoundedDomain_EnumeratedFunction, and so plays some role in structuring the knowledge base. But it has no direct role in knowledge acquisition. | ||
BoundedDomain_EnumeratedFunction | Domain classes which will use one of the table widgets must be subclasses of BoundedDomain_EnumeratedFunction. Subclasses of BoundedDomain_EnumeratedFunction must also be instances of EnumeratedRelationMetaclass in order for the table widgets to function properly. | Instances of BoundedDomain_EnumeratedFunction, or its subclasses, will be acquired from the user. | ||
Entry | None. | An instance of BoundedDomain_EnumeratedFunction consists of some domain specific information (which the table widgets don't care about) and a slot which points to multiple instances of some subclass of Entry. These instances are generated by the table widgets, in response to user actions, and, taken as a whole, constitute the actual functional relationship being modeled. | ||
EntryMetaclass | Subclasses of either DomainRangeObjectPairEntry or SingleObjectEntry should be instances of EntryMetaclass (rather than :STANDARD-CLASS). This will cause the slots :domain_value_slots and :range_value_slots to be attached to the subclass as own slots. | The values of :domain_value_slots and :range_value_slots are used by the various table widgets to construct the user interface. From the user's point of view, once the ontology is defined, EntryMetaclass can be ignored. | ||
DomainRangeObjectPairEntry | One of the two defined subclasses of Entry. DomainRangeObjectPairEntry carries the semantics that the domain and range of the function are encapsulated in separate objects (and that the role of an instance of the entry class for the function is to contain pointers to an instance of the domain class and an instance of the range class in its :domain-value and :range-value slots). When DomainRangeObjectPairEntry is used as the base class for the functional entry class, the interpretation of :domain_value_slots and :range_value_slots is that they refer to slots on the domain and range objects (not to slots on the functional entry class). | The user doesn't have to consciously consider that she is creating instances of some subclass of DomainRangeObjectPairEntry. Instead, these are created automatically by the table widget and stored in the knowledge base. | ||
SingleObjectEntry | One of the two defined subclasses of Entry. SingleObjectEntry carries the semantics that the domain and range of the function don't actually exist as separate objects, but instead are simply slot values on the functional entry class (and that, therefore, the role of an instance of the entry class is to encapsulate some part of the function). When SingleObjectEntry is used as the base class for the functional entry class, the interpretation of :domain_value_slots and :range_value_slots is that they refer to slots on the functional entry class. | The user doesn't have to consciously consider that she is creating instances of some subclass of SingleObjectEntry. Instead, these are created automatically by the table widget and stored in the knowledge base. |