| 738 | | a <tt>SetID</tt>, and an <tt>Operand</tt>.</p> |
|---|
| | 738 | a <tt>SetID</tt>, and an <tt>Operand</tt>. Here's an example ruleset:</p> |
|---|
| | 739 | <table> |
|---|
| | 740 | <tr><th>Operation</th><th>SetID</th><th>Operand</th></tr> |
|---|
| | 741 | <tr><td>CREATE</td><td>1</td><td>Invoice</td></tr> |
|---|
| | 742 | <tr><td>FILTER</td><td>1</td><td>(Expression)</td></tr> |
|---|
| | 743 | <tr><td>CREATE</td><td>2</td><td>Inventory</td></tr> |
|---|
| | 744 | <tr><td>FILTER</td><td>2</td><td>(Expression)</td></tr> |
|---|
| | 745 | <tr><td>TRANSFORM</td><td>2</td><td>Invoice</td></tr> |
|---|
| | 746 | <tr><td>DIFFERENCE</td><td>1</td><td>2</td></tr> |
|---|
| | 747 | <tr><td>RETURN</td><td>1</td><td></td></tr> |
|---|
| | 748 | </table> |
|---|
| | 749 | |
|---|
| | 750 | <p>As you can see, every Rule operates on a <i>Set</i> of Units. The first |
|---|
| | 751 | rule is always to CREATE a set, declaring it to contain a certain Type |
|---|
| | 752 | of Units. In most cases, you will then FILTER that set. If you simply |
|---|
| | 753 | created a set and then returned it, it would contain all Units of the |
|---|
| | 754 | declared Type. When you filter a set, howevr, you remove Units from |
|---|
| | 755 | the whole which do not match the filter's Expression.</p> |
|---|
| | 756 | |
|---|
| | 757 | <p>In the example above, we CREATE a second Set so that we can eventually |
|---|
| | 758 | obtain the DIFFERENCE between Set 1 and Set 2. The second Set contains |
|---|
| | 759 | Units of a different Type than the first. Once we filter Set 2, we then |
|---|
| | 760 | TRANSFORM it; for each Inventory Unit, we look up associated Invoice |
|---|
| | 761 | Units. Then, we find the difference between the two Invoice sets and |
|---|
| | 762 | RETURN it.</p> |
|---|
| | 763 | |
|---|
| | 764 | <p>Rules are executed in order according to their <tt>Sequence</tt> |
|---|
| | 765 | attribute (lowest first). When you use the <tt>Engine.add_rule</tt> method, |
|---|
| | 766 | the next <tt>Sequence</tt> value is retrieved for you. Notice that each |
|---|
| | 767 | Rule belongs to one and only one Engine; they are not shared between |
|---|
| | 768 | Engines. Each Rule has its own <tt>EngineID</tt> attribute.</p> |
|---|
| 745 | | <p></p> |
|---|
| 746 | | |
|---|
| | 782 | <p>Dejavu includes various tools to help you manipulate groups of Units.</p> |
|---|
| | 783 | |
|---|
| | 784 | <h4>Sorting Units</h4> |
|---|
| | 785 | <p>When you recall Units, you receive a generator, and must iterate over |
|---|
| | 786 | the values in some way. Often, this is accomplished with a list |
|---|
| | 787 | comprehension: |
|---|
| | 788 | <pre>f = logic.Expression(lambda x: 'Aa' in x.Name) |
|---|
| | 789 | people = [x for x in sandbox.recall(Person, f)] |
|---|
| | 790 | </pre> |
|---|
| | 791 | However, the <tt>recall</tt> method doesn't do any sorting; you must sort |
|---|
| | 792 | your list in your Python code. Dejavu provides a <tt>sort(attrs, |
|---|
| | 793 | descending=False)</tt> function to assist you. It returns a function, which |
|---|
| | 794 | you can then use in Python's sort function. Continuing our example: |
|---|
| | 795 | <pre>sorted_people = people.sort(dejavu.sort('Size', 'Name'))</pre> |
|---|
| | 796 | The most important issue (and the reason we don't just use 2.4's attrgetter), |
|---|
| | 797 | is that any Unit property must allow values of None, which tends to raise |
|---|
| | 798 | errors when compared to values of other types. The function which |
|---|
| | 799 | <tt>sort</tt> creates for you treats None as "less than" any other value.</p> |
|---|
| | 800 | |
|---|
| | 801 | <h4>Cross-tabulation</h4> |
|---|
| | 802 | <p>Cross-tabs (also called <i>aggregate tables</i> or <i>pivot tables</i>) |
|---|
| | 803 | display aggregate information about objects by category. For example, |
|---|
| | 804 | rather than show a list of Safari records, one row per trip, you might |
|---|
| | 805 | wish to show a table where each row represents a Destination, and each |
|---|
| | 806 | column shows the count of Safaris to that Destination for each distinct |
|---|
| | 807 | Year. In this example, we say that the Safaris are "grouped by" their |
|---|
| | 808 | Destination values, and that we "pivot" on the Year values.</p> |
|---|
| | 809 | |
|---|
| | 810 | <p>Dejavu helps you form such a table via the <tt>CrossTab</tt> class. |
|---|
| | 811 | You need to specify the group(s) you wish to use, and the pivot attribute. |
|---|
| | 812 | Finally, you must specify the aggregate function. Here's a code example: |
|---|
| | 813 | <pre> |
|---|
| | 814 | >>> data = ["a", "b", "cc", "bddd", "a4", "b6"] |
|---|
| | 815 | >>> group = lambda x: x.isalpha() |
|---|
| | 816 | >>> pivot = lambda x: x[0] |
|---|
| | 817 | >>> ctab = analysis.CrossTab(data, [group], pivot, dejavu.COUNT) |
|---|
| | 818 | >>> data, columns = ctab.results() |
|---|
| | 819 | >>> data |
|---|
| | 820 | {(True,): {"a": 1, "b": 2, "c": 1}, |
|---|
| | 821 | (False,): {"a": 1, "b": 1}} |
|---|
| | 822 | >>> columns |
|---|
| | 823 | ["a", "b", "c"]</pre> |
|---|
| | 824 | You may notice that we're not using Units in our example; the |
|---|
| | 825 | <tt>CrossTab</tt> class is designed to work with any objects. Here's one |
|---|
| | 826 | way to lay out that data:</p> |
|---|
| | 827 | <table> |
|---|
| | 828 | <tr><th>Is Alpha</th><th>a</th><th>b</th><th>c</th></tr> |
|---|
| | 829 | <tr><td>Y</td><td>1</td><td>2</td><td>1</td></tr> |
|---|
| | 830 | <tr><td>N</td><td>1</td><td>1</td><td>0</td></tr> |
|---|
| | 831 | </table> |
|---|
| | 832 | |
|---|
| | 833 | <p>The <tt>results</tt> method returns two values. First, the table |
|---|
| | 834 | itself in the form of a dictionary; each key is a tuple of group values, |
|---|
| | 835 | and the corresponding value is a sub-dictionary. Each sub-dict has keys |
|---|
| | 836 | which are the pivot attribute, and values which equal the aggregates. |
|---|
| | 837 | I know, that was confusing; look at the example. The second value to |
|---|
| | 838 | be returned is a list of the pivot column values; you'll notice they're |
|---|
| | 839 | sorted.</p> |
|---|
| | 840 | |
|---|
| | 841 | <p>The groups and pivot arguments may be either strings or functions. |
|---|
| | 842 | If strings, they must be the names of attributes of the source objects. |
|---|
| | 843 | The final aggfunc argument defaults to COUNT, but may also be SUM. |
|---|
| | 844 | More aggfuncs may arrive in the future.</p> |
|---|