{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# RDF Containers\n",
    "\n",
    "In this notebook I will\n",
    "\n",
    "1. Explain RDF Containers\n",
    "2. Include test cases for [gastrodon](https://github.com/paulhoule/gastrodon) functions that support containers\n",
    "3. Demonstrate the use of inference rules to simplify queries against RDF Containers\n",
    "\n",
    "[RDF Containers](http://www.w3.org/TR/rdf-schema/#ch_containervocab) are one of two mechanisms to represent ordered collections in RDF:\n",
    "\n",
    "* **Containers** represent the order of members using predicate names that contain the sequence numbers of the members;  they work much like an ArrayList in Java.\n",
    "* **Collections** represent the order of members using a linked list similar to lists in LISP or like the LinkedList in Java.\n",
    "\n",
    "If you're developing a new application,  you may need to choose to use one or the other for a particular use.  From the viewpoint of this article,  however,  I'm working with a dataset that already uses one or the other,  and the goal is to write queries againt it.\n",
    "\n",
    "(Note:  the words \"container\" and \"collection\" are usedly loosely in things you will read,  for instance,  people might refer to the Python list as being a \"container\" or being a \"collection\".  A java `List` is a subclass of `Collection`, whereas the equivalent `vector` in C++ is defined in the `container` library,  for instance.  People writing natural languages are always going to be ambiguous,  and it's a challenge to be simultaneously correct and comprehensible.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "As always,  I start by importing sumbols and configuring pandas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autotime\n",
    "import sys\n",
    "from gastrodon import *\n",
    "from rdflib import *\n",
    "import pandas as pd\n",
    "pd.options.display.width=120\n",
    "pd.options.display.max_colwidth=100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Representing Sets\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just to make things clear,  I'll first show what happens if we simply link one topic to another with a predicate,  without using either a Container or Collection.  First,  I create a model that represents the five boroughs of New York City"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 7 ms\n"
     ]
    }
   ],
   "source": [
    "boros=inline(r\"\"\"\n",
    "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
    "@prefix : <http://example.com/> .\n",
    "\n",
    ":New_York_City\n",
    "   :boro :Manhattan,:Queens,:Brooklyn,:Bronx,:Staten_Island .\n",
    "\"\"\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the comma is a shorthand notation that lets me write a number of statements that share the same predicate and object.  A **Graph** implements `__iter__`,  so I can get all of the facts in it like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(rdflib.term.URIRef('http://example.com/New_York_City'),\n",
       "  rdflib.term.URIRef('http://example.com/boro'),\n",
       "  rdflib.term.URIRef('http://example.com/Queens')),\n",
       " (rdflib.term.URIRef('http://example.com/New_York_City'),\n",
       "  rdflib.term.URIRef('http://example.com/boro'),\n",
       "  rdflib.term.URIRef('http://example.com/Manhattan')),\n",
       " (rdflib.term.URIRef('http://example.com/New_York_City'),\n",
       "  rdflib.term.URIRef('http://example.com/boro'),\n",
       "  rdflib.term.URIRef('http://example.com/Bronx')),\n",
       " (rdflib.term.URIRef('http://example.com/New_York_City'),\n",
       "  rdflib.term.URIRef('http://example.com/boro'),\n",
       "  rdflib.term.URIRef('http://example.com/Staten_Island')),\n",
       " (rdflib.term.URIRef('http://example.com/New_York_City'),\n",
       "  rdflib.term.URIRef('http://example.com/boro'),\n",
       "  rdflib.term.URIRef('http://example.com/Brooklyn'))]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 10.5 ms\n"
     ]
    }
   ],
   "source": [
    "list(boros.graph)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just as there are five boroughs,  there are five facts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 6.5 ms\n"
     ]
    }
   ],
   "source": [
    "len(boros.graph)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now I make a `LocalEndpoint` which will re|nder SPARQL query results as pandas `DataFrame`(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>boro</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>:Queens</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>:Manhattan</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>:Staten_Island</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>:Brooklyn</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>:Bronx</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             boro\n",
       "0         :Queens\n",
       "1      :Manhattan\n",
       "2  :Staten_Island\n",
       "3       :Brooklyn\n",
       "4          :Bronx"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 1.34 s\n"
     ]
    }
   ],
   "source": [
    "boros.select(\"\"\"\n",
    "    SELECT ?boro { :New_York_City :boro ?boro}\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the order that the facts come back in the SPARQL query is random,  because RDF doesn't remember the order in which statements were made.  This is the right behavior in this case,  because the boroughs do not come in any particular order,  although we can order them alphabetically,  by population,  or some other metric,  so long as we have the data in the graph and write the right SPARQL query:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>boro</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>:Bronx</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>:Brooklyn</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>:Manhattan</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>:Queens</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>:Staten_Island</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             boro\n",
       "0          :Bronx\n",
       "1       :Brooklyn\n",
       "2      :Manhattan\n",
       "3         :Queens\n",
       "4  :Staten_Island"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 24 ms\n"
     ]
    }
   ],
   "source": [
    "boros.select(\"\"\"\n",
    "    SELECT ?boro \n",
    "        { :New_York_City :boro ?boro}\n",
    "    ORDER BY ?boro\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another characteristic of a set is that a given topic can only be listed once.  For instance,  if we repeat the same fact over and over again,  RDF will only capture it once:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 5.51 ms\n"
     ]
    }
   ],
   "source": [
    "boros=inline(r\"\"\"\n",
    "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
    "@prefix : <http://example.com/> .\n",
    "\n",
    ":New_York_City\n",
    "   :boro :Manhattan,:Manhattan,:Manhattan .\n",
    "\"\"\")\n",
    "\n",
    "len(boros.graph)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>boro</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>:Manhattan</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         boro\n",
       "0  :Manhattan"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 20 ms\n"
     ]
    }
   ],
   "source": [
    "boros.select(\"\"\"\n",
    "    SELECT ?boro { :New_York_City :boro ?boro}\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A simple sequence example\n",
    "\n",
    "Next I create a list of three items.  This RDF graph has four statements in it,  one for the `rdf:Seq` type,  and three for the members of the list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 15.5 ms\n"
     ]
    }
   ],
   "source": [
    "sequence=inline(r\"\"\"\n",
    "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
    "[] a rdf:Seq ;\n",
    "    rdf:_1 \"Beginning\" ;\n",
    "    rdf:_2 \"Middle\" ;\n",
    "    rdf:_3 \"End\" .\n",
    "\"\"\").graph\n",
    "\n",
    "len(sequence)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "rdflib comes with definitions for classes and predicates in common namespaces such as `<http://www.w3.org/1999/02/22-rdf-syntax-ns#>`;  since traditional RDF tools won't give an error if you misspell a resource URI,  you should use these to avoid mistakes.\n",
    "\n",
    "(In most cases,  if you misspell a predicate or resource name,  your queries will get no results)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq'),\n",
       " rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'))"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 9.5 ms\n"
     ]
    }
   ],
   "source": [
    "RDF.Seq,RDF.type"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I used a blank node to 'name' the list,  so I need a reference to the list to work with.\n",
    "\n",
    "The rdflib `Graph` object (the sequence below) uses the Python Slice operator in an unusual way;  the fields of the slice mean:\n",
    "\n",
    "`graph[subject:object:predicate]`\n",
    "\n",
    "so if I specify the object and predicate and leave the subject out,  I get the matching subjects.  The `one` function picks out the first and only element of the resulting list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "rdflib.term.BNode('ub3bL2C1')"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 9.5 ms\n"
     ]
    }
   ],
   "source": [
    "lhs=one(sequence[:RDF.type:RDF.Seq])\n",
    "lhs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 12.5 ms\n"
     ]
    }
   ],
   "source": [
    "endpoint=LocalEndpoint(sequence)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sometimes you might want to turn an RDF Container into a Python list so you can work on it with Python.  You can do this with the `decollect` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Beginning', 'Middle', 'End']"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 63.5 ms\n"
     ]
    }
   ],
   "source": [
    "endpoint.decollect(lhs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once you've converted a list to Python,  you can take the length with the `len` function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 42.5 ms\n"
     ]
    }
   ],
   "source": [
    "len(endpoint.decollect(lhs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What if we want to write a SPARQL query to get the length?  There isn't a SPARQL function to get the length of a list,  but we can write our own.  One thing I might try is counting the statements for which the container is the subject."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cnt</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   cnt\n",
       "0    4"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 26 ms\n"
     ]
    }
   ],
   "source": [
    "endpoint.select('''\n",
    "    SELECT (COUNT(*) AS ?cnt) {\n",
    "        ?s ?p ?o .\n",
    "    }\n",
    "''',bindings=dict(s=lhs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Close,  but no cigar.  I got four instead of three because it counted the statement that\n",
    "\n",
    "`[] a rdf:Seq`\n",
    "    \n",
    "which has nothing to do with the list members.  Thus,  I need to write a query that skips this statement.\n",
    "\n",
    "One way to do that is to apply the negation operator to remove any `a` statements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cnt</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   cnt\n",
       "0    3"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 30.5 ms\n"
     ]
    }
   ],
   "source": [
    "endpoint.select('''\n",
    "    SELECT (COUNT(*) AS ?cnt) {\n",
    "        ?s ?p ?o .\n",
    "        MINUS {?s a ?o}\n",
    "    }\n",
    "''')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The above query works in this case,  and works no matter how many types are associated with the container.  Nothing stops people from adding more statements where the container is the subject,  and in that case we'd get a count that's too high.  The following query is better,  because it selects exactly for predicates of the form `rdf:_...`.\n",
    "\n",
    "It's ugly however,  and still not 100% compliant with the standard because only predicates of the form `rdf:_{number}` (where the number does not start with zero) are container membership predicates.  On the other hand,  if somebody writes RDF like that they are asking for trouble..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cnt</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   cnt\n",
       "0    3"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 37 ms\n"
     ]
    }
   ],
   "source": [
    "endpoint.select('''\n",
    "    SELECT (COUNT(*) AS ?cnt) {\n",
    "        ?s ?p ?o .\n",
    "        FILTER(STRSTARTS(STR(?p),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "    } \n",
    "''')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A more complex case"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 15.5 ms\n"
     ]
    }
   ],
   "source": [
    "duo=inline(r\"\"\"\n",
    "@prefix : <http://example.com/> .\n",
    "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
    "\n",
    ":simple a rdf:Seq ;\n",
    "    rdf:_1 \"uno\" ;\n",
    "    rdf:_2 \"dos\" ;\n",
    "    rdf:_3 3 ;\n",
    "    rdf:_4 <http://dbpedia.org/resource/4> .\n",
    "\n",
    ":complex a rdf:Seq ;\n",
    "    rdf:_1 [\n",
    "        a rdf:Seq ;\n",
    "            rdf:_1 33 ;\n",
    "            rdf:_2 91 ;\n",
    "            rdf:_3 15 \n",
    "        ] ;\n",
    "    rdf:_2 [\n",
    "        a rdf:Seq ;\n",
    "            rdf:_1 541 ;\n",
    "            rdf:_2 3 \n",
    "        ].    \n",
    "\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "RDF and SPARQL let you look at lists in a different way from most languages.  For instance,  the following query finds all of the lists in the model and counts how many members each have.  Two of the lists have URI names,  the other two are the containers inside `:complex` which are represented as blank nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cnt</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>s</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>ub4bL11C12</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>:simple</th>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ub4bL17C12</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>:complex</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            cnt\n",
       "s              \n",
       "ub4bL11C12    3\n",
       ":simple       4\n",
       "ub4bL17C12    2\n",
       ":complex      2"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 56.5 ms\n"
     ]
    }
   ],
   "source": [
    "duo.select(\"\"\"\n",
    "    SELECT ?s (COUNT(*) AS ?cnt) {\n",
    "        ?s ?p ?o .\n",
    "        FILTER(STRSTARTS(STR(?p),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "    } GROUP BY ?s\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another kind of query you can write looks for all the containers that contain a certain value,  for instance,  the number 3."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>s</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>:simple</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ub4bL17C12</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            s\n",
       "0     :simple\n",
       "1  ub4bL17C12"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 30 ms\n"
     ]
    }
   ],
   "source": [
    "duo.select(\"\"\"\n",
    "    SELECT ?s {\n",
    "        ?s ?p 3 .\n",
    "        FILTER(STRSTARTS(STR(?p),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "    }\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It starts getting ugly though,  if you want to write a query that involves more than one list,  say,  lists that are nested.  For instance,  to list the items in `:complex` (not in order),  the query is"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>member</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>541</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>91</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>15</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>33</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   member\n",
       "0     541\n",
       "1       3\n",
       "2      91\n",
       "3      15\n",
       "4      33"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 58.5 ms\n"
     ]
    }
   ],
   "source": [
    "duo.select(\"\"\"\n",
    "    SELECT ?member {\n",
    "        :complex ?p1 ?innerList .\n",
    "        ?innerList ?p2 ?member .\n",
    "        FILTER(STRSTARTS(STR(?p1),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "        FILTER(STRSTARTS(STR(?p2),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "    }\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Decollecting a single list from the model is simple;  the decollect method automatically converts RDF terms into native Python data types (strings and integers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['uno', 'dos', 3, rdflib.term.URIRef('http://dbpedia.org/resource/4')]"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 49.5 ms\n"
     ]
    }
   ],
   "source": [
    "duo.decollect(URIRef(\"http://example.com/simple\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Counting to Eleven"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This next example tests the decollect code;  particularly it checks that we sort the container membership properties in numerical order (as opposed to alphabetic,  which would go [1,10,11,2,...]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 8.5 ms\n"
     ]
    }
   ],
   "source": [
    "sequence_11=inline(r\"\"\"\n",
    "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
    "@prefix : <http://example.com/> .\n",
    "\n",
    ":s11 a rdf:Seq ;\n",
    "    rdf:_1 \"one\" ;\n",
    "    rdf:_2 \"two\" ;\n",
    "    rdf:_3 \"three\" ;\n",
    "    rdf:_4 \"four\" ;\n",
    "    rdf:_5 \"five\" ;\n",
    "    rdf:_6 \"six\" ;\n",
    "    rdf:_7 \"seven\" ;\n",
    "    rdf:_8 \"eight\" ;\n",
    "    rdf:_9 \"nine\" ;\n",
    "    rdf:_10 \"ten\" ;\n",
    "    rdf:_11 \"eleven\" .\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['one',\n",
       " 'two',\n",
       " 'three',\n",
       " 'four',\n",
       " 'five',\n",
       " 'six',\n",
       " 'seven',\n",
       " 'eight',\n",
       " 'nine',\n",
       " 'ten',\n",
       " 'eleven']"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 70 ms\n"
     ]
    }
   ],
   "source": [
    "goes_to_eleven=sequence_11.decollect(URIRef(\"http://example.com/s11\"))\n",
    "goes_to_eleven"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 2 ms\n"
     ]
    }
   ],
   "source": [
    "assert goes_to_eleven[0]==\"one\"\n",
    "assert goes_to_eleven[1]==\"two\"\n",
    "assert goes_to_eleven[10]==\"eleven\"\n",
    "assert len(goes_to_eleven)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just to see how you could get it wrong,  the following query gives the wrong answer because RDF resources sort in alphabetical order:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>member</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ten</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>eleven</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>three</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>four</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>five</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>six</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>seven</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>eight</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>nine</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    member\n",
       "0      one\n",
       "1      ten\n",
       "2   eleven\n",
       "3      two\n",
       "4    three\n",
       "5     four\n",
       "6     five\n",
       "7      six\n",
       "8    seven\n",
       "9    eight\n",
       "10    nine"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 65.5 ms\n"
     ]
    }
   ],
   "source": [
    "sequence_11.select(\"\"\"\n",
    "   SELECT ?member {\n",
    "      :s11 ?index ?member\n",
    "      FILTER(STRSTARTS(STR(?index),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "   } ORDER BY(?index)\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Don't be that guy!\n",
    "\n",
    "To get the right order,  convert the indexes to numbers,  like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>member</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>two</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>three</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>four</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>five</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>six</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>seven</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>eight</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>nine</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>ten</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>eleven</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    member\n",
       "0      one\n",
       "1      two\n",
       "2    three\n",
       "3     four\n",
       "4     five\n",
       "5      six\n",
       "6    seven\n",
       "7    eight\n",
       "8     nine\n",
       "9      ten\n",
       "10  eleven"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 65 ms\n"
     ]
    }
   ],
   "source": [
    "sequence_11.select(\"\"\"\n",
    "   SELECT ?member {\n",
    "      :s11 ?index ?member\n",
    "      FILTER(STRSTARTS(STR(?index),\"http://www.w3.org/1999/02/22-rdf-syntax-ns#_\"))\n",
    "      BIND(xsd:integer(SUBSTR(STR(?index),45)) AS ?number)\n",
    "   } ORDER BY(?number)\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Bags\n",
    "\n",
    "RDF has two additional container types,  `rdf:Bag` and `rdf:Alt`.  In both cases we have enough information to return an ordered list,  but `rdf:Bag` is a statement that the order of the elements does not matter,  while `rdf:Alt` is a statement that the application should choose one of the values.  (An example of that could be labels in different languages)\n",
    "\n",
    "`decollect` treats an `rdf:Alt` the same way it treats an `rdf:Seq`,  and it would treat a container that is missing like a list too.  (I'll leave it you which alternative you want to choose)  `decollect` has special treatment for bags.  For an example,  I make a bag that contains the words from a sentence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 5 ms\n"
     ]
    }
   ],
   "source": [
    "laurie=inline(r\"\"\"\n",
    "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
    "@prefix : <http://example.com/> .\n",
    "\n",
    ":from_the_air a rdf:Bag ;\n",
    "    rdf:_1 \"this\" ;\n",
    "    rdf:_2 \"is\" ;\n",
    "    rdf:_3 \"the\" ;\n",
    "    rdf:_4 \"time\" ;\n",
    "    rdf:_5 \"and\" ;\n",
    "    rdf:_6 \"this\" ;\n",
    "    rdf:_7 \"is\" ;\n",
    "    rdf:_8 \"the\" ;\n",
    "    rdf:_9 \"record\" ;\n",
    "    rdf:_10 \"of\" ;\n",
    "    rdf:_11 \"the\" ;\n",
    "    rdf:_12 \"time\" .\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python has a built-in collection type called `Counter` which intended to represent bags,  so `decollect` converts a bag to a `Counter`,  giving us a nice example of a \"bag of words\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'and': 1,\n",
       "         'is': 2,\n",
       "         'of': 1,\n",
       "         'record': 1,\n",
       "         'the': 3,\n",
       "         'this': 2,\n",
       "         'time': 2})"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 118 ms\n"
     ]
    }
   ],
   "source": [
    "laurie.decollect(URIRef(\"http://example.com/from_the_air\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Array Index Offsets\n",
    "\n",
    "Some people argue over the Red Sox and the Yankees.  Some people argue whether you should break an egg on the big end or the little end.  Microprocessor designers make different choices about the order of bytes in larger words.\n",
    "\n",
    "One thing programming languages have long disagreed about is how to define array indexes.\n",
    "\n",
    "In FORTRAN,  the first element of an array has an index of 1.  In C,  the first element of an array has an index of 0.  Neither one is right or wrong,  but these are two different conventions that you will encounter.\n",
    "\n",
    "As seen in the examples above,  the native indexes of RDF Containers start at one.  On the other hand,  Python lists start at zero:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'first'"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 3.5 ms\n"
     ]
    }
   ],
   "source": [
    "x=[\"first\",\"second\",\"third\"]\n",
    "x[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nothing prevents a library in a language like Python from indexing lists any way it wants,  but Pandas frames behave like Python lists, both in how they are displayed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>0</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>25</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>or</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    0\n",
       "0  25\n",
       "1  or\n",
       "2   6\n",
       "3   2\n",
       "4   4"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 15 ms\n"
     ]
    }
   ],
   "source": [
    "x=pd.DataFrame([25,\"or\",6,2,4])\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "in how in they are accessed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 13 ms\n"
     ]
    }
   ],
   "source": [
    "x.at[0,0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's an obvious and very possible mistake that you could want to access (say) the third item of a list,  and get confused as to it being a Python list where one would write\n",
    "\n",
    "```\n",
    "x[2]\n",
    "```\n",
    "\n",
    "as opposed to a SPARQL query in which one would write\n",
    "\n",
    "```\n",
    "?list rdf:_3 ?member .\n",
    "```\n",
    "\n",
    "There are times when one might have a Python variable with a numeric value and want to use it to index a RDF collection,  and that is why gastrodon has a `member` function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#_1')"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 13 ms\n"
     ]
    }
   ],
   "source": [
    "member(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Which can be used as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>word</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>three</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    word\n",
       "0  three"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 18 ms\n"
     ]
    }
   ],
   "source": [
    "idx=2   # the third word!\n",
    "sequence_11.select(\"\"\"\n",
    "   SELECT ?word { :s11 ?index ?word . }\n",
    "\"\"\",bindings=dict(index=member(idx)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Thus if you are writing Python in the Python world,  `member` lets you use Python indexing."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Conclusion\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This article is a quick introduction to RDF Containers,  one of two ways of representing ordered collections in RDF.  I covered both the RDF Containers themselves,  and also the facilities in `gastrodon` to work with them to do tasks such as:\n",
    "\n",
    "* turn a RDF `:Seq` or `:Alt` to an ordinary Python list\n",
    "* turn an RDF `:Bag` into a Python `Counter`\n",
    "* write SPARQL queries that \n",
    "  * count the members of a list\n",
    "  * iterate over the members of a list\n",
    "  * work with multiple-leveled lists, and\n",
    "  * select individual members\n",
    "  \n",
    "this notebook is part of the unit tests for gastrodon and is part of a series."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table width=\"600\" align=\"center\"> \n",
    "    <tbody>\n",
    "     <tr>\n",
    "      <td style=\"background: navy; align:center\">\n",
    "       <div style=\"color: white; text-align:center\">\n",
    "         This article is part of a series.<br>\n",
    "         Subscribe to my mailing list to be notified when new installments come out.\n",
    "        <br>&nbsp;\n",
    "        <br> \n",
    "        <form action=\"https://legalentityidentifier.info/lei/submitForm\" method=\"post\"> \n",
    "         <input name=\"email\" value=\"\"> \n",
    "         <input type=\"hidden\" name=\"what\" value=\"rs-book\"> \n",
    "         <input type=\"hidden\" name=\"v\" value=\"24c0e1c0-ab8a-4068-806d-71e6db4ca6fc\"> \n",
    "         <input type=\"submit\" name=\"action\" value=\"Subscribe!\" style=\"color: black\"> \n",
    "        </form> \n",
    "       </div></td>\n",
    "     </tr> \n",
    "    </tbody>\n",
    "   </table><script>\n",
    "  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n",
    "  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n",
    "  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n",
    "  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\n",
    "\n",
    "  ga('create', 'UA-3262918-17', 'auto');\n",
    "  ga('send', 'pageview');\n",
    "</script>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
