{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# About this notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook illustrates the working of LAF-Fabric, a tool to analyze the data inside *LAF* resources (Linguistic Annotation Framework). We use it for a particular LAF resource: the Hebrew Bible with linguistic annotations. The software to get the Hebrew Bible in LAF is part of LAF-Fabric (see *emdros2laf*).\n", "\n", "> **NB 1.** This is a static copy of the Gender notebook. You can download it, and if you have iPython installed and the LAF-Fabric, then you can run this notebook. And you can create many more notebooks like this, looking for patterns in the Hebrew Bible.\n", "\n", "> **NB 2.** All software involved is open source, and the data is Open Access (not for commercial use). Click the logo:\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Gender in the Hebrew Bible" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Words in Hebrew are either masculine, or feminine, or unknown.\n", "\n", "We want to plot the percentage of masculine and feminine words per chapter." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The LAF way\n", "In the Hebrew LAF data, some nodes are annotated as ``word``, and some nodes as ``chapter``\n", "(there are many more kinds of node, of course).\n", "\n", "The names of chapters and the genders of words are coded as features inside annotations to these\n", "nodes.\n", "\n", "## More on feature names\n", "The features we need are present in an annotation space named ``etcbc4`` (after the name and version of this LAF resource).\n", "The chapter features are labeled with ``sft`` and the other features with ``ft``.\n", "\n", "When LAF-Fabric compiles features into binary data, it forgets the annotations in which the features come,\n", "but the annotation *space* and *label* are retained in a double prefix to the feature name.\n", "\n", "LAF-Fabric remembers those features by their *fully qualified* names: ``etcbc4:ft.gender``, ``etcbc4:sft.chapter`` etc.\n", "There may also be annotations without feature contents." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Importing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next cell loads the required libraries and creates a task processor." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 0.00s This is LAF-Fabric 4.8.1\n", "API reference: http://laf-fabric.readthedocs.org/en/latest/texts/API-reference.html\n", "Feature doc: https://shebanq.ancient-data.org/static/docs/featuredoc/texts/welcome.html\n", "\n", " 0.00s DETAIL: Data dir = /Users/dirk/laf/laf-fabric-data\n", " 0.00s DETAIL: Laf dir = /Users/dirk/laf/laf-fabric-data\n", " 0.00s DETAIL: Output dir = /Users/dirk/laf/laf-fabric-output\n" ] } ], "source": [ "import sys\n", "import collections\n", "\n", "from laf.fabric import LafFabric\n", "fabric = LafFabric(verbose='DETAIL')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The processor needs data. Here is where we say what data to load. We do not need the XML identifiers as they show up in the original LAF resource. But we do need a few features of nodes, namely the ones that give us the gender of the words, and the numbers of the chapters and the books in which the chapters are contained.\n", "\n", "The *init* function actually draws that data in, and it will take a few seconds. \n", "\n", "It needs to know the name of the **source**. \n", "This name corresponds with a subdirectory in your *work_dir*.\n", "\n", "The '--' means that we do not draw in an **annox** (extra annotation package). \n", "If you want to do that, this is the place to give the name of such a package, which must be a subdirectory name inside the *annotations* directory in your *work_dir*.\n", "\n", "Then **gender** is just a name we choose to give to this task.\n", "This name determines where on the filesystem the log file and output (if any) will be put:\n", "a subdirectory *gender* inside the **source** directory inside your *output_dir*.\n", "\n", "The last argument to ``load()`` is a dictionary of data items to load.\n", "\n", "The **primary** key indicates whether the primary data itself must be loaded.\n", "Tasks can then use methods to find the primary data that is attached to a node.\n", "For the Hebrew data this is hardly necessary, because the words have textual information as features on them.\n", "\n", "The **xmlids** are tables mapping nodes and edges to the original xml identifiers they have in the original LAF source.\n", "Most tasks do not need this.\n", "Only when a task needs to link new annotations to nodes and edges and write the result as an additional LAF file,\n", "it needs to know the original identifiers.\n", "\n", "The **features** to be loaded are specified by two strings, one for node features and one for edge features.\n", "For all these features, data will be loaded, and all other features' data will be unloaded, if still loaded.\n", "\n", "**Caution: Missing feature data**\n", "\n", "If you forget to mention a feature in the load declaration and you\n", "do use it in your task,\n", "LAF-Fabric will stop your task and shout error messages at you.\n", "If you declare features that do not exist in the LAF data, you just get\n", "a warning. But if you try to use such features, you get also a loud error." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 0.00s LOADING API: please wait ... \n", " 0.00s USING main: etcbc4b DATA COMPILED AT: 2015-11-02T15-08-56\n", " 2.01s LOGFILE=/Users/dirk/laf/laf-fabric-output/etcbc4b/gender/__log__gender.txt\n", " 2.01s INFO: DATA LOADED FROM SOURCE etcbc4b AND ANNOX FOR TASK gender AT 2016-09-09T14-48-44\n" ] } ], "source": [ "fabric.load('etcbc4b', '--', 'gender',\n", "{\n", " \"primary\": False,\n", " \"xmlids\": {\"node\": False, \"edge\": False},\n", " \"features\": (\"otype gn chapter book\", \"\"),\n", "})\n", "exec(fabric.localnames.format(var='fabric'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to write an efficient task,\n", "it is convenient to import the names of the API methods as *local variables*.\n", "The lookup of names in Python is fastest for local names.\n", "And it makes the code much cleaner. The method ``load()`` does this.\n", "See the [API reference](http://laf-fabric.readthedocs.org/texts/API-reference.html) for full documentation.\n", "\n", "## F\n", "All that you want to know about features and are not afraid to ask.\n", "It is an object, and for each feature that you have declared, it has a member\n", "with a handy name. For example,\n", "\n", " F.etcbc4_db_otype\n", "\n", "is a feature object\n", "that corresponds with the LAF feature given in an annotation \n", "in the annotation space ``etcbc4``, with label ``db`` and name ``otype``.\n", "It is a node feature, because otherwise we had to use ``FE`` instead of ``F``.\n", "\n", "You do not have to mention the annotation space and label, Laf-Fabric will find out what they should be\n", "given the available features. If there is confusion, Laf-Fabric will tell you, and you can supply\n", "more full names.\n", "\n", "You can look up a feature value of this feature, say for node ``n``, by saying\n", "\n", " F.otype.v(n) \n", "\n", "## NN(test=function value=something values=list of somethings)\n", "If you want to walk through all the nodes, possibly skipping some, then this is your method.\n", "It is an *iterator* that yields a new node everytime it is called.\n", "The order is so-called *primary data order*, which will be explained below.\n", "The ``test`` and ``value`` and ``values`` arguments are optional.\n", "If given, ``test`` should be a *callable* with one argument, returning a string;\n", "``value`` should be a string, ``values`` a list of strings.\n", "``test`` will be called for each passing node,\n", "and if the value returned is not equal to the given ``value`` and not a member of ``values``,\n", "the node will be skipped.\n", "\n", "### msg\n", "Issues a timed message to the standard error and to the log file.\n", "\n", "### infile(filename)\n", "Creates a open file handle for reading a file in your task output directory \n", "\n", "### outfile(filename)\n", "Creates a open file handle for writing a file in your task output directory\n", "\n", "### my_file(filename)\n", "Gives the full path to a file in your task output directory" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Available features\n", "\n", "The *F_all* component delivers a list of available node features in the chosen source, \n", "and like wise *FE_all* yields the edge features.\n", "Let us see what we have got. For convenience, the components *fF_all* and *fFE_all* produce formatted outputs for\n", "these feature lists." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "etcbc4:\n", "\tdb.maxmonad:\n", "\tdb.minmonad:\n", "\tdb.monads:\n", "\tdb.oid:\n", "\tdb.otype:\n", "\tft.code:\n", "\tft.det:\n", "\tft.dist:\n", "\tft.dist_unit:\n", "\tft.domain:\n", "\tft.function:\n", "\tft.g_cons:\n", "\tft.g_cons_utf8:\n", "\tft.g_lex:\n", "\tft.g_lex_utf8:\n", "\tft.g_nme:\n", "\tft.g_nme_utf8:\n", "\tft.g_pfm:\n", "\tft.g_pfm_utf8:\n", "\tft.g_prs:\n", "\tft.g_prs_utf8:\n", "\tft.g_uvf:\n", "\tft.g_uvf_utf8:\n", "\tft.g_vbe:\n", "\tft.g_vbe_utf8:\n", "\tft.g_vbs:\n", "\tft.g_vbs_utf8:\n", "\tft.g_word:\n", "\tft.g_word_utf8:\n", "\tft.gn:\n", "\tft.is_root:\n", "\tft.kind:\n", "\tft.language:\n", "\tft.lex:\n", "\tft.lex_utf8:\n", "\tft.ls:\n", "\tft.mother_object_type:\n", "\tft.nme:\n", "\tft.nu:\n", "\tft.number:\n", "\tft.pdp:\n", "\tft.pfm:\n", "\tft.prs:\n", "\tft.ps:\n", "\tft.rela:\n", "\tft.sp:\n", "\tft.st:\n", "\tft.tab:\n", "\tft.trailer_utf8:\n", "\tft.txt:\n", "\tft.typ:\n", "\tft.uvf:\n", "\tft.vbe:\n", "\tft.vbs:\n", "\tft.vs:\n", "\tft.vt:\n", "\tsft.book:\n", "\tsft.chapter:\n", "\tsft.label:\n", "\tsft.verse:\n", "etcbc4:\n", "\tft.distributional_parent:\n", "\tft.functional_parent:\n", "\tft.mother:\n", "laf:\n", "\t('', 'x'):\n", "\t('', 'y'):\n" ] } ], "source": [ "print(fF_all)\n", "print(fFE_all)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Task Execution" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to get an output file to write to.\n", "A simple method provides a handle to a file open for writing.\n", "The file will be created in the *output_dir*, under the subdir *etcbc4*, under the subdir *gender*." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "table = outfile('table.tsv')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All open files (reading and writing) will be closed with\n", "\n", " close()\n", "\n", "below." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Walking the nodes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we loop over a bunch of nodes (in fact over all nodes), in a convenient document order.\n", "\n", "### Node order\n", "There is an implicit partial order on nodes.\n", "The short story is: the nodes that are linked to primary data, inherit the order that is present\n", "in the primary data.\n", "The long story is a bit more complicated, since nodes may be attached to multiple ranges of \n", "primary data.\n", "\n", "See [node order](http://laf-fabric.readthedocs.org/texts/API-reference.html#node-order) for details.\n", "If you don't, it might be enough to know\n", "that *embedding* nodes always come before *embedded* nodes, meaning that if a node happens \n", "to be attached to a big piece of primary data, and a second node to a part of that data,\n", "then the node with the bigger attachment comes first.\n", "\n", "When there is no inclusion either way, and the start and end points are the same, the order is left undefined. \n", "\n", "### Initialization\n", "We initialize the counters in which we store the word counts.\n", "We keep track of the chapter we are in and accumulate counts of the words, masculine and feminine.\n", "For each chapter we create entries in the *ch*, *m* and *f* lists.\n", "\n", "Note also the progress messages after each chapter." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "stats = [0, 0, 0]\n", "cur_chapter = None\n", "cur_book = None\n", "ch = []\n", "m = []\n", "f = []" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\n", "Genesis 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50\n", "Exodus 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40\n", "Leviticus 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27\n", "Numeri 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36\n", "Deuteronomium 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34\n", "Josua 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24\n", "Judices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21\n", "Samuel_I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31\n", "Samuel_II 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24\n", "Reges_I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22\n", "Reges_II 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25\n", "Jesaia 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66\n", "Jeremia 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52\n", "Ezechiel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48\n", "Hosea 1 2 3 4 5 6 7 8 9 10 11 12 13 14\n", "Joel 1 2 3 4\n", "Amos 1 2 3 4 5 6 7 8 9\n", "Obadia 1\n", "Jona 1 2 3 4\n", "Micha 1 2 3 4 5 6 7\n", "Nahum 1 2 3\n", "Habakuk 1 2 3\n", "Zephania 1 2 3\n", "Haggai 1 2\n", "Sacharia 1 2 3 4 5 6 7 8 9 10 11 12 13 14\n", "Maleachi 1 2 3\n", "Psalmi 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150\n", "Iob 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42\n", "Proverbia 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31\n", "Ruth 1 2 3 4\n", "Canticum 1 2 3 4 5 6 7 8\n", "Ecclesiastes 1 2 3 4 5 6 7 8 9 10 11 12\n", "Threni 1 2 3 4 5\n", "Esther 1 2 3 4 5 6 7 8 9 10\n", "Daniel 1 2 3 4 5 6 7 8 9 10 11 12\n", "Esra 1 2 3 4 5 6 7 8 9 10\n", "Nehemia 1 2 3 4 5 6 7 8 9 10 11 12 13\n", "Chronica_I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n", "Chronica_II 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36" ] } ], "source": [ "for node in NN():\n", " otype = F.otype.v(node)\n", " if otype == \"word\":\n", " stats[0] += 1\n", " if F.gn.v(node) == \"m\":\n", " stats[1] += 1\n", " elif F.gn.v(node) == \"f\":\n", " stats[2] += 1\n", " elif otype == \"chapter\":\n", " if cur_chapter != None:\n", " masc = 0 if not stats[0] else 100 * float(stats[1]) / stats[0]\n", " fem = 0 if not stats[0] else 100 * float(stats[2]) / stats[0]\n", " ch.append(cur_chapter)\n", " m.append(masc)\n", " f.append(fem)\n", " table.write(\"{},{},{}\\n\".format(cur_chapter, masc, fem))\n", " else:\n", " table.write(\"{},{},{}\\n\".format('book chapter', 'masculine', 'feminine'))\n", " this_book = F.book.v(node)\n", " this_chapnum = F.chapter.v(node)\n", " this_chapter = \"{} {}\".format(this_book, this_chapnum)\n", " if this_book != cur_book:\n", " sys.stderr.write(\"\\n{}\".format(this_book))\n", " cur_book = this_book\n", " sys.stderr.write(\" {}\".format(this_chapnum))\n", " stats = [0, 0, 0]\n", " cur_chapter = this_chapter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Closing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to close open files. This is exactly what the next statement does." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 22s Results directory:\n", "/Users/dirk/SURFdrive/laf-fabric-output/etcbc4b/gender\n", "\n", "__log__gender.txt 217 Fri Nov 13 14:09:01 2015\n", "table.tsv 43234 Fri Nov 13 14:09:01 2015\n" ] } ], "source": [ "close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Showing off" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Everything is still in memory. Now it is the time to generate a graphical representation of the data.\n", "\n", "The *matplotlib* package is full of instruments to do that.\n", "\n", "But let us first have a look at a few rows of the data itself." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import pandas\n", "import matplotlib.pyplot as plt\n", "from IPython.display import display\n", "pandas.set_option('display.notebook_repr_html', True)\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The files that have been generated reside in a subdirectory of your work directory.\n", "You can easily refer to them as follows:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "table_file = my_file('table.tsv')\n", "df = pandas.read_csv(table_file)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "
\n", " | book chapter | \n", "masculine | \n", "feminine | \n", "
---|---|---|---|
0 | \n", "Genesis 1 | \n", "42.347697 | \n", "5.794948 | \n", "
1 | \n", "Genesis 2 | \n", "38.663968 | \n", "7.692308 | \n", "
2 | \n", "Genesis 3 | \n", "37.474950 | \n", "10.020040 | \n", "
3 | \n", "Genesis 4 | \n", "43.046358 | \n", "11.920530 | \n", "
4 | \n", "Genesis 5 | \n", "40.748441 | \n", "18.918919 | \n", "
5 | \n", "Genesis 6 | \n", "36.613272 | \n", "9.610984 | \n", "
6 | \n", "Genesis 7 | \n", "33.596838 | \n", "11.462451 | \n", "
7 | \n", "Genesis 8 | \n", "31.300813 | \n", "9.959350 | \n", "
8 | \n", "Genesis 9 | \n", "37.972167 | \n", "9.741551 | \n", "
9 | \n", "Genesis 10 | \n", "30.679157 | \n", "4.683841 | \n", "
10 | \n", "Genesis 11 | \n", "38.416988 | \n", "15.057915 | \n", "
11 | \n", "Genesis 12 | \n", "31.151832 | \n", "10.209424 | \n", "
12 | \n", "Genesis 13 | \n", "36.994220 | \n", "3.757225 | \n", "
13 | \n", "Genesis 14 | \n", "40.393013 | \n", "4.148472 | \n", "
14 | \n", "Genesis 15 | \n", "36.187845 | \n", "5.524862 | \n", "
15 | \n", "Genesis 16 | \n", "29.794521 | \n", "22.945205 | \n", "
16 | \n", "Genesis 17 | \n", "41.299790 | \n", "11.111111 | \n", "
17 | \n", "Genesis 18 | \n", "34.028892 | \n", "8.346709 | \n", "
18 | \n", "Genesis 19 | \n", "30.900243 | \n", "10.705596 | \n", "
19 | \n", "Genesis 20 | \n", "35.638298 | \n", "11.702128 | \n", "
20 | \n", "Genesis 21 | \n", "35.559265 | \n", "12.520868 | \n", "
21 | \n", "Genesis 22 | \n", "38.132296 | \n", "5.252918 | \n", "
22 | \n", "Genesis 23 | \n", "38.873995 | \n", "9.115282 | \n", "
23 | \n", "Genesis 24 | \n", "32.801822 | \n", "12.984055 | \n", "
24 | \n", "Genesis 25 | \n", "42.572464 | \n", "10.326087 | \n", "
25 | \n", "Genesis 26 | \n", "36.930091 | \n", "8.662614 | \n", "
26 | \n", "Genesis 27 | \n", "42.129630 | \n", "7.754630 | \n", "
27 | \n", "Genesis 28 | \n", "36.444444 | \n", "8.444444 | \n", "
28 | \n", "Genesis 29 | \n", "30.745342 | \n", "18.012422 | \n", "
29 | \n", "Genesis 30 | \n", "32.655654 | \n", "15.374841 | \n", "
... | \n", "... | \n", "... | \n", "... | \n", "
70 | \n", "Exodus 21 | \n", "40.703518 | \n", "10.050251 | \n", "
71 | \n", "Exodus 22 | \n", "39.555556 | \n", "8.000000 | \n", "
72 | \n", "Exodus 23 | \n", "38.218924 | \n", "6.493506 | \n", "
73 | \n", "Exodus 24 | \n", "45.187166 | \n", "4.010695 | \n", "
74 | \n", "Exodus 25 | \n", "33.438986 | \n", "15.055468 | \n", "
75 | \n", "Exodus 26 | \n", "33.152909 | \n", "18.809202 | \n", "
76 | \n", "Exodus 27 | \n", "37.333333 | \n", "18.666667 | \n", "
77 | \n", "Exodus 28 | \n", "39.756098 | \n", "13.048780 | \n", "
78 | \n", "Exodus 29 | \n", "36.707566 | \n", "6.952965 | \n", "
79 | \n", "Exodus 30 | \n", "38.473520 | \n", "11.059190 | \n", "
80 | \n", "Exodus 31 | \n", "31.104651 | \n", "10.174419 | \n", "
81 | \n", "Exodus 32 | \n", "40.183246 | \n", "4.450262 | \n", "
82 | \n", "Exodus 33 | \n", "40.120968 | \n", "2.016129 | \n", "
83 | \n", "Exodus 34 | \n", "42.469471 | \n", "4.884668 | \n", "
84 | \n", "Exodus 35 | \n", "36.060606 | \n", "11.363636 | \n", "
85 | \n", "Exodus 36 | \n", "34.505208 | \n", "17.578125 | \n", "
86 | \n", "Exodus 37 | \n", "34.637965 | \n", "16.634051 | \n", "
87 | \n", "Exodus 38 | \n", "35.598706 | \n", "16.343042 | \n", "
88 | \n", "Exodus 39 | \n", "39.303483 | \n", "11.815920 | \n", "
89 | \n", "Exodus 40 | \n", "38.923077 | \n", "4.923077 | \n", "
90 | \n", "Leviticus 1 | \n", "38.186813 | \n", "4.945055 | \n", "
91 | \n", "Leviticus 2 | \n", "39.007092 | \n", "16.312057 | \n", "
92 | \n", "Leviticus 3 | \n", "38.418079 | \n", "6.497175 | \n", "
93 | \n", "Leviticus 4 | \n", "37.997433 | \n", "10.654685 | \n", "
94 | \n", "Leviticus 5 | \n", "32.947020 | \n", "13.245033 | \n", "
95 | \n", "Leviticus 6 | \n", "38.927739 | \n", "13.519814 | \n", "
96 | \n", "Leviticus 7 | \n", "35.714286 | \n", "13.025210 | \n", "
97 | \n", "Leviticus 8 | \n", "36.829559 | \n", "7.985697 | \n", "
98 | \n", "Leviticus 9 | \n", "36.686391 | \n", "7.692308 | \n", "
99 | \n", "Leviticus 10 | \n", "40.652174 | \n", "7.173913 | \n", "
100 rows × 3 columns
\n", "