From 435d107677c7ab1323c5fd3e257d6d8d441bee61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Fernando=20S=C3=A1nchez?= Date: Wed, 17 Jul 2019 16:29:30 +0200 Subject: [PATCH] Add headers and minor fixes --- CHANGELOG.md | 10 +- ...ckstart-30minutes.ipynb => Advanced.ipynb} | 982 +++------- docs/Evaluation.ipynb | 592 ++++++ docs/Quickstart-10minutes.ipynb | 1513 --------------- docs/Quickstart-1minute.ipynb | 152 -- docs/Quickstart.ipynb | 1666 ++++++++++++++++- docs/advanced.rst | 10 - docs/commandline.rst | 10 - docs/conf.py | 1 + docs/conversion.rst | 173 +- docs/demo.rst | 2 +- docs/development.rst | 1 + docs/eval_table.png | Bin 0 -> 78822 bytes docs/index.rst | 85 +- docs/installation.rst | 18 +- docs/plugins-definition.rst | 23 +- docs/plugins-faq.rst | 2 +- docs/plugins-quickstart.rst | 3 +- docs/projects.rst | 49 + docs/publications.rst | 14 +- docs/server-cli.rst | 18 +- example-plugins/async_plugin.py | 16 + example-plugins/basic.py | 16 + example-plugins/basic_analyse_entry_plugin.py | 15 + example-plugins/basic_box_plugin.py | 15 + example-plugins/basic_plugin.py | 16 + example-plugins/configurable_plugin.py | 16 + example-plugins/dummy_plugin.py | 16 + example-plugins/dummy_required_plugin.py | 16 + example-plugins/emorand_plugin.py | 16 + example-plugins/mynoop.py | 16 + example-plugins/parameterized_plugin.py | 16 + example-plugins/rand_plugin.py | 16 + example-plugins/sklearn/mydata.py | 16 + example-plugins/sklearn/mypipeline.py | 16 + example-plugins/sklearn/pipeline_plugin.py | 16 + example-plugins/sleep_plugin.py | 16 + senpy/__init__.py | 5 +- senpy/__main__.py | 3 +- senpy/api.py | 16 + senpy/blueprints.py | 21 +- senpy/cli.py | 15 + senpy/client.py | 16 + senpy/extensions.py | 41 +- senpy/gsitk_compat.py | 38 +- senpy/meta.py | 15 + senpy/models.py | 15 + senpy/plugins/__init__.py | 37 +- senpy/plugins/misc/split_plugin.py | 16 + .../postprocessing/emotion/centroids.py | 24 +- .../postprocessing/emotion/ekman2vad.senpy | 18 +- .../emotion/maxEmotion_plugin.py | 16 + .../{sentiment140 => }/sentiment140_plugin.py | 16 + senpy/static/js/main.js | 14 +- senpy/templates/index.html | 81 +- senpy/testing.py | 16 + senpy/utils.py | 15 + senpy/version.py | 18 +- setup.py | 36 +- tests/test_api.py | 16 + tests/test_blueprints.py | 16 + tests/test_cli.py | 16 + tests/test_client.py | 16 + tests/test_extensions.py | 16 + tests/test_models.py | 16 + tests/test_plugins.py | 16 + tests/test_schemas.py | 16 + tests/test_semantics.py | 16 + tests/test_test.py | 16 + 69 files changed, 3638 insertions(+), 2592 deletions(-) rename docs/{Quickstart-30minutes.ipynb => Advanced.ipynb} (90%) create mode 100644 docs/Evaluation.ipynb delete mode 100644 docs/Quickstart-10minutes.ipynb delete mode 100644 docs/Quickstart-1minute.ipynb delete mode 100644 docs/advanced.rst delete mode 100644 docs/commandline.rst create mode 100644 docs/eval_table.png create mode 100644 docs/projects.rst rename senpy/plugins/sentiment/{sentiment140 => }/sentiment140_plugin.py (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0f0583..536392d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.0.1] +### Added +* License headers +* Description for PyPI (setup.py) +### Changed +* The evaluation tab shows datasets inline, and a tooltip shows the number of instances +* The docs should be clearer now + +## [1.0.0] ### Fixed * Restored hash changing function in `main.js` diff --git a/docs/Quickstart-30minutes.ipynb b/docs/Advanced.ipynb similarity index 90% rename from docs/Quickstart-30minutes.ipynb rename to docs/Advanced.ipynb index d733d48..d376794 100644 --- a/docs/Quickstart-30minutes.ipynb +++ b/docs/Advanced.ipynb @@ -4,20 +4,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Senpy in 30 minutes" + "# Advanced features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "This tutorial takes up where the [10 minute tutorial](Quickstart-10minutes.ipynb) left off.\n", + "This tutorial takes up where the [basic tutorial](Quickstart.ipynb) left off.\n", "\n", "It covers more advanced tasks such as:\n", "\n", - "* Asking for specific emotion models (automatic model conversion)\n", "* Listing available services in an endpoint\n", - "* Transforming the results\n", + "* Transforming the results of a service\n", "* Calling multiple services in the same request (Pipelines)\n", "* Running your own Senpy instance" ] @@ -47,25 +46,22 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import requests\n", - "try:\n", - " from IPython.display import Code\n", - " def pretty(txt, language='json-ld'):\n", - " return Code(txt, language=language)\n", - "except ImportError:\n", - " def pretty(txt, **kwargs):\n", - " print(txt)" + "from IPython.display import Code\n", + " \n", + "def query(endpoint, raw=False, **kwargs):\n", + " '''Query a given Senpy endpoint with specific parameters, and prettify the output'''\n", + " res = requests.get(endpoint,\n", + " params=kwargs)\n", + " if raw:\n", + " return res\n", + " return Code(res.text, language=kwargs.get('outformat', 'json-ld'))" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -87,22 +83,101 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "[\"Senpy is a wonderful service\"]\n" - ] + "data": { + "text/html": [ + "
["Senpy is a wonderful service"]\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{[}\\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service\\PYZdq{}}\\PY{p}{]}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "[\"Senpy is a wonderful service\"]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\"input\": \"Senpy is a wonderful service\",\n", - " \"fields\": 'entries[].\"nif:isString\"'})\n", - "print(res.text)" + "query(f'{endpoint}/sentiment140',\n", + " input=\"Senpy is a wonderful service\",\n", + " fields='entries[].\"nif:isString\"')" ] }, { @@ -114,23 +189,102 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "[\"Senpy is a service. Wonderful service.\", \"marl:Neutral\"]\n" - ] + "data": { + "text/html": [ + "
["Senpy is a service. Wonderful service.", "marl:Neutral"]\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{[}\\PY{l+s+s2}{\\PYZdq{}Senpy is a service. Wonderful service.\\PYZdq{}}\\PY{p}{,} \\PY{l+s+s2}{\\PYZdq{}marl:Neutral\\PYZdq{}}\\PY{p}{]}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "[\"Senpy is a service. Wonderful service.\", \"marl:Neutral\"]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\"input\": \"Senpy is a service. Wonderful service.\",\n", - " \"delimiter\": \"sentence\",\n", - " \"fields\": 'entries[0].[\"nif:isString\", \"marl:hasOpinion\"[0].\"marl:hasPolarity\"]'})\n", - "print(res.text)" + "query(f'{endpoint}/sentiment140',\n", + " input=\"Senpy is a service. Wonderful service.\",\n", + " delimiter=\"sentence\",\n", + " fields='entries[0].[\"nif:isString\", \"marl:hasOpinion\"[0].\"marl:hasPolarity\"]')" ] }, { @@ -147,77 +301,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Emotion conversion" + "## Emotion conversion with field selection" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "If the model used by a plugin is not right for your application, you can ask for a specific emotion model in your request.\n", - "\n", - "Senpy ships with emotion conversion capabilities, and it will try to automatically convert the results.\n", - "\n", - "For example, the `emotion-anew` plugin uses the dimensional `pad` (or VAD, valence-arousal-dominance) model, as we can see here:" + "We could mix emotion conversion with field selection to only get the label of an emotion analysis that has been automatically converted:" ] }, { "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQj\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [],\n", - " \"nif:isString\": \"Senpy is a wonderful service and I love it\",\n", - " \"onyx:hasEmotionSet\": [\n", - " {\n", - " \"@id\": \"Emotions0\",\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@id\": \"Emotion0\",\n", - " \"@type\": \"Emotion\",\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 6.44,\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 7.11,\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 8.72,\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.775705\"\n", - " }\n", - " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.775705\"\n", - " }\n", - " ]\n", - " }\n", - " ]\n", - "}\n" - ] - } - ], - "source": [ - "res = requests.get(f'{endpoint}/emotion-anew',\n", - " params={\"input\": \"Senpy is a wonderful service and I love it\"})\n", - "print(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we need a category level, we can ask for the equivalent results in the `big6` model:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -291,485 +387,29 @@ ".output_html .vg { color: #19177C } /* Name.Variable.Global */\n", ".output_html .vi { color: #19177C } /* Name.Variable.Instance */\n", ".output_html .vm { color: #19177C } /* Name.Variable.Magic */\n", - ".output_html .il { color: #666666 } /* Literal.Number.Integer.Long */
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbi1tb2RlbD1lbW9tbCUzQWJpZzYj",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [],\n",
-       "      "nif:isString": "Senpy is a wonderful service and I love it",\n",
-       "      "onyx:hasEmotionSet": [\n",
-       "        {\n",
-       "          "@id": "Emotions0",\n",
-       "          "@type": "EmotionSet",\n",
-       "          "onyx:hasEmotion": [\n",
-       "            {\n",
-       "              "@id": "Emotion0",\n",
-       "              "@type": "Emotion",\n",
-       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal": 6.44,\n",
-       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance": 7.11,\n",
-       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence": 8.72,\n",
-       "              "prov:wasGeneratedBy": "prefix:Analysis_1554382982.8068626"\n",
-       "            }\n",
-       "          ],\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554382982.8068626"\n",
-       "        },\n",
-       "        {\n",
-       "          "@type": "EmotionSet",\n",
-       "          "onyx:hasEmotion": [\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:algorithmConfidence": 7.449999999999999,\n",
-       "              "onyx:hasEmotionCategory": "emoml:big6fear"\n",
-       "            }\n",
-       "          ],\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554382982.8106468"\n",
-       "        }\n",
-       "      ]\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
+       ".output_html .il { color: #666666 } /* Literal.Number.Integer.Long */
[["Senpy is a wonderful service and I love it"]]\n",
        "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbi1tb2RlbD1lbW9tbCUzQWJpZzYj\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotions0\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion0\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}arousal\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{6.44}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}dominance\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.11}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}valence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{8.72}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382982.8068626\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382982.8068626\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:algorithmConfidence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.449999999999999}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}emoml:big6fear\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382982.8106468\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", + "\\PY{p}{[}\\PY{p}{[}\\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{]}\\PY{p}{]}\n", "\\end{Verbatim}\n" ], "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbi1tb2RlbD1lbW9tbCUzQWJpZzYj\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [],\n", - " \"nif:isString\": \"Senpy is a wonderful service and I love it\",\n", - " \"onyx:hasEmotionSet\": [\n", - " {\n", - " \"@id\": \"Emotions0\",\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@id\": \"Emotion0\",\n", - " \"@type\": \"Emotion\",\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 6.44,\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 7.11,\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 8.72,\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.8068626\"\n", - " }\n", - " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.8068626\"\n", - " },\n", - " {\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:algorithmConfidence\": 7.449999999999999,\n", - " \"onyx:hasEmotionCategory\": \"emoml:big6fear\"\n", - " }\n", - " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.8106468\"\n", - " }\n", - " ]\n", - " }\n", - " ]\n", - "}" + "[[\"Senpy is a wonderful service and I love it\"]]" ] }, - "execution_count": 14, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/emotion-anew',\n", - " params={\"input\": \"Senpy is a wonderful service and I love it\",\n", - " \"emotion-model\": \"emoml:big6\"})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because we don't usually care about the original emotion, the conversion can be presented in three ways:\n", - "\n", - "* full: the original and converted emotions are included at the same level\n", - "* filtered: the original emotion is replaced by the converted emotion\n", - "* nested: the original emotion is replaced, but the converted emotion points to it\n", - "\n", - "For example, here's how the `nested` structure would look like:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbi1tb2RlbD1lbW9tbCUzQWJpZzYmY29udmVyc2lvbj1uZXN0ZWQj",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [],\n",
-       "      "nif:isString": "Senpy is a wonderful service and I love it",\n",
-       "      "onyx:hasEmotionSet": [\n",
-       "        {\n",
-       "          "@type": "EmotionSet",\n",
-       "          "onyx:hasEmotion": [\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:algorithmConfidence": 7.449999999999999,\n",
-       "              "onyx:hasEmotionCategory": "emoml:big6fear"\n",
-       "            }\n",
-       "          ],\n",
-       "          "prov:wasDerivedFrom": {\n",
-       "            "@id": "Emotions0",\n",
-       "            "@type": "EmotionSet",\n",
-       "            "onyx:hasEmotion": [\n",
-       "              {\n",
-       "                "@id": "Emotion0",\n",
-       "                "@type": "Emotion",\n",
-       "                "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal": 6.44,\n",
-       "                "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance": 7.11,\n",
-       "                "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence": 8.72,\n",
-       "                "prov:wasGeneratedBy": "prefix:Analysis_1554382982.8948743"\n",
-       "              }\n",
-       "            ],\n",
-       "            "prov:wasGeneratedBy": "prefix:Analysis_1554382982.8948743"\n",
-       "          },\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554382982.8985674"\n",
-       "        }\n",
-       "      ]\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbi1tb2RlbD1lbW9tbCUzQWJpZzYmY29udmVyc2lvbj1uZXN0ZWQj\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:algorithmConfidence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.449999999999999}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}emoml:big6fear\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasDerivedFrom\\PYZdq{}}\\PY{p}{:} \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotions0\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion0\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}arousal\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{6.44}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}dominance\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.11}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}valence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{8.72}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382982.8948743\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382982.8948743\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382982.8985674\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbi1tb2RlbD1lbW9tbCUzQWJpZzYmY29udmVyc2lvbj1uZXN0ZWQj\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [],\n", - " \"nif:isString\": \"Senpy is a wonderful service and I love it\",\n", - " \"onyx:hasEmotionSet\": [\n", - " {\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:algorithmConfidence\": 7.449999999999999,\n", - " \"onyx:hasEmotionCategory\": \"emoml:big6fear\"\n", - " }\n", - " ],\n", - " \"prov:wasDerivedFrom\": {\n", - " \"@id\": \"Emotions0\",\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@id\": \"Emotion0\",\n", - " \"@type\": \"Emotion\",\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 6.44,\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 7.11,\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 8.72,\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.8948743\"\n", - " }\n", - " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.8948743\"\n", - " },\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382982.8985674\"\n", - " }\n", - " ]\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = requests.get(f'{endpoint}/emotion-anew',\n", - " params={\"input\": \"Senpy is a wonderful service and I love it\",\n", - " \"emotion-model\": \"emoml:big6\",\n", - " \"conversion\": \"nested\"})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, for completion, we could get only the label with the `fields` parameter:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
[["Senpy is a wonderful service and I love it", "emoml:big6fear"]]\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{[}\\PY{p}{[}\\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{,} \\PY{l+s+s2}{\\PYZdq{}emoml:big6fear\\PYZdq{}}\\PY{p}{]}\\PY{p}{]}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "[[\"Senpy is a wonderful service and I love it\", \"emoml:big6fear\"]]" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = requests.get(f'{endpoint}/emotion-anew',\n", - " params={\"input\": \"Senpy is a wonderful service and I love it\",\n", - " \"emotion-model\": \"emoml:big6\",\n", - " \"fields\": 'entries[].[[\"nif:isString\",\"onyx:hasEmotionSet\"[].\"onyx:hasEmotion\"[].\"onyx:hasEmotionCategory\"][]][]',\n", - " \"conversion\": \"filtered\"})\n", - "pretty(res.text)" + "query(f'{endpoint}/emotion-anew',\n", + " input=\"Senpy is a wonderful service and I love it\",\n", + " emotionmodel=\"emoml:big6\",\n", + " fields='entries[].[[\"nif:isString\",\"onyx:hasEmotionSet\"[].\"onyx:hasEmotion\"[].\"onyx:hasEmotionCategory\"][]][]',\n", + " conversion=\"filtered\")" ] }, { @@ -793,7 +433,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -878,7 +518,7 @@ " {\n", " "@type": "Sentiment",\n", " "marl:hasPolarity": "marl:Neutral",\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382983.2991712"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369539.5176148"\n", " }\n", " ],\n", " "nif:isString": "Senpy is a wonderful service",\n", @@ -927,7 +567,7 @@ " "onyx:hasEmotionIntensity": 0.09950234435617733\n", " }\n", " ],\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382983.3010163"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369539.5185866"\n", " }\n", " ]\n", " }\n", @@ -948,7 +588,7 @@ " \\PY{p}{\\PYZob{}}\n", " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Neutral\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382983.2991712\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369539.5176148\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service\\PYZdq{}}\\PY{p}{,}\n", @@ -997,7 +637,7 @@ " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.09950234435617733}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382983.3010163\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369539.5185866\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\n", " \\PY{p}{\\PYZcb{}}\n", @@ -1017,7 +657,7 @@ " {\n", " \"@type\": \"Sentiment\",\n", " \"marl:hasPolarity\": \"marl:Neutral\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382983.2991712\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369539.5176148\"\n", " }\n", " ],\n", " \"nif:isString\": \"Senpy is a wonderful service\",\n", @@ -1066,7 +706,7 @@ " \"onyx:hasEmotionIntensity\": 0.09950234435617733\n", " }\n", " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382983.3010163\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369539.5185866\"\n", " }\n", " ]\n", " }\n", @@ -1074,15 +714,14 @@ "}" ] }, - "execution_count": 17, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/sentiment140/emotion-depechemood',\n", - " params={\"input\": \"Senpy is a wonderful service\"})\n", - "pretty(res.text)" + "query(f'{endpoint}/sentiment140/emotion-depechemood',\n", + " input=\"Senpy is a wonderful service\")" ] }, { @@ -1102,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -1187,7 +826,7 @@ " {\n", " "@type": "Sentiment",\n", " "marl:hasPolarity": "marl:Positive",\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382983.5748618"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369539.7878842"\n", " }\n", " ],\n", " "nif:isString": "Senpy is awesome. And services are composable.",\n", @@ -1200,7 +839,7 @@ " {\n", " "@type": "Sentiment",\n", " "marl:hasPolarity": "marl:Positive",\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382983.5748618"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369539.7878842"\n", " }\n", " ],\n", " "nif:isString": "Senpy is awesome.",\n", @@ -1213,7 +852,7 @@ " {\n", " "@type": "Sentiment",\n", " "marl:hasPolarity": "marl:Neutral",\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382983.5748618"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369539.7878842"\n", " }\n", " ],\n", " "nif:isString": "And services are composable.",\n", @@ -1236,7 +875,7 @@ " \\PY{p}{\\PYZob{}}\n", " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382983.5748618\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369539.7878842\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome. And services are composable.\\PYZdq{}}\\PY{p}{,}\n", @@ -1249,7 +888,7 @@ " \\PY{p}{\\PYZob{}}\n", " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382983.5748618\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369539.7878842\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome.\\PYZdq{}}\\PY{p}{,}\n", @@ -1262,7 +901,7 @@ " \\PY{p}{\\PYZob{}}\n", " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Neutral\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382983.5748618\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369539.7878842\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}And services are composable.\\PYZdq{}}\\PY{p}{,}\n", @@ -1284,7 +923,7 @@ " {\n", " \"@type\": \"Sentiment\",\n", " \"marl:hasPolarity\": \"marl:Positive\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382983.5748618\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369539.7878842\"\n", " }\n", " ],\n", " \"nif:isString\": \"Senpy is awesome. And services are composable.\",\n", @@ -1297,7 +936,7 @@ " {\n", " \"@type\": \"Sentiment\",\n", " \"marl:hasPolarity\": \"marl:Positive\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382983.5748618\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369539.7878842\"\n", " }\n", " ],\n", " \"nif:isString\": \"Senpy is awesome.\",\n", @@ -1310,7 +949,7 @@ " {\n", " \"@type\": \"Sentiment\",\n", " \"marl:hasPolarity\": \"marl:Neutral\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382983.5748618\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369539.7878842\"\n", " }\n", " ],\n", " \"nif:isString\": \"And services are composable.\",\n", @@ -1320,18 +959,17 @@ "}" ] }, - "execution_count": 18, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/split/sentiment140',\n", - " params={\"input\": \"Senpy is awesome. And services are composable.\", \n", - " \"delimiter\": \"sentence\",\n", - " \"language\": \"en\",\n", - " \"outformat\": \"json-ld\"})\n", - "pretty(res.text)" + "query(f'{endpoint}/split/sentiment140',\n", + " input=\"Senpy is awesome. And services are composable.\", \n", + " delimiter=\"sentence\",\n", + " language=\"en\",\n", + " outformat=\"json-ld\")" ] }, { @@ -1350,7 +988,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -1436,19 +1074,18 @@ "[[\"Senpy is awesome. And services are composable.\", \"marl:Positive\"], [\"Senpy is awesome.\", \"marl:Positive\"], [\"And services are composable.\", \"marl:Neutral\"]]" ] }, - "execution_count": 19, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/split/sentiment140',\n", - " params={\"input\": \"Senpy is awesome. And services are composable.\", \n", - " \"delimiter\": \"sentence\",\n", - " \"fields\": 'entries[].[[\"nif:isString\",\"marl:hasOpinion\"[].\"marl:hasPolarity\"][]][]',\n", - " \"language\": \"en\",\n", - " \"outformat\": \"json-ld\"})\n", - "pretty(res.text)" + "query(f'{endpoint}/split/sentiment140',\n", + " input=\"Senpy is awesome. And services are composable.\", \n", + " delimiter=\"sentence\",\n", + " fields='entries[].[[\"nif:isString\",\"marl:hasOpinion\"[].\"marl:hasPolarity\"][]][]',\n", + " language=\"en\",\n", + " outformat=\"json-ld\")" ] }, { @@ -1467,7 +1104,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 9, "metadata": { "scrolled": true }, @@ -2323,14 +1960,13 @@ "}" ] }, - "execution_count": 20, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/plugins')\n", - "pretty(res.text)" + "query(f'{endpoint}/plugins')" ] }, { @@ -2343,7 +1979,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -2852,14 +2488,14 @@ "}" ] }, - "execution_count": 21, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/plugins', params={\"plugin_type\": \"SentimentPlugin\"})\n", - "pretty(res.text)" + "query(f'{endpoint}/plugins',\n", + " plugin_type=\"SentimentPlugin\")" ] }, { @@ -2871,7 +2507,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -2957,14 +2593,14 @@ "[[\"endpoint:plugins/emotion-anew_0.5.1\", \"EmotionPlugin\"], [\"endpoint:plugins/emotion-depechemood_0.1\", \"EmotionPlugin\"], [\"endpoint:plugins/emotion-wnaffect_0.2\", \"EmotionPlugin\"], [\"endpoint:plugins/example-plugin_0.1\", \"Plugin\"], [\"endpoint:plugins/sentiment-basic_0.1.1\", \"SentimentPlugin\"], [\"endpoint:plugins/sentiment-meaningcloud_1.1\", \"SentimentPlugin\"], [\"endpoint:plugins/sentiment-vader_0.1.1\", \"SentimentPlugin\"], [\"endpoint:plugins/sentiment140_0.2\", \"SentimentPlugin\"], [\"endpoint:plugins/split_0.3\", \"Plugin\"]]" ] }, - "execution_count": 22, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/plugins', params={\"fields\": 'plugins[].[\"@id\",\"@type\"]'})\n", - "pretty(res.text)" + "query(f'{endpoint}/plugins',\n", + " fields='plugins[].[\"@id\",\"@type\"]')" ] }, { @@ -2974,34 +2610,6 @@ "Alternatively:" ] }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "EmotionPlugin -> endpoint:plugins/emotion-anew_0.5.1\n", - "EmotionPlugin -> endpoint:plugins/emotion-depechemood_0.1\n", - "EmotionPlugin -> endpoint:plugins/emotion-wnaffect_0.2\n", - "Plugin -> endpoint:plugins/example-plugin_0.1\n", - "SentimentPlugin -> endpoint:plugins/sentiment-basic_0.1.1\n", - "SentimentPlugin -> endpoint:plugins/sentiment-meaningcloud_1.1\n", - "SentimentPlugin -> endpoint:plugins/sentiment-vader_0.1.1\n", - "SentimentPlugin -> endpoint:plugins/sentiment140_0.2\n", - "Plugin -> endpoint:plugins/split_0.3\n" - ] - } - ], - "source": [ - "for pid, ptype in res.json():\n", - " print('{:20s} -> {}'.format(ptype, pid))" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -3020,7 +2628,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -3331,18 +2939,16 @@ "}" ] }, - "execution_count": 24, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "res = requests.get(f'{endpoint}/evaluate',\n", - " params={\"algo\": \"sentiment-vader\",\n", - " \"dataset\": \"vader,sts\",\n", - " 'outformat': 'json-ld'\n", - " })\n", - "pretty(res.text)" + "query(f'{endpoint}/evaluate',\n", + " algo=\"sentiment-vader\",\n", + " dataset=\"vader,sts\",\n", + " outformat='json-ld')" ] }, { @@ -3419,8 +3025,10 @@ }, { "cell_type": "code", - "execution_count": 25, - "metadata": {}, + "execution_count": 13, + "metadata": { + "scrolled": true + }, "outputs": [ { "data": { @@ -3498,7 +3106,7 @@ " "@type": "Results",\n", " "activities": [\n", " {\n", - " "@id": "prefix:Analysis_1554382985.03282",\n", + " "@id": "prefix:Analysis_1563369541.408701",\n", " "@type": "Analysis",\n", " "marl:maxPolarityValue": 1,\n", " "marl:minPolarityValue": 0,\n", @@ -3590,7 +3198,7 @@ " {\n", " "@type": "Sentiment",\n", " "marl:hasPolarity": "marl:Positive",\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382985.03282"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369541.408701"\n", " }\n", " ],\n", " "nif:isString": "Senpy is the best framework for semantic sentiment analysis, and very easy to use",\n", @@ -3607,7 +3215,7 @@ " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}activities\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382985.03282\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369541.408701\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Analysis\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:maxPolarityValue\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mi}{1}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:minPolarityValue\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mi}{0}\\PY{p}{,}\n", @@ -3699,7 +3307,7 @@ " \\PY{p}{\\PYZob{}}\n", " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382985.03282\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369541.408701\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is the best framework for semantic sentiment analysis, and very easy to use\\PYZdq{}}\\PY{p}{,}\n", @@ -3715,7 +3323,7 @@ " \"@type\": \"Results\",\n", " \"activities\": [\n", " {\n", - " \"@id\": \"prefix:Analysis_1554382985.03282\",\n", + " \"@id\": \"prefix:Analysis_1563369541.408701\",\n", " \"@type\": \"Analysis\",\n", " \"marl:maxPolarityValue\": 1,\n", " \"marl:minPolarityValue\": 0,\n", @@ -3807,7 +3415,7 @@ " {\n", " \"@type\": \"Sentiment\",\n", " \"marl:hasPolarity\": \"marl:Positive\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382985.03282\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369541.408701\"\n", " }\n", " ],\n", " \"nif:isString\": \"Senpy is the best framework for semantic sentiment analysis, and very easy to use\",\n", @@ -3817,18 +3425,15 @@ "}" ] }, - "execution_count": 25, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\n", - " \"input\": \"Senpy is the best framework for semantic sentiment analysis, and very easy to use\",\n", - " \"verbose\": True}).text\n", - "pretty(res)" + "query(f'{endpoint}/sentiment140',\n", + " input=\"Senpy is the best framework for semantic sentiment analysis, and very easy to use\",\n", + " verbose=True)" ] }, { @@ -3840,7 +3445,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -4553,17 +4158,14 @@ "}" ] }, - "execution_count": 26, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/',\n", - " params={\n", - " \"help\": True}).text\n", - "pretty(res)" + "query(f'{endpoint}/',\n", + " help=True)" ] }, { @@ -4575,7 +4177,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -4668,10 +4270,10 @@ " "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal": 4.22,\n", " "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance": 5.17,\n", " "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence": 5.2,\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382985.2021937"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369541.631805"\n", " }\n", " ],\n", - " "prov:wasGeneratedBy": "prefix:Analysis_1554382985.2021937"\n", + " "prov:wasGeneratedBy": "prefix:Analysis_1563369541.631805"\n", " }\n", " ]\n", " }\n", @@ -4700,10 +4302,10 @@ " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}arousal\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{4.22}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}dominance\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{5.17}\\PY{p}{,}\n", " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}valence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{5.2}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382985.2021937\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369541.631805\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554382985.2021937\\PYZdq{}}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563369541.631805\\PYZdq{}}\n", " \\PY{p}{\\PYZcb{}}\n", " \\PY{p}{]}\n", " \\PY{p}{\\PYZcb{}}\n", @@ -4731,10 +4333,10 @@ " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 4.22,\n", " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 5.17,\n", " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 5.2,\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382985.2021937\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369541.631805\"\n", " }\n", " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554382985.2021937\"\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563369541.631805\"\n", " }\n", " ]\n", " }\n", @@ -4742,18 +4344,15 @@ "}" ] }, - "execution_count": 27, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/',\n", - " params={\n", - " \"input\": \"This will tell senpy to only include the context in the headers\",\n", - " \"inheaders\": True})\n", - "pretty(res.text)" + "query(f'{endpoint}/',\n", + " input=\"This will tell senpy to only include the context in the headers\",\n", + " inheaders=True)" ] }, { @@ -4765,7 +4364,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -4777,15 +4376,12 @@ } ], "source": [ + "# We first repeat the query, to get the raw requests response using raw=True\n", + "res = query(f'{endpoint}/', input=\"This will tell senpy to only include the context in the headers\", inheaders=True, raw=True)\n", + "\n", + "# The URI of the context is in the headers:\n", "print(res.headers['Link'])" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/docs/Evaluation.ipynb b/docs/Evaluation.ipynb new file mode 100644 index 0000000..712e08f --- /dev/null +++ b/docs/Evaluation.ipynb @@ -0,0 +1,592 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Evaluating Services" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sentiment analysis plugins can also be evaluated on a series of pre-defined datasets.\n", + "This can be done in three ways: through the Web UI (playground), through the web API and programmatically.\n", + "\n", + "Regardless of the way you perform the evaluation, you will need to specify a plugin (service) that you want to evaluate, and a series of datasets on which it should be evaluated.\n", + "\n", + "to evaluate a plugin on a dataset, senpy use the plugin to predict the sentiment in each entry in the dataset.\n", + "These predictions are compared with the expected values to produce several metrics, such as: accuracy, precision and f1-score.\n", + "\n", + "**note**: the evaluation process might take long for plugins that use external services, such as `sentiment140`.\n", + "\n", + "**note**: plugins are assumed to be pre-trained and invariant. i.e., the prediction for an entry should " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Web UI (Playground)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The playground should contain a tab for Evaluation, where you can select any plugin that can be evaluated, and the set of datasets that you want to test the plugin on.\n", + "\n", + "For example, the image below shows the results of the `sentiment-vader` plugin on the `vader` and `sts` datasets:\n", + "\n", + "\n", + "![](eval_table.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Web API" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The api exposes an endpoint (`/evaluate`), which accents the plugin and the set of datasets on which it should be evaluated." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following code is not necessary, but it will display the results better:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is a simple call using the requests library:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2V2YWx1YXRlLz9hbGdvPXNlbnRpbWVudC12YWRlciZkYXRhc2V0PXZhZGVyJTJDc3RzJm91dGZvcm1hdD1qc29uLWxkIw%3D%3D",\n",
+       "  "@type": "AggregatedEvaluation",\n",
+       "  "senpy:evaluations": [\n",
+       "    {\n",
+       "      "@type": "Evaluation",\n",
+       "      "evaluates": "endpoint:plugins/sentiment-vader_0.1.1__vader",\n",
+       "      "evaluatesOn": "vader",\n",
+       "      "metrics": [\n",
+       "        {\n",
+       "          "@type": "Accuracy",\n",
+       "          "value": 0.6907142857142857\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "Precision_macro",\n",
+       "          "value": 0.34535714285714286\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "Recall_macro",\n",
+       "          "value": 0.5\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_macro",\n",
+       "          "value": 0.40853400929446554\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_weighted",\n",
+       "          "value": 0.5643605528396403\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_micro",\n",
+       "          "value": 0.6907142857142857\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_macro",\n",
+       "          "value": 0.40853400929446554\n",
+       "        }\n",
+       "      ]\n",
+       "    },\n",
+       "    {\n",
+       "      "@type": "Evaluation",\n",
+       "      "evaluates": "endpoint:plugins/sentiment-vader_0.1.1__sts",\n",
+       "      "evaluatesOn": "sts",\n",
+       "      "metrics": [\n",
+       "        {\n",
+       "          "@type": "Accuracy",\n",
+       "          "value": 0.3107177974434612\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "Precision_macro",\n",
+       "          "value": 0.1553588987217306\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "Recall_macro",\n",
+       "          "value": 0.5\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_macro",\n",
+       "          "value": 0.23705926481620407\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_weighted",\n",
+       "          "value": 0.14731706525451424\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_micro",\n",
+       "          "value": 0.3107177974434612\n",
+       "        },\n",
+       "        {\n",
+       "          "@type": "F1_macro",\n",
+       "          "value": 0.23705926481620407\n",
+       "        }\n",
+       "      ]\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2V2YWx1YXRlLz9hbGdvPXNlbnRpbWVudC12YWRlciZkYXRhc2V0PXZhZGVyJTJDc3RzJm91dGZvcm1hdD1qc29uLWxkIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}AggregatedEvaluation\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}senpy:evaluations\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Evaluation\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}evaluates\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}endpoint:plugins/sentiment\\PYZhy{}vader\\PYZus{}0.1.1\\PYZus{}\\PYZus{}vader\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}evaluatesOn\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}vader\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}metrics\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Accuracy\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.6907142857142857}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Precision\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.34535714285714286}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Recall\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.5}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.40853400929446554}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}weighted\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.5643605528396403}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}micro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.6907142857142857}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.40853400929446554}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Evaluation\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}evaluates\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}endpoint:plugins/sentiment\\PYZhy{}vader\\PYZus{}0.1.1\\PYZus{}\\PYZus{}sts\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}evaluatesOn\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}sts\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}metrics\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Accuracy\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.3107177974434612}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Precision\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.1553588987217306}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Recall\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.5}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.23705926481620407}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}weighted\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.14731706525451424}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}micro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.3107177974434612}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nt}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}F1\\PYZus{}macro\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}value\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.23705926481620407}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2V2YWx1YXRlLz9hbGdvPXNlbnRpbWVudC12YWRlciZkYXRhc2V0PXZhZGVyJTJDc3RzJm91dGZvcm1hdD1qc29uLWxkIw%3D%3D\",\n", + " \"@type\": \"AggregatedEvaluation\",\n", + " \"senpy:evaluations\": [\n", + " {\n", + " \"@type\": \"Evaluation\",\n", + " \"evaluates\": \"endpoint:plugins/sentiment-vader_0.1.1__vader\",\n", + " \"evaluatesOn\": \"vader\",\n", + " \"metrics\": [\n", + " {\n", + " \"@type\": \"Accuracy\",\n", + " \"value\": 0.6907142857142857\n", + " },\n", + " {\n", + " \"@type\": \"Precision_macro\",\n", + " \"value\": 0.34535714285714286\n", + " },\n", + " {\n", + " \"@type\": \"Recall_macro\",\n", + " \"value\": 0.5\n", + " },\n", + " {\n", + " \"@type\": \"F1_macro\",\n", + " \"value\": 0.40853400929446554\n", + " },\n", + " {\n", + " \"@type\": \"F1_weighted\",\n", + " \"value\": 0.5643605528396403\n", + " },\n", + " {\n", + " \"@type\": \"F1_micro\",\n", + " \"value\": 0.6907142857142857\n", + " },\n", + " {\n", + " \"@type\": \"F1_macro\",\n", + " \"value\": 0.40853400929446554\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"@type\": \"Evaluation\",\n", + " \"evaluates\": \"endpoint:plugins/sentiment-vader_0.1.1__sts\",\n", + " \"evaluatesOn\": \"sts\",\n", + " \"metrics\": [\n", + " {\n", + " \"@type\": \"Accuracy\",\n", + " \"value\": 0.3107177974434612\n", + " },\n", + " {\n", + " \"@type\": \"Precision_macro\",\n", + " \"value\": 0.1553588987217306\n", + " },\n", + " {\n", + " \"@type\": \"Recall_macro\",\n", + " \"value\": 0.5\n", + " },\n", + " {\n", + " \"@type\": \"F1_macro\",\n", + " \"value\": 0.23705926481620407\n", + " },\n", + " {\n", + " \"@type\": \"F1_weighted\",\n", + " \"value\": 0.14731706525451424\n", + " },\n", + " {\n", + " \"@type\": \"F1_micro\",\n", + " \"value\": 0.3107177974434612\n", + " },\n", + " {\n", + " \"@type\": \"F1_macro\",\n", + " \"value\": 0.23705926481620407\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import requests\n", + "from IPython.display import Code\n", + "\n", + "endpoint = 'http://senpy.gsi.upm.es/api'\n", + "res = requests.get(f'{endpoint}/evaluate',\n", + " params={\"algo\": \"sentiment-vader\",\n", + " \"dataset\": \"vader,sts\",\n", + " 'outformat': 'json-ld'\n", + " })\n", + "Code(res.text, language='json')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Programmatically (expert)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A third option is to evaluate plugins manually without launching the server.\n", + "\n", + "This option is particularly interesting for advanced users that want faster iterations and evaluation results, and for automation.\n", + "\n", + "We would first need an instance of a plugin.\n", + "In this example we will use the Sentiment140 plugin that is included in every senpy installation:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "from senpy.plugins.sentiment import sentiment140_plugin" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "s140 = sentiment140_plugin.Sentiment140()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then, we need to know what datasets are available.\n", + "We can list all datasets and basic stats (e.g., number of instances and labels used) like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "vader {'instances': 4200, 'labels': [1, -1]}\n", + "sts {'instances': 4200, 'labels': [1, -1]}\n", + "imdb_unsup {'instances': 50000, 'labels': [1, -1]}\n", + "imdb {'instances': 50000, 'labels': [1, -1]}\n", + "sst {'instances': 11855, 'labels': [1, -1]}\n", + "multidomain {'instances': 38548, 'labels': [1, -1]}\n", + "sentiment140 {'instances': 1600000, 'labels': [1, -1]}\n", + "semeval07 {'instances': 'None', 'labels': [1, -1]}\n", + "semeval14 {'instances': 7838, 'labels': [1, -1]}\n", + "pl04 {'instances': 4000, 'labels': [1, -1]}\n", + "pl05 {'instances': 10662, 'labels': [1, -1]}\n", + "semeval13 {'instances': 6259, 'labels': [1, -1]}\n" + ] + } + ], + "source": [ + "from senpy.gsitk_compat import datasets\n", + "for k, d in datasets.items():\n", + " print(k, d['stats'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we will evaluate our plugin in one of the smallest datasets, `sts`:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{\n", + " \"@type\": \"Evaluation\",\n", + " \"evaluates\": \"endpoint:plugins/sentiment140_0.2\",\n", + " \"evaluatesOn\": \"sts\",\n", + " \"metrics\": [\n", + " {\n", + " \"@type\": \"Accuracy\",\n", + " \"value\": 0.872173058013766\n", + " },\n", + " {\n", + " \"@type\": \"Precision_macro\",\n", + " \"value\": 0.9035254323131467\n", + " },\n", + " {\n", + " \"@type\": \"Recall_macro\",\n", + " \"value\": 0.8021249029415483\n", + " },\n", + " {\n", + " \"@type\": \"F1_macro\",\n", + " \"value\": 0.8320673712021136\n", + " },\n", + " {\n", + " \"@type\": \"F1_weighted\",\n", + " \"value\": 0.8631351567604358\n", + " },\n", + " {\n", + " \"@type\": \"F1_micro\",\n", + " \"value\": 0.872173058013766\n", + " },\n", + " {\n", + " \"@type\": \"F1_macro\",\n", + " \"value\": 0.8320673712021136\n", + " }\n", + " ]\n", + " }]" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s140.evaluate(['sts', ])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "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.7.3" + }, + "toc": { + "colors": { + "hover_highlight": "#DAA520", + "running_highlight": "#FF0000", + "selected_highlight": "#FFD700" + }, + "moveMenuLeft": true, + "nav_menu": { + "height": "68px", + "width": "252px" + }, + "navigate_menu": true, + "number_sections": true, + "sideBar": true, + "threshold": 4, + "toc_cell": false, + "toc_section_display": "block", + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/docs/Quickstart-10minutes.ipynb b/docs/Quickstart-10minutes.ipynb deleted file mode 100644 index ad85a42..0000000 --- a/docs/Quickstart-10minutes.ipynb +++ /dev/null @@ -1,1513 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Senpy in 10 minutes\n", - "\n", - "This tutorial assumes you have completed the [1 minute tutorial](Quickstart-1minute.ipynb) and you know how to access the API.\n", - "\n", - "It covers:\n", - "\n", - "* Annotating entiment with a sentiment analysis service\n", - "* Annotating emotions\n", - "* Interoperability of services\n", - "* Using other semantic formats (RDF)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Requirements" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once again, we will use the demo server at http://senpy.gsi.upm.es.\n", - "This time, we will use the requests library.\n", - "\n", - "We will use a variable for our endpoint, so you can try this examples on a different senpy instance:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "endpoint = 'http://senpy.gsi.upm.es/api'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will also add a function to get syntax highlighting for the JSON-LD/Turtle results:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " from IPython.display import Code\n", - " def pretty(txt, language='json-ld'):\n", - " return Code(txt, language=language)\n", - "except ImportError:\n", - " def pretty(txt, **kwargs):\n", - " print(txt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Sentiment Analysis of Text" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To start, let us analyse the sentiment in the following sentence: *senpy is awesome*.\n", - "\n", - "For now, we will use the [sentiment140](http://www.sentiment140.com/) service, through the sentiment140 plugin.\n", - "We will later cover how to use a different service.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [\n",
-       "        {\n",
-       "          "@type": "Sentiment",\n",
-       "          "marl:hasPolarity": "marl:Positive",\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554396807.629645"\n",
-       "        }\n",
-       "      ],\n",
-       "      "nif:isString": "Senpy is awesome",\n",
-       "      "onyx:hasEmotionSet": []\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554396807.629645\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [\n", - " {\n", - " \"@type\": \"Sentiment\",\n", - " \"marl:hasPolarity\": \"marl:Positive\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554396807.629645\"\n", - " }\n", - " ],\n", - " \"nif:isString\": \"Senpy is awesome\",\n", - " \"onyx:hasEmotionSet\": []\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\"input\": \"Senpy is awesome\",})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Senpy services always return an object of type `senpy:Results`, with a list of entries.\n", - "You can think of an entry as a self-contained textual context (`nif:Context` and `senpy:Entry`).\n", - "Entries can be as short as a sentence, or as long as a news article.\n", - "\n", - "Each entry has a `nif:isString` property that contains the original text of the entry, and several other properties that are provided by the plugins.\n", - "\n", - "For instance, sentiment annotations are provided through `marl:hasOpinion`.\n", - "\n", - "The annotations are semantic.\n", - "We can ask Senpy for the expanded JSON-LD output to reveal the full URIs of each property and entity:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lJmV4cGFuZGVkPVRydWUj",\n",
-       "  "@type": [\n",
-       "    "http://www.gsi.upm.es/onto/senpy/ns#Results"\n",
-       "  ],\n",
-       "  "http://www.w3.org/ns/prov#used": [\n",
-       "    {\n",
-       "      "@id": "http://senpy.invalid/",\n",
-       "      "@type": [\n",
-       "        "http://www.gsi.upm.es/onto/senpy/ns#Entry"\n",
-       "      ],\n",
-       "      "http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#isString": [\n",
-       "        {\n",
-       "          "@value": "Senpy is awesome"\n",
-       "        }\n",
-       "      ],\n",
-       "      "http://www.gsi.dit.upm.es/ontologies/marl/ns#hasOpinion": [\n",
-       "        {\n",
-       "          "@type": [\n",
-       "            "http://www.gsi.upm.es/onto/senpy/ns#Sentiment"\n",
-       "          ],\n",
-       "          "http://www.gsi.dit.upm.es/ontologies/marl/ns#hasPolarity": [\n",
-       "            {\n",
-       "              "@value": "marl:Positive"\n",
-       "            }\n",
-       "          ],\n",
-       "          "http://www.w3.org/ns/prov#wasGeneratedBy": [\n",
-       "            {\n",
-       "              "@id": "http://senpy.invalid/Analysis_1554389672.269198"\n",
-       "            }\n",
-       "          ]\n",
-       "        }\n",
-       "      ],\n",
-       "      "http://www.gsi.dit.upm.es/ontologies/onyx/ns#hasEmotionSet": []\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lJmV4cGFuZGVkPVRydWUj\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{l+s+s2}{\\PYZdq{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}Results\\PYZdq{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.w3.org/ns/prov\\PYZsh{}used\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.invalid/\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{l+s+s2}{\\PYZdq{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}Entry\\PYZdq{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://persistence.uni\\PYZhy{}leipzig.org/nlp2rdf/ontologies/nif\\PYZhy{}core\\PYZsh{}isString\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@value\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/marl/ns\\PYZsh{}hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{l+s+s2}{\\PYZdq{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}Sentiment\\PYZdq{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/marl/ns\\PYZsh{}hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@value\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.w3.org/ns/prov\\PYZsh{}wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.invalid/Analysis\\PYZus{}1554389672.269198\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/ns\\PYZsh{}hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lJmV4cGFuZGVkPVRydWUj\",\n", - " \"@type\": [\n", - " \"http://www.gsi.upm.es/onto/senpy/ns#Results\"\n", - " ],\n", - " \"http://www.w3.org/ns/prov#used\": [\n", - " {\n", - " \"@id\": \"http://senpy.invalid/\",\n", - " \"@type\": [\n", - " \"http://www.gsi.upm.es/onto/senpy/ns#Entry\"\n", - " ],\n", - " \"http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#isString\": [\n", - " {\n", - " \"@value\": \"Senpy is awesome\"\n", - " }\n", - " ],\n", - " \"http://www.gsi.dit.upm.es/ontologies/marl/ns#hasOpinion\": [\n", - " {\n", - " \"@type\": [\n", - " \"http://www.gsi.upm.es/onto/senpy/ns#Sentiment\"\n", - " ],\n", - " \"http://www.gsi.dit.upm.es/ontologies/marl/ns#hasPolarity\": [\n", - " {\n", - " \"@value\": \"marl:Positive\"\n", - " }\n", - " ],\n", - " \"http://www.w3.org/ns/prov#wasGeneratedBy\": [\n", - " {\n", - " \"@id\": \"http://senpy.invalid/Analysis_1554389672.269198\"\n", - " }\n", - " ]\n", - " }\n", - " ],\n", - " \"http://www.gsi.dit.upm.es/ontologies/onyx/ns#hasEmotionSet\": []\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\"input\": \"Senpy is awesome\",\n", - " \"expanded\": True})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Service interoperability" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All senpy plugins use the same API, which makes moving from one service to another a breeze.\n", - "\n", - "Let us go back to our simple example, which uses the `sentiment140` service." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [\n",
-       "        {\n",
-       "          "@type": "Sentiment",\n",
-       "          "marl:hasPolarity": "marl:Positive",\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554389675.2303865"\n",
-       "        }\n",
-       "      ],\n",
-       "      "nif:isString": "Senpy is awesome",\n",
-       "      "onyx:hasEmotionSet": []\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554389675.2303865\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [\n", - " {\n", - " \"@type\": \"Sentiment\",\n", - " \"marl:hasPolarity\": \"marl:Positive\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554389675.2303865\"\n", - " }\n", - " ],\n", - " \"nif:isString\": \"Senpy is awesome\",\n", - " \"onyx:hasEmotionSet\": []\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\"input\": \"Senpy is awesome\"})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we wanted to use a different service (e.g. the `sentiment-basic` plugin), we can do it just by changing the URL of the service:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudC1iYXNpYz9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [\n",
-       "        {\n",
-       "          "@type": "Sentiment",\n",
-       "          "marl:hasPolarity": "marl:Neutral",\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554389675.9911203"\n",
-       "        }\n",
-       "      ],\n",
-       "      "nif:isString": "Senpy is awesome",\n",
-       "      "onyx:hasEmotionSet": []\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudC1iYXNpYz9pbnB1dD1TZW5weStpcythd2Vzb21lIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Neutral\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554389675.9911203\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudC1iYXNpYz9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [\n", - " {\n", - " \"@type\": \"Sentiment\",\n", - " \"marl:hasPolarity\": \"marl:Neutral\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554389675.9911203\"\n", - " }\n", - " ],\n", - " \"nif:isString\": \"Senpy is awesome\",\n", - " \"onyx:hasEmotionSet\": []\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import requests\n", - "res = requests.get(f'{endpoint}/sentiment-basic',\n", - " params={\"input\": \"Senpy is awesome\"})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, the structure and annotation schema of the response is the same.\n", - "This makes it very easy to compare and migrate to different services.\n", - "\n", - "Service interoperability is not only useful for users.\n", - "It is also key for other features such as [automated evaluation](#Evaluation).\n", - "This is a compelling reason to adapt existing services to use the Senpy API.\n", - "In fact, the `sentiment140` senpy service is proxy to the public [Sentiment 140 service](http://www.sentiment140.com/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Emotion analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Senpy uses the `onyx` vocabulary to represent emotions, which incorporates the notion of `EmotionSet`'s, an emotion that is composed of several emotions.\n", - "In a nutshell, an `Entry` is linked to one or more `EmotionSet`, which in turn is made up of one or more `Emotion`.\n", - "\n", - "Let's illustrate it with an example, using the `emotion-depechemood` plugin." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrdGhhdCtzZXJ2aWNlIw%3D%3D",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [],\n",
-       "      "nif:isString": "Senpy is a wonderful that service",\n",
-       "      "onyx:hasEmotionSet": [\n",
-       "        {\n",
-       "          "@type": "EmotionSet",\n",
-       "          "onyx:hasEmotion": [\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:negative-fear",\n",
-       "              "onyx:hasEmotionIntensity": 0.06258366271018097\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:amusement",\n",
-       "              "onyx:hasEmotionIntensity": 0.15784834034155437\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:anger",\n",
-       "              "onyx:hasEmotionIntensity": 0.08728815135373413\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:annoyance",\n",
-       "              "onyx:hasEmotionIntensity": 0.12184635680460143\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:indifference",\n",
-       "              "onyx:hasEmotionIntensity": 0.1374081151031531\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:joy",\n",
-       "              "onyx:hasEmotionIntensity": 0.12267040802346799\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:awe",\n",
-       "              "onyx:hasEmotionIntensity": 0.21085262130713067\n",
-       "            },\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:sadness",\n",
-       "              "onyx:hasEmotionIntensity": 0.09950234435617733\n",
-       "            }\n",
-       "          ],\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554389679.0535374"\n",
-       "        }\n",
-       "      ]\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q\\PYZus{}aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrdGhhdCtzZXJ2aWNlIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful that service\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:negative\\PYZhy{}fear\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.06258366271018097}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:amusement\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.15784834034155437}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:anger\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.08728815135373413}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:annoyance\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.12184635680460143}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:indifference\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.1374081151031531}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:joy\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.12267040802346799}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:awe\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.21085262130713067}\n", - " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:sadness\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.09950234435617733}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554389679.0535374\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrdGhhdCtzZXJ2aWNlIw%3D%3D\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [],\n", - " \"nif:isString\": \"Senpy is a wonderful that service\",\n", - " \"onyx:hasEmotionSet\": [\n", - " {\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:negative-fear\",\n", - " \"onyx:hasEmotionIntensity\": 0.06258366271018097\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:amusement\",\n", - " \"onyx:hasEmotionIntensity\": 0.15784834034155437\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:anger\",\n", - " \"onyx:hasEmotionIntensity\": 0.08728815135373413\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:annoyance\",\n", - " \"onyx:hasEmotionIntensity\": 0.12184635680460143\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:indifference\",\n", - " \"onyx:hasEmotionIntensity\": 0.1374081151031531\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:joy\",\n", - " \"onyx:hasEmotionIntensity\": 0.12267040802346799\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:awe\",\n", - " \"onyx:hasEmotionIntensity\": 0.21085262130713067\n", - " },\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:sadness\",\n", - " \"onyx:hasEmotionIntensity\": 0.09950234435617733\n", - " }\n", - " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554389679.0535374\"\n", - " }\n", - " ]\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = requests.get(f'{endpoint}/emotion-depechemood',\n", - " params={\"input\": \"Senpy is a wonderful that service\"})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you have probably noticed, there are several emotions in this result, each with a different intensity.\n", - "\n", - "We can also tell senpy to only return the emotion with the maximum intensity using the `maxemotion` parameter:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrc2VydmljZSZtYXhlbW90aW9uPVRydWUj",\n",
-       "  "@type": "Results",\n",
-       "  "entries": [\n",
-       "    {\n",
-       "      "@id": "prefix:",\n",
-       "      "@type": "Entry",\n",
-       "      "marl:hasOpinion": [],\n",
-       "      "nif:isString": "Senpy is a wonderful service",\n",
-       "      "onyx:hasEmotionSet": [\n",
-       "        {\n",
-       "          "@type": "EmotionSet",\n",
-       "          "onyx:hasEmotion": [\n",
-       "            {\n",
-       "              "@type": "Emotion",\n",
-       "              "onyx:hasEmotionCategory": "wna:awe",\n",
-       "              "onyx:hasEmotionIntensity": 0.21085262130713067\n",
-       "            }\n",
-       "          ],\n",
-       "          "prov:wasGeneratedBy": "prefix:Analysis_1554389681.1941268"\n",
-       "        }\n",
-       "      ]\n",
-       "    }\n",
-       "  ]\n",
-       "}\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q\\PYZus{}aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrc2VydmljZSZtYXhlbW90aW9uPVRydWUj\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", - " \\PY{p}{\\PYZob{}}\n", - " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:awe\\PYZdq{}}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.21085262130713067}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\\PY{p}{,}\n", - " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1554389681.1941268\\PYZdq{}}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - " \\PY{p}{\\PYZcb{}}\n", - " \\PY{p}{]}\n", - "\\PY{p}{\\PYZcb{}}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrc2VydmljZSZtYXhlbW90aW9uPVRydWUj\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [],\n", - " \"nif:isString\": \"Senpy is a wonderful service\",\n", - " \"onyx:hasEmotionSet\": [\n", - " {\n", - " \"@type\": \"EmotionSet\",\n", - " \"onyx:hasEmotion\": [\n", - " {\n", - " \"@type\": \"Emotion\",\n", - " \"onyx:hasEmotionCategory\": \"wna:awe\",\n", - " \"onyx:hasEmotionIntensity\": 0.21085262130713067\n", - " }\n", - " ],\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554389681.1941268\"\n", - " }\n", - " ]\n", - " }\n", - " ]\n", - "}" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = requests.get(f'{endpoint}/emotion-depechemood',\n", - " params={\"input\": \"Senpy is a wonderful service\",\n", - " \"maxemotion\": True})\n", - "pretty(res.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Other output formats" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Senpy supports several semantic formats, like turtle and xml-RDF.\n", - "You can select the format of the output with the `outformat` parameter:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
@prefix : <http://www.gsi.upm.es/onto/senpy/ns#> .\n",
-       "@prefix dc: <http://dublincore.org/2012/06/14/dcelements#> .\n",
-       "@prefix emoml: <http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns#> .\n",
-       "@prefix endpoint: <http://senpy.gsi.upm.es/api/> .\n",
-       "@prefix fam: <http://vocab.fusepool.info/fam#> .\n",
-       "@prefix marl: <http://www.gsi.dit.upm.es/ontologies/marl/ns#> .\n",
-       "@prefix nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> .\n",
-       "@prefix onyx: <http://www.gsi.dit.upm.es/ontologies/onyx/ns#> .\n",
-       "@prefix prefix: <http://senpy.invalid/> .\n",
-       "@prefix prov: <http://www.w3.org/ns/prov#> .\n",
-       "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
-       "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n",
-       "@prefix senpy: <http://www.gsi.upm.es/onto/senpy/ns#> .\n",
-       "@prefix wna: <http://www.gsi.dit.upm.es/ontologies/wnaffect/ns#> .\n",
-       "@prefix xml: <http://www.w3.org/XML/1998/namespace> .\n",
-       "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n",
-       "\n",
-       "prefix: a senpy:Entry ;\n",
-       "    nif:isString "Senpy is the best framework for semantic sentiment analysis, and very easy to use" ;\n",
-       "    marl:hasOpinion [ a senpy:Sentiment ;\n",
-       "            marl:hasPolarity "marl:Positive" ;\n",
-       "            prov:wasGeneratedBy prefix:Analysis_1554389682.350598 ] .\n",
-       "\n",
-       "[] a senpy:Results ;\n",
-       "    prov:used prefix: .\n",
-       "
\n" - ], - "text/latex": [ - "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{dc:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://dublincore.org/2012/06/14/dcelements\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{emoml:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{endpoint:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://senpy.gsi.upm.es/api/\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{fam:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://vocab.fusepool.info/fam\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{marl:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/marl/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{nif:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://persistence.uni\\PYZhy{}leipzig.org/nlp2rdf/ontologies/nif\\PYZhy{}core\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{onyx:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/onyx/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{prefix:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://senpy.invalid/\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{prov:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/ns/prov\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{rdf:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/1999/02/22\\PYZhy{}rdf\\PYZhy{}syntax\\PYZhy{}ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{rdfs:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/2000/01/rdf\\PYZhy{}schema\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{wna:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/wnaffect/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{xml:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/XML/1998/namespace\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{xsd:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/2001/XMLSchema\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", - "\n", - "\\PY{err}{p}\\PY{err}{r}\\PY{err}{e}\\PY{err}{f}\\PY{err}{i}\\PY{err}{x}\\PY{p}{:}\\PY{+w}{ }\\PY{k+kt}{a}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{n+nt}{Entry}\\PY{+w}{ }\\PY{p}{;}\n", - "\\PY{+w}{ }\\PY{n+nn}{nif:}\\PY{n+nt}{isString}\\PY{+w}{ }\\PY{l+s}{\\PYZdq{}}\\PY{l+s}{Senpy is the best framework for semantic sentiment analysis, and very easy to use}\\PY{l+s}{\\PYZdq{}}\\PY{+w}{ }\\PY{p}{;}\n", - "\\PY{+w}{ }\\PY{n+nn}{marl:}\\PY{n+nt}{hasOpinion}\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{k+kt}{a}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{n+nt}{Sentiment}\\PY{+w}{ }\\PY{p}{;}\n", - "\\PY{+w}{ }\\PY{n+nn}{marl:}\\PY{n+nt}{hasPolarity}\\PY{+w}{ }\\PY{l+s}{\\PYZdq{}}\\PY{l+s}{marl:Positive}\\PY{l+s}{\\PYZdq{}}\\PY{+w}{ }\\PY{p}{;}\n", - "\\PY{+w}{ }\\PY{n+nn}{prov:}\\PY{n+nt}{wasGeneratedBy}\\PY{+w}{ }\\PY{n+nn}{prefix:}\\PY{n+nt}{Analysis\\PYZus{}1554389682}\\PY{l+m+mf}{.350598}\\PY{+w}{ }\\PY{p}{]}\\PY{+w}{ }\\PY{p}{.}\n", - "\n", - "\\PY{p}{[}\\PY{p}{]}\\PY{+w}{ }\\PY{k+kt}{a}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{n+nt}{Results}\\PY{+w}{ }\\PY{p}{;}\n", - "\\PY{+w}{ }\\PY{n+nn}{prov:}\\PY{n+nt}{used}\\PY{+w}{ }\\PY{err}{p}\\PY{err}{r}\\PY{err}{e}\\PY{err}{f}\\PY{err}{i}\\PY{err}{x}\\PY{p}{:}\\PY{+w}{ }\\PY{p}{.}\n", - "\\end{Verbatim}\n" - ], - "text/plain": [ - "@prefix : .\n", - "@prefix dc: .\n", - "@prefix emoml: .\n", - "@prefix endpoint: .\n", - "@prefix fam: .\n", - "@prefix marl: .\n", - "@prefix nif: .\n", - "@prefix onyx: .\n", - "@prefix prefix: .\n", - "@prefix prov: .\n", - "@prefix rdf: .\n", - "@prefix rdfs: .\n", - "@prefix senpy: .\n", - "@prefix wna: .\n", - "@prefix xml: .\n", - "@prefix xsd: .\n", - "\n", - "prefix: a senpy:Entry ;\n", - " nif:isString \"Senpy is the best framework for semantic sentiment analysis, and very easy to use\" ;\n", - " marl:hasOpinion [ a senpy:Sentiment ;\n", - " marl:hasPolarity \"marl:Positive\" ;\n", - " prov:wasGeneratedBy prefix:Analysis_1554389682.350598 ] .\n", - "\n", - "[] a senpy:Results ;\n", - " prov:used prefix: .\n" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = requests.get(f'{endpoint}/sentiment140',\n", - " params={\"input\": \"Senpy is the best framework for semantic sentiment analysis, and very easy to use\",\n", - " \"outformat\": \"turtle\"})\n", - "pretty(res.text, language='turtle')" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "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.7.3" - }, - "toc": { - "colors": { - "hover_highlight": "#DAA520", - "running_highlight": "#FF0000", - "selected_highlight": "#FFD700" - }, - "moveMenuLeft": true, - "nav_menu": { - "height": "68px", - "width": "252px" - }, - "navigate_menu": true, - "number_sections": true, - "sideBar": true, - "threshold": 4, - "toc_cell": false, - "toc_section_display": "block", - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/Quickstart-1minute.ipynb b/docs/Quickstart-1minute.ipynb deleted file mode 100644 index a4e4edb..0000000 --- a/docs/Quickstart-1minute.ipynb +++ /dev/null @@ -1,152 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Senpy in 1 minute\n", - "\n", - "This mini-tutorial only shows how to annotate with a service.\n", - "We will use the [demo server](http://senpy.gsi.upm.es), which runs some open source plugins for sentiment and emotion analysis." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Annotating with senpy is as simple as issuing an HTTP request to the API using your favourite tool.\n", - "This is just an example using curl:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\r\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD8j\",\r\n", - " \"@type\": \"Results\",\r\n", - " \"entries\": [\r\n", - " {\r\n", - " \"@id\": \"prefix:\",\r\n", - " \"@type\": \"Entry\",\r\n", - " \"marl:hasOpinion\": [\r\n", - " {\r\n", - " \"@type\": \"Sentiment\",\r\n", - " \"marl:hasPolarity\": \"marl:Positive\",\r\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554389334.6431913\"\r\n", - " }\r\n", - " ],\r\n", - " \"nif:isString\": \"Senpy is awesome\",\r\n", - " \"onyx:hasEmotionSet\": []\r\n", - " }\r\n", - " ]\r\n", - "}" - ] - } - ], - "source": [ - "!curl \"http://senpy.gsi.upm.es/api/sentiment140\" --data-urlencode \"input=Senpy is awesome\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Congratulations**, you've used your first senpy service!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is the equivalent using the `requests` library:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D\",\n", - " \"@type\": \"Results\",\n", - " \"entries\": [\n", - " {\n", - " \"@id\": \"prefix:\",\n", - " \"@type\": \"Entry\",\n", - " \"marl:hasOpinion\": [\n", - " {\n", - " \"@type\": \"Sentiment\",\n", - " \"marl:hasPolarity\": \"marl:Positive\",\n", - " \"prov:wasGeneratedBy\": \"prefix:Analysis_1554389335.9803226\"\n", - " }\n", - " ],\n", - " \"nif:isString\": \"Senpy is awesome\",\n", - " \"onyx:hasEmotionSet\": []\n", - " }\n", - " ]\n", - "}\n" - ] - } - ], - "source": [ - "import requests\n", - "res = requests.get('http://senpy.gsi.upm.es/api/sentiment140',\n", - " params={\"input\": \"Senpy is awesome\",})\n", - "print(res.text)" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "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.7.3" - }, - "toc": { - "colors": { - "hover_highlight": "#DAA520", - "running_highlight": "#FF0000", - "selected_highlight": "#FFD700" - }, - "moveMenuLeft": true, - "nav_menu": { - "height": "68px", - "width": "252px" - }, - "navigate_menu": true, - "number_sections": true, - "sideBar": true, - "threshold": 4, - "toc_cell": false, - "toc_section_display": "block", - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/Quickstart.ipynb b/docs/Quickstart.ipynb index fbd89bb..1680aaf 100644 --- a/docs/Quickstart.ipynb +++ b/docs/Quickstart.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Quickstart\n", + "# Tutorial\n", "\n", "This short tutorial will teach you how to consume senpy services for several tasks, and how to take advantage of the features of the framework.\n", "\n", @@ -13,32 +13,1664 @@ "* Annotating text with sentiment and emotion using interoperable services\n", "* Switching to different services (service interoperability)\n", "* Getting results in different formats (Turtle, XML, text...)\n", - "* Asking for specific emotion models (automatic model conversion)\n", - "* Listing available services in an endpoint\n", - "* Calling multiple services in the same request (Pipelines)\n", - "\n", - "These topics are split into two separate tutorials.\n", - "\n", - "Reading all the sections is not necessary, although it is encouraged in order to get a glimpse of all the features." + "* Asking for specific emotion models (automatic model conversion)" ] }, { "cell_type": "markdown", - "metadata": { - "nbsphinx-toctree": { - "maxdepth": 2 - } - }, + "metadata": {}, "source": [ - "* [Senpy in 1 minute](./Quickstart-1minute.ipynb) shows how to query the API.\n", - "* [Senpy in 10 minutes](./Quickstart-10minutes.ipynb) introduces basic sentiment and emotion analysis.\n", - "* [Senpy in 30 minutes](./Quickstart-30minutes.ipynb) builds on the previous and adds more advanced functionalities, such as emotion conversion, field selection and pipelines." + "## Requirements" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use the demo server at http://senpy.gsi.upm.es abd the requests library.\n", + "\n", + "We will use a variable for our endpoint so you can try these examples on other instances:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "endpoint = 'http://senpy.gsi.upm.es/api'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will also add a little helper function (`query`) to simplify our queries and pretty-print the results with syntax highlighting:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "from IPython.display import Code\n", + " \n", + "def query(endpoint, **kwargs):\n", + " '''Query a given Senpy endpoint with specific parameters, and prettify the output'''\n", + " res = requests.get(endpoint,\n", + " params=kwargs)\n", + " if res.status_code != 200:\n", + " raise Exception(res)\n", + " return Code(res.text, language=kwargs.get('outformat', 'json-ld'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sentiment Analysis of Text" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To start, let us analyse the sentiment in the following sentence: *senpy is awesome*.\n", + "\n", + "For now, we will use the [sentiment140](http://www.sentiment140.com/) service, through the sentiment140 plugin.\n", + "We will later cover how to use a different service.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [\n",
+       "        {\n",
+       "          "@type": "Sentiment",\n",
+       "          "marl:hasPolarity": "marl:Positive",\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372853.439696"\n",
+       "        }\n",
+       "      ],\n",
+       "      "nif:isString": "Senpy is awesome",\n",
+       "      "onyx:hasEmotionSet": []\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Positive\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372853.439696\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [\n", + " {\n", + " \"@type\": \"Sentiment\",\n", + " \"marl:hasPolarity\": \"marl:Positive\",\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372853.439696\"\n", + " }\n", + " ],\n", + " \"nif:isString\": \"Senpy is awesome\",\n", + " \"onyx:hasEmotionSet\": []\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/sentiment140', input=\"Senpy is awesome\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Senpy services always return an object of type `senpy:Results`, with a list of entries.\n", + "You can think of an entry as a self-contained textual context (`nif:Context` and `senpy:Entry`).\n", + "Entries can be as short as a sentence, or as long as a news article.\n", + "\n", + "Each entry has a `nif:isString` property that contains the original text of the entry, and several other properties that are provided by the plugins.\n", + "\n", + "For instance, sentiment annotations are provided through `marl:hasOpinion`.\n", + "\n", + "The annotations are semantic.\n", + "This is clear if we request a different semantic format, such as `turtle`.\n", + "The output format is controlled with the `outformat` parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
@prefix : <http://www.gsi.upm.es/onto/senpy/ns#> .\n",
+       "@prefix dc: <http://dublincore.org/2012/06/14/dcelements#> .\n",
+       "@prefix emoml: <http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns#> .\n",
+       "@prefix endpoint: <http://senpy.gsi.upm.es/api/> .\n",
+       "@prefix fam: <http://vocab.fusepool.info/fam#> .\n",
+       "@prefix marl: <http://www.gsi.dit.upm.es/ontologies/marl/ns#> .\n",
+       "@prefix nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> .\n",
+       "@prefix onyx: <http://www.gsi.dit.upm.es/ontologies/onyx/ns#> .\n",
+       "@prefix prefix: <http://senpy.invalid/> .\n",
+       "@prefix prov: <http://www.w3.org/ns/prov#> .\n",
+       "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
+       "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n",
+       "@prefix senpy: <http://www.gsi.upm.es/onto/senpy/ns#> .\n",
+       "@prefix wna: <http://www.gsi.dit.upm.es/ontologies/wnaffect/ns#> .\n",
+       "@prefix xml: <http://www.w3.org/XML/1998/namespace> .\n",
+       "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n",
+       "\n",
+       "prefix: a senpy:Entry ;\n",
+       "    nif:isString "Senpy is awesome" ;\n",
+       "    marl:hasOpinion [ a senpy:Sentiment ;\n",
+       "            marl:hasPolarity "marl:Positive" ;\n",
+       "            prov:wasGeneratedBy prefix:Analysis_1563372853.6874764 ] .\n",
+       "\n",
+       "[] a senpy:Results ;\n",
+       "    prov:used prefix: .\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{dc:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://dublincore.org/2012/06/14/dcelements\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{emoml:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{endpoint:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://senpy.gsi.upm.es/api/\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{fam:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://vocab.fusepool.info/fam\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{marl:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/marl/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{nif:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://persistence.uni\\PYZhy{}leipzig.org/nlp2rdf/ontologies/nif\\PYZhy{}core\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{onyx:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/onyx/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{prefix:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://senpy.invalid/\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{prov:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/ns/prov\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{rdf:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/1999/02/22\\PYZhy{}rdf\\PYZhy{}syntax\\PYZhy{}ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{rdfs:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/2000/01/rdf\\PYZhy{}schema\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.upm.es/onto/senpy/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{wna:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.gsi.dit.upm.es/ontologies/wnaffect/ns\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{xml:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/XML/1998/namespace\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\\PY{k}{@prefix}\\PY{+w}{ }\\PY{n+nn}{xsd:}\\PY{+w}{ }\\PY{n+nv}{\\PYZlt{}http://www.w3.org/2001/XMLSchema\\PYZsh{}\\PYZgt{}}\\PY{+w}{ }\\PY{p}{.}\n", + "\n", + "\\PY{err}{p}\\PY{err}{r}\\PY{err}{e}\\PY{err}{f}\\PY{err}{i}\\PY{err}{x}\\PY{p}{:}\\PY{+w}{ }\\PY{k+kt}{a}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{n+nt}{Entry}\\PY{+w}{ }\\PY{p}{;}\n", + "\\PY{+w}{ }\\PY{n+nn}{nif:}\\PY{n+nt}{isString}\\PY{+w}{ }\\PY{l+s}{\\PYZdq{}}\\PY{l+s}{Senpy is awesome}\\PY{l+s}{\\PYZdq{}}\\PY{+w}{ }\\PY{p}{;}\n", + "\\PY{+w}{ }\\PY{n+nn}{marl:}\\PY{n+nt}{hasOpinion}\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{k+kt}{a}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{n+nt}{Sentiment}\\PY{+w}{ }\\PY{p}{;}\n", + "\\PY{+w}{ }\\PY{n+nn}{marl:}\\PY{n+nt}{hasPolarity}\\PY{+w}{ }\\PY{l+s}{\\PYZdq{}}\\PY{l+s}{marl:Positive}\\PY{l+s}{\\PYZdq{}}\\PY{+w}{ }\\PY{p}{;}\n", + "\\PY{+w}{ }\\PY{n+nn}{prov:}\\PY{n+nt}{wasGeneratedBy}\\PY{+w}{ }\\PY{n+nn}{prefix:}\\PY{n+nt}{Analysis\\PYZus{}1563372853}\\PY{l+m+mf}{.6874764}\\PY{+w}{ }\\PY{p}{]}\\PY{+w}{ }\\PY{p}{.}\n", + "\n", + "\\PY{p}{[}\\PY{p}{]}\\PY{+w}{ }\\PY{k+kt}{a}\\PY{+w}{ }\\PY{n+nn}{senpy:}\\PY{n+nt}{Results}\\PY{+w}{ }\\PY{p}{;}\n", + "\\PY{+w}{ }\\PY{n+nn}{prov:}\\PY{n+nt}{used}\\PY{+w}{ }\\PY{err}{p}\\PY{err}{r}\\PY{err}{e}\\PY{err}{f}\\PY{err}{i}\\PY{err}{x}\\PY{p}{:}\\PY{+w}{ }\\PY{p}{.}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "@prefix : .\n", + "@prefix dc: .\n", + "@prefix emoml: .\n", + "@prefix endpoint: .\n", + "@prefix fam: .\n", + "@prefix marl: .\n", + "@prefix nif: .\n", + "@prefix onyx: .\n", + "@prefix prefix: .\n", + "@prefix prov: .\n", + "@prefix rdf: .\n", + "@prefix rdfs: .\n", + "@prefix senpy: .\n", + "@prefix wna: .\n", + "@prefix xml: .\n", + "@prefix xsd: .\n", + "\n", + "prefix: a senpy:Entry ;\n", + " nif:isString \"Senpy is awesome\" ;\n", + " marl:hasOpinion [ a senpy:Sentiment ;\n", + " marl:hasPolarity \"marl:Positive\" ;\n", + " prov:wasGeneratedBy prefix:Analysis_1563372853.6874764 ] .\n", + "\n", + "[] a senpy:Results ;\n", + " prov:used prefix: .\n" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/sentiment140',\n", + " input=\"Senpy is awesome\",\n", + " outformat=\"turtle\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moving to a different service" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All senpy plugins use the same API, which makes moving from one service to another a breeze.\n", + "\n", + "Let us modify the earlier example, which uses the `sentiment140` service, to use a different service (e.g. the `sentiment-basic` plugin).\n", + "We can do it just by changing the URL of the service:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudC1iYXNpYz9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [\n",
+       "        {\n",
+       "          "@type": "Sentiment",\n",
+       "          "marl:hasPolarity": "marl:Neutral",\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372853.8034046"\n",
+       "        }\n",
+       "      ],\n",
+       "      "nif:isString": "Senpy is awesome",\n",
+       "      "onyx:hasEmotionSet": []\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudC1iYXNpYz9pbnB1dD1TZW5weStpcythd2Vzb21lIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Sentiment\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasPolarity\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}marl:Neutral\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372853.8034046\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is awesome\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudC1iYXNpYz9pbnB1dD1TZW5weStpcythd2Vzb21lIw%3D%3D\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [\n", + " {\n", + " \"@type\": \"Sentiment\",\n", + " \"marl:hasPolarity\": \"marl:Neutral\",\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372853.8034046\"\n", + " }\n", + " ],\n", + " \"nif:isString\": \"Senpy is awesome\",\n", + " \"onyx:hasEmotionSet\": []\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/sentiment-basic',\n", + " input=\"Senpy is awesome\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the structure and annotation schema of the response is the same.\n", + "This makes it very easy to compare and migrate to different services.\n", + "\n", + "Service interoperability is not only useful for users.\n", + "It is also key for other features such as [automated evaluation](http://senpy.readthedocs.io/Evaluation.html).\n", + "This is a compelling reason to adapt existing services to use the Senpy API.\n", + "In fact, the `sentiment140` senpy service is proxy to the public [Sentiment 140 service](http://www.sentiment140.com/)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Emotion analysis" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Senpy uses the `onyx` vocabulary to represent emotions, which incorporates the notion of `EmotionSet`'s, an emotion that is composed of several emotions.\n", + "In a nutshell, an `Entry` is linked to one or more `EmotionSet`, which in turn is made up of one or more `Emotion`.\n", + "\n", + "Let's illustrate it with an example, using the `emotion-depechemood` plugin." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrdGhhdCtzZXJ2aWNlIw%3D%3D",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [],\n",
+       "      "nif:isString": "Senpy is a wonderful that service",\n",
+       "      "onyx:hasEmotionSet": [\n",
+       "        {\n",
+       "          "@type": "EmotionSet",\n",
+       "          "onyx:hasEmotion": [\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:negative-fear",\n",
+       "              "onyx:hasEmotionIntensity": 0.06258366271018097\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:amusement",\n",
+       "              "onyx:hasEmotionIntensity": 0.15784834034155437\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:anger",\n",
+       "              "onyx:hasEmotionIntensity": 0.08728815135373413\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:annoyance",\n",
+       "              "onyx:hasEmotionIntensity": 0.12184635680460143\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:indifference",\n",
+       "              "onyx:hasEmotionIntensity": 0.1374081151031531\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:joy",\n",
+       "              "onyx:hasEmotionIntensity": 0.12267040802346799\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:awe",\n",
+       "              "onyx:hasEmotionIntensity": 0.21085262130713067\n",
+       "            },\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:sadness",\n",
+       "              "onyx:hasEmotionIntensity": 0.09950234435617733\n",
+       "            }\n",
+       "          ],\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372853.9469151"\n",
+       "        }\n",
+       "      ]\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q\\PYZus{}aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrdGhhdCtzZXJ2aWNlIw\\PYZpc{}3D\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful that service\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:negative\\PYZhy{}fear\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.06258366271018097}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:amusement\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.15784834034155437}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:anger\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.08728815135373413}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:annoyance\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.12184635680460143}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:indifference\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.1374081151031531}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:joy\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.12267040802346799}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:awe\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.21085262130713067}\n", + " \\PY{p}{\\PYZcb{}}\\PY{p}{,}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:sadness\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.09950234435617733}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372853.9469151\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrdGhhdCtzZXJ2aWNlIw%3D%3D\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [],\n", + " \"nif:isString\": \"Senpy is a wonderful that service\",\n", + " \"onyx:hasEmotionSet\": [\n", + " {\n", + " \"@type\": \"EmotionSet\",\n", + " \"onyx:hasEmotion\": [\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:negative-fear\",\n", + " \"onyx:hasEmotionIntensity\": 0.06258366271018097\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:amusement\",\n", + " \"onyx:hasEmotionIntensity\": 0.15784834034155437\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:anger\",\n", + " \"onyx:hasEmotionIntensity\": 0.08728815135373413\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:annoyance\",\n", + " \"onyx:hasEmotionIntensity\": 0.12184635680460143\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:indifference\",\n", + " \"onyx:hasEmotionIntensity\": 0.1374081151031531\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:joy\",\n", + " \"onyx:hasEmotionIntensity\": 0.12267040802346799\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:awe\",\n", + " \"onyx:hasEmotionIntensity\": 0.21085262130713067\n", + " },\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:sadness\",\n", + " \"onyx:hasEmotionIntensity\": 0.09950234435617733\n", + " }\n", + " ],\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372853.9469151\"\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/emotion-depechemood',\n", + " input=\"Senpy is a wonderful that service\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you have probably noticed, there are several emotions in this result, each with a different intensity.\n", + "\n", + "We can also tell senpy to only return the emotion with the maximum intensity using the `maxemotion` parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrc2VydmljZSZtYXhlbW90aW9uPVRydWUj",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [],\n",
+       "      "nif:isString": "Senpy is a wonderful service",\n",
+       "      "onyx:hasEmotionSet": [\n",
+       "        {\n",
+       "          "@type": "EmotionSet",\n",
+       "          "onyx:hasEmotion": [\n",
+       "            {\n",
+       "              "@type": "Emotion",\n",
+       "              "onyx:hasEmotionCategory": "wna:awe",\n",
+       "              "onyx:hasEmotionIntensity": 0.21085262130713067\n",
+       "            }\n",
+       "          ],\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372854.0490181"\n",
+       "        }\n",
+       "      ]\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q\\PYZus{}aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrc2VydmljZSZtYXhlbW90aW9uPVRydWUj\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionCategory\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}wna:awe\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionIntensity\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{0.21085262130713067}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.0490181\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tZGVwZWNoZW1vb2Q_aW5wdXQ9U2VucHkraXMrYSt3b25kZXJmdWwrc2VydmljZSZtYXhlbW90aW9uPVRydWUj\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [],\n", + " \"nif:isString\": \"Senpy is a wonderful service\",\n", + " \"onyx:hasEmotionSet\": [\n", + " {\n", + " \"@type\": \"EmotionSet\",\n", + " \"onyx:hasEmotion\": [\n", + " {\n", + " \"@type\": \"Emotion\",\n", + " \"onyx:hasEmotionCategory\": \"wna:awe\",\n", + " \"onyx:hasEmotionIntensity\": 0.21085262130713067\n", + " }\n", + " ],\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.0490181\"\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/emotion-depechemood',\n", + " input=\"Senpy is a wonderful service\",\n", + " maxemotion=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Emotion conversion\n", + "\n", + "Sometimes the model used by a plugin is not right for your application. Senpy ships with emotion conversion capabilities: you can ask for a specific emotion model in your request and the service will try to automatically convert the results.\n", + "\n", + "For example, the `emotion-anew` plugin uses the dimensional `pad` (or VAD, valence-arousal-dominance) model, as we can see here:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQj",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [],\n",
+       "      "nif:isString": "Senpy is a wonderful service and I love it",\n",
+       "      "onyx:hasEmotionSet": [\n",
+       "        {\n",
+       "          "@id": "Emotions0",\n",
+       "          "@type": "EmotionSet",\n",
+       "          "onyx:hasEmotion": [\n",
+       "            {\n",
+       "              "@id": "Emotion0",\n",
+       "              "@type": "Emotion",\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal": 6.44,\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance": 7.11,\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence": 8.72,\n",
+       "              "prov:wasGeneratedBy": "prefix:Analysis_1563372854.2822595"\n",
+       "            }\n",
+       "          ],\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372854.2822595"\n",
+       "        }\n",
+       "      ]\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQj\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotions0\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion0\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}arousal\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{6.44}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}dominance\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.11}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}valence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{8.72}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.2822595\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.2822595\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQj\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [],\n", + " \"nif:isString\": \"Senpy is a wonderful service and I love it\",\n", + " \"onyx:hasEmotionSet\": [\n", + " {\n", + " \"@id\": \"Emotions0\",\n", + " \"@type\": \"EmotionSet\",\n", + " \"onyx:hasEmotion\": [\n", + " {\n", + " \"@id\": \"Emotion0\",\n", + " \"@type\": \"Emotion\",\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 6.44,\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 7.11,\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 8.72,\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.2822595\"\n", + " }\n", + " ],\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.2822595\"\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/emotion-anew',\n", + " input=\"Senpy is a wonderful service and I love it\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we need a category level, we can ask for the equivalent results in the `big6` model:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbm1vZGVsPWVtb21sJTNBYmlnNiM%3D",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [],\n",
+       "      "nif:isString": "Senpy is a wonderful service and I love it",\n",
+       "      "onyx:hasEmotionSet": [\n",
+       "        {\n",
+       "          "@id": "Emotions0",\n",
+       "          "@type": "EmotionSet",\n",
+       "          "onyx:hasEmotion": [\n",
+       "            {\n",
+       "              "@id": "Emotion0",\n",
+       "              "@type": "Emotion",\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal": 6.44,\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance": 7.11,\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence": 8.72,\n",
+       "              "prov:wasGeneratedBy": "prefix:Analysis_1563372854.3354168"\n",
+       "            }\n",
+       "          ],\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372854.3354168"\n",
+       "        }\n",
+       "      ]\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbm1vZGVsPWVtb21sJTNBYmlnNiM\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotions0\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion0\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}arousal\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{6.44}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}dominance\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.11}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}valence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{8.72}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.3354168\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.3354168\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbm1vZGVsPWVtb21sJTNBYmlnNiM%3D\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [],\n", + " \"nif:isString\": \"Senpy is a wonderful service and I love it\",\n", + " \"onyx:hasEmotionSet\": [\n", + " {\n", + " \"@id\": \"Emotions0\",\n", + " \"@type\": \"EmotionSet\",\n", + " \"onyx:hasEmotion\": [\n", + " {\n", + " \"@id\": \"Emotion0\",\n", + " \"@type\": \"Emotion\",\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 6.44,\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 7.11,\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 8.72,\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.3354168\"\n", + " }\n", + " ],\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.3354168\"\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/emotion-anew',\n", + " input=\"Senpy is a wonderful service and I love it\",\n", + " emotionmodel=\"emoml:big6\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because we don't usually care about the original emotion, the conversion can be presented in three ways:\n", + "\n", + "* full: the original and converted emotions are included at the same level\n", + "* filtered: the original emotion is replaced by the converted emotion\n", + "* nested: the original emotion is replaced, but the converted emotion points to it\n", + "\n", + "For example, here's how the `nested` structure would look like:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "  "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbm1vZGVsPWVtb21sJTNBYmlnNiZjb252ZXJzaW9uPW5lc3RlZCM%3D",\n",
+       "  "@type": "Results",\n",
+       "  "entries": [\n",
+       "    {\n",
+       "      "@id": "prefix:",\n",
+       "      "@type": "Entry",\n",
+       "      "marl:hasOpinion": [],\n",
+       "      "nif:isString": "Senpy is a wonderful service and I love it",\n",
+       "      "onyx:hasEmotionSet": [\n",
+       "        {\n",
+       "          "@id": "Emotions0",\n",
+       "          "@type": "EmotionSet",\n",
+       "          "onyx:hasEmotion": [\n",
+       "            {\n",
+       "              "@id": "Emotion0",\n",
+       "              "@type": "Emotion",\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal": 6.44,\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance": 7.11,\n",
+       "              "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence": 8.72,\n",
+       "              "prov:wasGeneratedBy": "prefix:Analysis_1563372854.3876536"\n",
+       "            }\n",
+       "          ],\n",
+       "          "prov:wasGeneratedBy": "prefix:Analysis_1563372854.3876536"\n",
+       "        }\n",
+       "      ]\n",
+       "    }\n",
+       "  ]\n",
+       "}\n",
+       "
\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@context\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbm1vZGVsPWVtb21sJTNBYmlnNiZjb252ZXJzaW9uPW5lc3RlZCM\\PYZpc{}3D\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Results\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}entries\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Entry\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}marl:hasOpinion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}nif:isString\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Senpy is a wonderful service and I love it\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotionSet\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotions0\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}EmotionSet\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}onyx:hasEmotion\\PYZdq{}}\\PY{p}{:} \\PY{p}{[}\n", + " \\PY{p}{\\PYZob{}}\n", + " \\PY{n+nd}{\\PYZdq{}@id\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion0\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nd}{\\PYZdq{}@type\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}Emotion\\PYZdq{}}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}arousal\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{6.44}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}dominance\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{7.11}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns\\PYZsh{}valence\\PYZdq{}}\\PY{p}{:} \\PY{l+m+mf}{8.72}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.3876536\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\\PY{p}{,}\n", + " \\PY{n+nt}{\\PYZdq{}prov:wasGeneratedBy\\PYZdq{}}\\PY{p}{:} \\PY{l+s+s2}{\\PYZdq{}prefix:Analysis\\PYZus{}1563372854.3876536\\PYZdq{}}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + " \\PY{p}{\\PYZcb{}}\n", + " \\PY{p}{]}\n", + "\\PY{p}{\\PYZcb{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "{\n", + " \"@context\": \"http://senpy.gsi.upm.es/api/contexts/YXBpL2Vtb3Rpb24tYW5ldz9pbnB1dD1TZW5weStpcythK3dvbmRlcmZ1bCtzZXJ2aWNlK2FuZCtJK2xvdmUraXQmZW1vdGlvbm1vZGVsPWVtb21sJTNBYmlnNiZjb252ZXJzaW9uPW5lc3RlZCM%3D\",\n", + " \"@type\": \"Results\",\n", + " \"entries\": [\n", + " {\n", + " \"@id\": \"prefix:\",\n", + " \"@type\": \"Entry\",\n", + " \"marl:hasOpinion\": [],\n", + " \"nif:isString\": \"Senpy is a wonderful service and I love it\",\n", + " \"onyx:hasEmotionSet\": [\n", + " {\n", + " \"@id\": \"Emotions0\",\n", + " \"@type\": \"EmotionSet\",\n", + " \"onyx:hasEmotion\": [\n", + " {\n", + " \"@id\": \"Emotion0\",\n", + " \"@type\": \"Emotion\",\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#arousal\": 6.44,\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#dominance\": 7.11,\n", + " \"http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/anew/ns#valence\": 8.72,\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.3876536\"\n", + " }\n", + " ],\n", + " \"prov:wasGeneratedBy\": \"prefix:Analysis_1563372854.3876536\"\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query(f'{endpoint}/emotion-anew',\n", + " input=\"Senpy is a wonderful service and I love it\",\n", + " emotionmodel=\"emoml:big6\",\n", + " conversion=\"nested\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Learn more\n", + "\n", + "[A separate notebook](Advanced.ipynb) covers advanced topics such as listing all plugins available in an endpoint, or creating analysis pipelines that chain several analysis services." ] } ], "metadata": { "anaconda-cloud": {}, - "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "Python 3", "language": "python", diff --git a/docs/advanced.rst b/docs/advanced.rst deleted file mode 100644 index b07c218..0000000 --- a/docs/advanced.rst +++ /dev/null @@ -1,10 +0,0 @@ -Advanced usage --------------- - -.. toctree:: - :maxdepth: 1 - - server-cli - conversion - commandline - development diff --git a/docs/commandline.rst b/docs/commandline.rst deleted file mode 100644 index 47582a7..0000000 --- a/docs/commandline.rst +++ /dev/null @@ -1,10 +0,0 @@ -Command line -============ - -Although the main use of senpy is to publish services, the tool can also be used locally to analyze text in the command line. -This is a short video demonstration: - -.. image:: https://asciinema.org/a/9uwef1ghkjk062cw2t4mhzpyk.png - :width: 100% - :target: https://asciinema.org/a/9uwef1ghkjk062cw2t4mhzpyk - :alt: CLI demo diff --git a/docs/conf.py b/docs/conf.py index 1b1d06f..914ff4c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -130,6 +130,7 @@ html_theme_options = { 'github_user': 'gsi-upm', 'github_repo': 'senpy', 'github_banner': True, + 'sidebar_collapse': True, } diff --git a/docs/conversion.rst b/docs/conversion.rst index c761148..e287066 100644 --- a/docs/conversion.rst +++ b/docs/conversion.rst @@ -1,93 +1,152 @@ -Conversion ----------- +Automatic Model Conversion +-------------------------- -Senpy includes experimental support for emotion/sentiment conversion plugins. +Senpy includes support for emotion and sentiment conversion. +When a user requests a specific model, senpy will choose a strategy to convert the model that the service usually outputs and the model requested by the user. + +Out of the box, senpy can convert from the `emotionml:pad` (pleasure-arousal-dominance) dimensional model to `emoml:big6` (Ekman's big-6) categories, and vice versa. +This specific conversion uses a series of dimensional centroids (`emotionml:pad`) for each emotion category (`emotionml:big6`). +A dimensional value is converted to a category by looking for the nearest centroid. +The centroids are calculated according to this article: + +.. code-block:: text + + Kim, S. M., Valitutti, A., & Calvo, R. A. (2010, June). + Evaluation of unsupervised emotion models to textual affect recognition. + In Proceedings of the NAACL HLT 2010 Workshop on Computational Approaches to Analysis and Generation of Emotion in Text (pp. 62-70). + Association for Computational Linguistics. + + + +It is possible to add new conversion strategies by `Developing a conversion plugin`_. Use === -Consider the original query: http://127.0.0.1:5000/api/?i=hello&algo=emotion-random +Consider the following query to an emotion service: http://senpy.gsi.upm.es/api/emotion-anew?i=good -The requested plugin (emotion-random) returns emotions using Ekman's model (or big6 in EmotionML): +The requested plugin (emotion-random) returns emotions using the VAD space (FSRE dimensions in EmotionML): .. code:: json - ... rest of the document ... - { - "@type": "emotionSet", - "onyx:hasEmotion": { - "@type": "emotion", - "onyx:hasEmotionCategory": "emoml:big6anger" - }, - "prov:wasGeneratedBy": "plugins/emotion-random_0.1" - } + [ + { + "@type": "EmotionSet", + "onyx:hasEmotion": [ + { + "@type": "Emotion", + "emoml:pad-dimensions_arousal": 5.43, + "emoml:pad-dimensions_dominance": 6.41, + "emoml:pad-dimensions_pleasure": 7.47, + "prov:wasGeneratedBy": "prefix:Analysis_1562744784.8789825" + } + ], + "prov:wasGeneratedBy": "prefix:Analysis_1562744784.8789825" + } + ] + -To get these emotions in VAD space (FSRE dimensions in EmotionML), we'd do this: +To get the equivalent of these emotions in Ekman's categories (i.e., Ekman's Big 6 in EmotionML), we'd do this: -http://127.0.0.1:5000/api/?i=hello&algo=emotion-random&emotionModel=emoml:fsre-dimensions +http://senpy.gsi.upm.es/api/emotion-anew?i=good&emotion-model=emoml:big6 This call, provided there is a valid conversion plugin from Ekman's to VAD, would return something like this: .. code:: json - - ... rest of the document ... + [ + { + "@type": "EmotionSet", + "onyx:hasEmotion": [ { - "@type": "emotionSet", - "onyx:hasEmotion": { - "@type": "emotion", - "onyx:hasEmotionCategory": "emoml:big6anger" - }, - "prov:wasGeneratedBy": "plugins/emotion-random.1" - }, { - "@type": "emotionSet", - "onyx:hasEmotion": { - "@type": "emotion", - "A": 7.22, - "D": 6.28, - "V": 8.6 - }, - "prov:wasGeneratedBy": "plugins/Ekman2VAD_0.1" - + "@type": "Emotion", + "onyx:algorithmConfidence": 4.4979, + "onyx:hasEmotionCategory": "emoml:big6happiness" } + ], + "prov:wasDerivedFrom": { + "@id": "Emotions0", + "@type": "EmotionSet", + "onyx:hasEmotion": [ + { + "@id": "Emotion0", + "@type": "Emotion", + "emoml:pad-dimensions_arousal": 5.43, + "emoml:pad-dimensions_dominance": 6.41, + "emoml:pad-dimensions_pleasure": 7.47, + "prov:wasGeneratedBy": "prefix:Analysis_1562745220.1553965" + } + ], + "prov:wasGeneratedBy": "prefix:Analysis_1562745220.1553965" + }, + "prov:wasGeneratedBy": "prefix:Analysis_1562745220.1570725" + } + ] That is called a *full* response, as it simply adds the converted emotion alongside. It is also possible to get the original emotion nested within the new converted emotion, using the `conversion=nested` parameter: +http://senpy.gsi.upm.es/api/emotion-anew?i=good&emotion-model=emoml:big6&conversion=nested + .. code:: json + [ + { + "@type": "EmotionSet", + "onyx:hasEmotion": [ + { + "@type": "Emotion", + "onyx:algorithmConfidence": 4.4979, + "onyx:hasEmotionCategory": "emoml:big6happiness" + } + ], + "prov:wasDerivedFrom": { + "@id": "Emotions0", + "@type": "EmotionSet", + "onyx:hasEmotion": [ + { + "@id": "Emotion0", + "@type": "Emotion", + "emoml:pad-dimensions_arousal": 5.43, + "emoml:pad-dimensions_dominance": 6.41, + "emoml:pad-dimensions_pleasure": 7.47, + "prov:wasGeneratedBy": "prefix:Analysis_1562744962.896306" + } + ], + "prov:wasGeneratedBy": "prefix:Analysis_1562744962.896306" + }, + "prov:wasGeneratedBy": "prefix:Analysis_1562744962.8978968" + } + ] - ... rest of the document ... - { - "@type": "emotionSet", - "onyx:hasEmotion": { - "@type": "emotion", - "onyx:hasEmotionCategory": "emoml:big6anger" - }, - "prov:wasGeneratedBy": "plugins/emotion-random.1" - "onyx:wasDerivedFrom": { - "@type": "emotionSet", - "onyx:hasEmotion": { - "@type": "emotion", - "A": 7.22, - "D": 6.28, - "V": 8.6 - }, - "prov:wasGeneratedBy": "plugins/Ekman2VAD_0.1" - } - - } Lastly, `conversion=filtered` would only return the converted emotions. + +.. code:: json + + [ + { + "@type": "EmotionSet", + "onyx:hasEmotion": [ + { + "@type": "Emotion", + "onyx:algorithmConfidence": 4.4979, + "onyx:hasEmotionCategory": "emoml:big6happiness" + } + ], + "prov:wasGeneratedBy": "prefix:Analysis_1562744925.7322266" + } + ] + Developing a conversion plugin -================================ +============================== Conversion plugins are discovered by the server just like any other plugin. The difference is the slightly different API, and the need to specify the `source` and `target` of the conversion. @@ -106,7 +165,6 @@ For instance, an emotion conversion plugin needs the following: - .. code:: python @@ -114,3 +172,6 @@ For instance, an emotion conversion plugin needs the following: def convert(self, emotionSet, fromModel, toModel, params): pass + + +More implementation details are shown in the `centroids plugin `_. diff --git a/docs/demo.rst b/docs/demo.rst index 96ceea7..b645c09 100644 --- a/docs/demo.rst +++ b/docs/demo.rst @@ -2,7 +2,7 @@ Demo ---- There is a demo available on http://senpy.gsi.upm.es/, where you can test a live instance of Senpy, with several open source plugins. -You can use the playground (a web interface) or make HTTP requests to the service API. +You can use the playground (a web interface) or the HTTP API. .. image:: playground-0.20.png :target: http://senpy.gsi.upm.es diff --git a/docs/development.rst b/docs/development.rst index 5def52e..3b0c1ab 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -19,6 +19,7 @@ Sharing your sentiment analysis with the world has never been easier! .. toctree:: :maxdepth: 1 + server-cli plugins-quickstart plugins-faq plugins-definition diff --git a/docs/eval_table.png b/docs/eval_table.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e7570183fbd2f970188d6fad5b7b3eaded9aca GIT binary patch literal 78822 zcmZ6y19T-(w=EoX*y$MEvC*+@+a23>I!4E~ZQHifv2EM_tM7aFe((NkjGS>sC8<-j z*IIMUId`29IT=xS*dMSUARzGKVnPZaAP~PmK)`jO!GI&idx-QPAm0t#lvEuR^jz?5 z?QM+BERFCTU2ToAsYo@m$8Jgt_%uN4fEe$y2hqgOb(tEwyfj1;7cbOS zeTcKHp-cg{4Ppd2{`>iU>Gau`rLOPXQm-vqk;&`2heU?g>izEe{*{OKlgC@az4Q20 zb_mBsY_e#Wz|+n1_P*UKaOC>NiGS>J_w;bL@_aF9X_M9M(+`W+J2ta?@&R}6RfM85 zGU(BTbpvDkrO$EWbLhSE{Pm#AYcfJtb=!-g^|yl9$1`hZW;~|w53`%PYa6i$ly`^x z{QfTb`>w&sjv=H@xGn3f**6NW4+F#FpV(Dco>FBW$VHFWa$fA2Ja~BCKjeci6?YbC zGhg$s;E*eZU}#~e-n*r!59zThm} z53#YW!uwd{m9QlIvk&87;x}8hNUZ{+(~ywKMW%yYbP(lb{m=;*Hsx4rH_f{M1${Je^e!G!elS^HRJ+j-|uCOY+Z#I17mI_tgzm>>m? z=Xga~&i8Oe+6L#@iKaET)pM7%=adE)74N?*RVlBNYH*k317z|XRh~u>eHET-gwQgf z_9BD&-wfL+z)>BQ{(2y=iyFzW-x84~DTIXAlCz`7(=#N?2JZ(>u6Y_=G`TtmJ<8sq zca5bho_t`XI7qA`q`X>ebdID>Kv~bm;X4t~e<46QcR6J#ME6NinQUwjg-H@I4=*)J z8#kIucqkPio6x)ZGAYAtd|~z6nn?NQ;OF{AK!urRH1*#E!3iUWW2>ACI}6T|Z<~;6 zTpx*^sTuSRdn4`~o6e1K4xC2krb13OrvcU7M|!VXw`LWjq{BxGBw2P)3{pgs>tIA= zp-ksEyU|2WAX3)72$x{z?@id&0FW zo(*xaXN)Sgyu;?cpbRRE7;jlyX4b+ssBJw>q!_s9k~r2n!?1j*8JV~c>4VDnu0 z<|6y_(Tz~AYn378Uvp|#^j8>6q;%Y4Qenik5LAUb#PzHPKH2$Oou$6pgXwns(PMeq z&;wG!jbY`4%3pFM@^j-xj)@orV%S5-LQV(mup@Zf=?Hh_ESqX42r%@LmJOJ9+v*j~ z-FjV$^RziON2ZqGhHqO=MZ&KLtHcrUuGUN*xl+5~yO>SL)U?To?XepCH=*yunduZk zTs8}NW(U?`q3L_6U;my_?1?oNOKNR{Cj=hZr742nPS7*f#O3T;*Ol`Ko;6*CE3zcn zc*{lUp#($f&MmpnfW9Yw-^zV$l80Py34s$Imu0bEwwB!(TE&mIz!3zaW6; zv3DCWX_RxuT7>yYd8^e|4Cg4oxcho?_f}$oZo!JRKjWP10*AOgp|CA=5+tX1J+@-2 zvQWUn>LaK%&DPt9270IsZ4^X!6yj{0%aoJ=&vKc}+{$o}3%Qs}m9nxvz`!RQOAeM> z2P-}A*pB)B{r!93!+hTRO2nMBASk^|bmRP@P@=^%cZ`|PC-O|^F`Z;kN|w{H@q1(q z3!UDdJqNILVqz;1%$o~EGPY<;v9Lqi>3fO@;jEH;2p69GmG3{0E-Gwq;nw&h$Q)$B95U;ldAOsy0u#yXN9`sfx|w6!b^{P!&WZ;cc06FKq!sjHP%Mlx26XvLvh6qM8?cGj^<_g)?CZv@K6VJ7} zkmX{C%tWDCLFLR3l_MVzzLr&UDFa09gfNQg5o_uUl@`0OZ!8fr!xP`Gv`)c|t>88S zCVzQnF1t9%-4M3av)>B{Ik+pew-s`gl7F*T*P&=k&oXyDha;xx*pDwVrb@Z9fy; zs4@I6{I}HPUj`OCGb{K-b4C5a@$$D|XgU&0!yplC5cz88c9LE~Wyg7~RWj2&$77oV zixA*YjA30JPi|%ix9_(P`>=%rBpnzº{jXPsPaomQ}lw$q}mP=gdsy{(NfWPbF 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 ujy!6nSGx>MXX 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ⅇ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 Yw*#ur54O}T9TWK8&hc>d!87rH2Q?N$(f|Me literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst index 04432d5..bbd00b4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,24 +12,97 @@ Welcome to Senpy's documentation! .. image:: https://img.shields.io/pypi/l/requests.svg :target: https://lab.gsi.upm.es/senpy/senpy/ +Senpy is a framework to build sentiment and emotion analysis services. +It provides functionalities for: -Senpy is a framework for sentiment and emotion analysis services. -Senpy services are interchangeable and easy to use because they share a common semantic :doc:`apischema`. +- developing sentiment and emotion classifier and exposing them as an HTTP service +- requesting sentiment and emotion analysis from different providers (i.e. Vader, Sentimet140, ...) using the same interface (:doc:`apischema`). In this way, applications do not depend on the API offered for these services. +- combining services that use different sentiment model (e.g. polarity between [-1, 1] or [0,1] or emotion models (e.g. Ekkman or VAD) +- evaluating sentiment algorithms with well known datasets -If you interested in consuming Senpy services, read :doc:`Quickstart`. + +Using senpy services is as simple as sending an HTTP request with your favourite tool or library. +Let's analyze the sentiment of the text "Senpy is awesome". + +We can call the `Sentiment140 `_ service with an HTTP request using curl: + + +.. code:: shell + :emphasize-lines: 14,18 + + $ curl "http://senpy.gsi.upm.es/api/sentiment140" \ + --data-urlencode "input=Senpy is awesome" + + { + "@context": "http://senpy.gsi.upm.es/api/contexts/YXBpL3NlbnRpbWVudDE0MD8j", + "@type": "Results", + "entries": [ + { + "@id": "prefix:", + "@type": "Entry", + "marl:hasOpinion": [ + { + "@type": "Sentiment", + "marl:hasPolarity": "marl:Positive", + "prov:wasGeneratedBy": "prefix:Analysis_1554389334.6431913" + } + ], + "nif:isString": "Senpy is awesome", + "onyx:hasEmotionSet": [] + } + ] + } + + +Congratulations, you’ve used your first senpy service! +You can observe the result: the polarity is positive (marl:Positive). The reason of this prefix is that Senpy follows a linked data approach. + +You can analyze the same sentence using a different sentiment service (e.g. Vader) and requesting a different format (e.g. turtle): + + + +.. code:: shell + + $ curl "http://senpy.gsi.upm.es/api/sentiment-vader" \ + --data-urlencode "input=Senpy is awesome" \ + --data-urlencode "outformat=turtle" + + @prefix : . + @prefix endpoint: . + @prefix marl: . + @prefix nif: . + @prefix prefix: . + @prefix prov: . + @prefix senpy: . + + prefix: a senpy:Entry ; + nif:isString "Senpy is awesome" ; + marl:hasOpinion [ a senpy:Sentiment ; + marl:hasPolarity "marl:Positive" ; + marl:polarityValue 6.72e-01 ; + prov:wasGeneratedBy prefix:Analysis_1562668175.9808676 ] . + + [] a senpy:Results ; + prov:used prefix: . + +As you see, Vader returns also the polarity value (0.67) in addition to the category (positive). + +If you are interested in consuming Senpy services, read :doc:`Quickstart`. To get familiar with the concepts behind Senpy, and what it can offer for service developers, check out :doc:`development`. :doc:`apischema` contains information about the semantic models and vocabularies used by Senpy. - .. toctree:: :caption: Learn more about senpy: :maxdepth: 2 + :hidden: senpy demo Quickstart.ipynb installation + conversion + Evaluation.ipynb apischema - advanced + development publications - + projects diff --git a/docs/installation.rst b/docs/installation.rst index e81d1f6..7aadd88 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -17,18 +17,20 @@ Through PIP .. code:: bash + pip install senpy + + # Or with --user if you get permission errors: + pip install --user senpy - -Alternatively, you can use the development version: - -.. code:: bash +.. + Alternatively, you can use the development version: - git clone git@github.com:gsi-upm/senpy - cd senpy - pip install --user . + .. code:: bash -If you want to install senpy globally, use sudo instead of the ``--user`` flag. + git clone git@github.com:gsi-upm/senpy + cd senpy + pip install --user . Docker Image ************ diff --git a/docs/plugins-definition.rst b/docs/plugins-definition.rst index bec02a8..a078b3a 100644 --- a/docs/plugins-definition.rst +++ b/docs/plugins-definition.rst @@ -9,20 +9,21 @@ Lastly, it is also possible to add new plugins programmatically. .. contents:: :local: -What is a plugin? -================= +.. + What is a plugin? + ================= -A plugin is a program that, given a text, will add annotations to it. -In practice, a plugin consists of at least two files: + A plugin is a program that, given a text, will add annotations to it. + In practice, a plugin consists of at least two files: -- Definition file: a `.senpy` file that describes the plugin (e.g. what input parameters it accepts, what emotion model it uses). -- Python module: the actual code that will add annotations to each input. + - Definition file: a `.senpy` file that describes the plugin (e.g. what input parameters it accepts, what emotion model it uses). + - Python module: the actual code that will add annotations to each input. -This separation allows us to deploy plugins that use the same code but employ different parameters. -For instance, one could use the same classifier and processing in several plugins, but train with different datasets. -This scenario is particularly useful for evaluation purposes. + This separation allows us to deploy plugins that use the same code but employ different parameters. + For instance, one could use the same classifier and processing in several plugins, but train with different datasets. + This scenario is particularly useful for evaluation purposes. -The only limitation is that the name of each plugin needs to be unique. + The only limitation is that the name of each plugin needs to be unique. Definition files ================ @@ -109,5 +110,3 @@ Now, in a file named ``helloworld.py``: sentiment['marl:hasPolarity'] = 'marl:Negative' entry.sentiments.append(sentiment) yield entry - -The complete code of the example plugin is available `here `__. diff --git a/docs/plugins-faq.rst b/docs/plugins-faq.rst index bfc5967..6f55168 100644 --- a/docs/plugins-faq.rst +++ b/docs/plugins-faq.rst @@ -23,7 +23,7 @@ In practice, this is what a plugin looks like, tests included: .. literalinclude:: ../example-plugins/rand_plugin.py - :emphasize-lines: 5-11 + :emphasize-lines: 21-28 :language: python diff --git a/docs/plugins-quickstart.rst b/docs/plugins-quickstart.rst index a55a4f3..0577a6e 100644 --- a/docs/plugins-quickstart.rst +++ b/docs/plugins-quickstart.rst @@ -37,7 +37,8 @@ The framework consists of two main modules: Senpy core, which is the building bl What is a plugin? ################# -A plugin is a python object that can process entries. Given an entry, it will modify it, add annotations to it, or generate new entries. +A plugin is a python object that can process entries. +Given an entry, it will modify it, add annotations to it, or generate new entries. What is an entry? diff --git a/docs/projects.rst b/docs/projects.rst new file mode 100644 index 0000000..7664ad0 --- /dev/null +++ b/docs/projects.rst @@ -0,0 +1,49 @@ +Projects using Senpy +-------------------- + +Are you using Senpy in your work?, we would love to hear from you! +Here is a list of on-going and past projects that have benefited from senpy: + + +MixedEmotions +,,,,,,,,,,,,, + +`MixedEmotions `_ develops innovative multilingual multi-modal Big Data analytics applications. +The analytics relies on a common toolbox for multi-modal sentiment and emotion analysis. +The NLP parts of the toolbox are based on senpy and its API. + +The toolbox is featured in this publication: + +.. code-block:: text + + Buitelaar, P., Wood, I. D., Arcan, M., McCrae, J. P., Abele, A., Robin, C., … Tummarello, G. (2018). + MixedEmotions: An Open-Source Toolbox for Multi-Modal Emotion Analysis. + IEEE Transactions on Multimedia. + +EuroSentiment +,,,,,,,,,,,,, + +The aim of the EUROSENTIMENT project was to create a pool for multilingual language resources and services for Sentiment Analysis. + +The EuroSentiment project was the main motivation behind the development of Senpy, and some early versions were used: + +.. code-block:: text + + Sánchez-Rada, J. F., Vulcu, G., Iglesias, C. A., & Buitelaar, P. (2014). + EUROSENTIMENT: Linked Data Sentiment Analysis. + Proceedings of the ISWC 2014 Posters & Demonstrations Track + 13th International Semantic Web Conference (ISWC 2014) (Vol. 1272, pp. 145–148). + + +SoMeDi +,,,,,, +`SoMeDi `_ is an ITEA3 project to research machine learning and artificial intelligence techniques that can be used to turn digital interaction data into Digital Interaction Intelligence and approaches that can be used to effectively enter and act in social media, and to automate this process. +SoMeDi exploits senpy's interoperability of services in their customizable data enrichment and NLP workflows. + +TRIVALENT +,,,,,,,,, + +`TRIVALENT `_ is an EU funded project which aims to a better understanding of root causes of the phenomenon of violent radicalisation in Europe in order to develop appropriate countermeasures, ranging from early detection methodologies to techniques of counter-narrative. + +In addition to sentiment and emotion analysis services, trivalent provides other types of senpy services such as radicalism and writing style analysis. + diff --git a/docs/publications.rst b/docs/publications.rst index edd646c..a3cb4ea 100644 --- a/docs/publications.rst +++ b/docs/publications.rst @@ -2,7 +2,7 @@ Publications ============ -If you use Senpy in your research, please cite `Senpy: A Pragmatic Linked Sentiment Analysis Framework `__ (`BibTex `__): +And if you use Senpy in your research, please cite `Senpy: A Pragmatic Linked Sentiment Analysis Framework `__ (`BibTex `__): .. code-block:: text @@ -12,7 +12,6 @@ If you use Senpy in your research, please cite `Senpy: A Pragmatic Linked Sentim 2016 IEEE International Conference on (pp. 735-742). IEEE. - Senpy uses Onyx for emotion representation, first introduced in: .. code-block:: text @@ -28,19 +27,10 @@ Senpy uses Marl for sentiment representation, which was presented in: Westerski, A., Iglesias Fernandez, C. A., & Tapia Rico, F. (2011). Linked opinions: Describing sentiments on the structured web of data. - -Senpy has been used extensively in the toolbox of the MixedEmotions project: - -.. code-block:: text - - Buitelaar, P., Wood, I. D., Arcan, M., McCrae, J. P., Abele, A., Robin, C., … Tummarello, G. (2018). - MixedEmotions: An Open-Source Toolbox for Multi-Modal Emotion Analysis. - IEEE Transactions on Multimedia. - The representation models, formats and challenges are partially covered in a chapter of the book Sentiment Analysis in Social Networks: .. code-block:: text Iglesias, C. A., Sánchez-Rada, J. F., Vulcu, G., & Buitelaar, P. (2017). Linked Data Models for Sentiment and Emotion Analysis in Social Networks. - In Sentiment Analysis in Social Networks (pp. 49-69). \ No newline at end of file + In Sentiment Analysis in Social Networks (pp. 49-69). diff --git a/docs/server-cli.rst b/docs/server-cli.rst index 1bd4d5e..11135f2 100644 --- a/docs/server-cli.rst +++ b/docs/server-cli.rst @@ -1,5 +1,8 @@ -Server -====== +Command line tool +================= + +Basic usage +----------- The senpy server is launched via the `senpy` command: @@ -70,3 +73,14 @@ For instance, to accept connections on port 6000 on any interface: senpy --host 0.0.0.0 --port 6000 For more options, see the `--help` page. + +Sentiment analysis in the command line +-------------------------------------- + +Although the main use of senpy is to publish services, the tool can also be used locally to analyze text in the command line. +This is a short video demonstration: + +.. image:: https://asciinema.org/a/9uwef1ghkjk062cw2t4mhzpyk.png + :width: 100% + :target: https://asciinema.org/a/9uwef1ghkjk062cw2t4mhzpyk + :alt: CLI demo diff --git a/example-plugins/async_plugin.py b/example-plugins/async_plugin.py index dd628f1..3bc008a 100644 --- a/example-plugins/async_plugin.py +++ b/example-plugins/async_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import AnalysisPlugin import multiprocessing diff --git a/example-plugins/basic.py b/example-plugins/basic.py index 1c0c78f..93aaaad 100644 --- a/example-plugins/basic.py +++ b/example-plugins/basic.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + emoticons = { 'pos': [':)', ':]', '=)', ':D'], diff --git a/example-plugins/basic_analyse_entry_plugin.py b/example-plugins/basic_analyse_entry_plugin.py index b0b28bc..3ec042d 100644 --- a/example-plugins/basic_analyse_entry_plugin.py +++ b/example-plugins/basic_analyse_entry_plugin.py @@ -1,5 +1,20 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from senpy import easy_test, models, plugins diff --git a/example-plugins/basic_box_plugin.py b/example-plugins/basic_box_plugin.py index fa7d111..2bf6c09 100644 --- a/example-plugins/basic_box_plugin.py +++ b/example-plugins/basic_box_plugin.py @@ -1,5 +1,20 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from senpy import easy_test, SentimentBox diff --git a/example-plugins/basic_plugin.py b/example-plugins/basic_plugin.py index 0f62f4e..6debd0a 100644 --- a/example-plugins/basic_plugin.py +++ b/example-plugins/basic_plugin.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import easy_test, SentimentBox diff --git a/example-plugins/configurable_plugin.py b/example-plugins/configurable_plugin.py index fcdd22b..c61dcef 100644 --- a/example-plugins/configurable_plugin.py +++ b/example-plugins/configurable_plugin.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import easy_test, models, plugins diff --git a/example-plugins/dummy_plugin.py b/example-plugins/dummy_plugin.py index 7ee2754..640b336 100644 --- a/example-plugins/dummy_plugin.py +++ b/example-plugins/dummy_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import AnalysisPlugin, easy diff --git a/example-plugins/dummy_required_plugin.py b/example-plugins/dummy_required_plugin.py index 237d7ba..def98a2 100644 --- a/example-plugins/dummy_required_plugin.py +++ b/example-plugins/dummy_required_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import AnalysisPlugin, easy diff --git a/example-plugins/emorand_plugin.py b/example-plugins/emorand_plugin.py index 42283cb..0ed1e22 100644 --- a/example-plugins/emorand_plugin.py +++ b/example-plugins/emorand_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import random from senpy.plugins import EmotionPlugin diff --git a/example-plugins/mynoop.py b/example-plugins/mynoop.py index 98b05b7..0f443d0 100644 --- a/example-plugins/mynoop.py +++ b/example-plugins/mynoop.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import noop from senpy.plugins import SentimentPlugin diff --git a/example-plugins/parameterized_plugin.py b/example-plugins/parameterized_plugin.py index f915d38..87c8176 100644 --- a/example-plugins/parameterized_plugin.py +++ b/example-plugins/parameterized_plugin.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import easy_test, models, plugins diff --git a/example-plugins/rand_plugin.py b/example-plugins/rand_plugin.py index 0c9363a..b3d1d87 100644 --- a/example-plugins/rand_plugin.py +++ b/example-plugins/rand_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import random from senpy import SentimentPlugin, Sentiment, Entry diff --git a/example-plugins/sklearn/mydata.py b/example-plugins/sklearn/mydata.py index 7368842..6786430 100644 --- a/example-plugins/sklearn/mydata.py +++ b/example-plugins/sklearn/mydata.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ''' Create a dummy dataset. Messages with a happy emoticon are labelled positive diff --git a/example-plugins/sklearn/mypipeline.py b/example-plugins/sklearn/mypipeline.py index 0394e1e..45f6eab 100644 --- a/example-plugins/sklearn/mypipeline.py +++ b/example-plugins/sklearn/mypipeline.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import CountVectorizer from sklearn.model_selection import train_test_split diff --git a/example-plugins/sklearn/pipeline_plugin.py b/example-plugins/sklearn/pipeline_plugin.py index a4d4b52..64d7bb9 100644 --- a/example-plugins/sklearn/pipeline_plugin.py +++ b/example-plugins/sklearn/pipeline_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import SentimentBox, easy_test from mypipeline import pipeline diff --git a/example-plugins/sleep_plugin.py b/example-plugins/sleep_plugin.py index 95d4381..38c00fe 100644 --- a/example-plugins/sleep_plugin.py +++ b/example-plugins/sleep_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy.plugins import AnalysisPlugin from time import sleep diff --git a/senpy/__init__.py b/senpy/__init__.py index 035c097..f0d31a3 100644 --- a/senpy/__init__.py +++ b/senpy/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2014 J. Fernando Sánchez Rada - Grupo de Sistemas Inteligentes -# DIT, UPM +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# """ Sentiment analysis server in Python """ diff --git a/senpy/__main__.py b/senpy/__main__.py index 640b5ad..bb5d1d1 100644 --- a/senpy/__main__.py +++ b/senpy/__main__.py @@ -1,7 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2014 J. Fernando Sánchez Rada - Grupo de Sistemas Inteligentes -# DIT, UPM +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/senpy/api.py b/senpy/api.py index 8e0503e..b11e162 100644 --- a/senpy/api.py +++ b/senpy/api.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from future.utils import iteritems from .models import Error, Results, Entry, from_string import logging diff --git a/senpy/blueprints.py b/senpy/blueprints.py index a3b4f0a..34b5f7e 100644 --- a/senpy/blueprints.py +++ b/senpy/blueprints.py @@ -1,19 +1,20 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2014 J. Fernando Sánchez Rada - Grupo de Sistemas Inteligentes -# DIT, UPM # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM # -# http://www.apache.org/licenses/LICENSE-2.0 +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# Unless required by applicable law or agreed to in writing, software +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# """ Blueprints for Senpy """ @@ -24,7 +25,7 @@ from . import api from .version import __version__ from functools import wraps -from .gsitk_compat import GSITK_AVAILABLE +from .gsitk_compat import GSITK_AVAILABLE, datasets import logging import json @@ -272,8 +273,6 @@ def plugin(plugin): @api_blueprint.route('/datasets/', methods=['POST', 'GET']) @basic_api -def datasets(): - sp = current_app.senpy - datasets = sp.datasets +def get_datasets(): dic = Datasets(datasets=list(datasets.values())) return dic diff --git a/senpy/cli.py b/senpy/cli.py index e0ee796..7bf203d 100644 --- a/senpy/cli.py +++ b/senpy/cli.py @@ -1,3 +1,18 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from __future__ import print_function import sys diff --git a/senpy/client.py b/senpy/client.py index dfe0579..2805f5b 100644 --- a/senpy/client.py +++ b/senpy/client.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import requests import logging from . import models diff --git a/senpy/extensions.py b/senpy/extensions.py index 2f5f6a6..04c42d6 100644 --- a/senpy/extensions.py +++ b/senpy/extensions.py @@ -1,3 +1,18 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# """ Main class for Senpy. It orchestrates plugin (de)activation and analysis. @@ -274,36 +289,16 @@ class Senpy(object): return response def _get_datasets(self, request): - if not self.datasets: - raise Error( - status=404, - message=("No datasets found." - " Please verify DatasetManager")) datasets_name = request.parameters.get('dataset', None).split(',') for dataset in datasets_name: - if dataset not in self.datasets: + if dataset not in gsitk_compat.datasets: logger.debug(("The dataset '{}' is not valid\n" "Valid datasets: {}").format( - dataset, self.datasets.keys())) + dataset, gsitk_compat.datasets.keys())) raise Error( status=404, message="The dataset '{}' is not valid".format(dataset)) - dm = gsitk_compat.DatasetManager() - datasets = dm.prepare_datasets(datasets_name) - return datasets - - @property - def datasets(self): - self._dataset_list = {} - dm = gsitk_compat.DatasetManager() - for item in dm.get_datasets(): - for key in item: - if key in self._dataset_list: - continue - properties = item[key] - properties['@id'] = key - self._dataset_list[key] = properties - return self._dataset_list + return datasets_name def evaluate(self, params): logger.debug("evaluating request: {}".format(params)) diff --git a/senpy/gsitk_compat.py b/senpy/gsitk_compat.py index c91c1d7..1d46e09 100644 --- a/senpy/gsitk_compat.py +++ b/senpy/gsitk_compat.py @@ -1,4 +1,21 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import logging +import os from pkg_resources import parse_version, get_distribution, DistributionNotFound @@ -17,15 +34,34 @@ try: gsitk_distro = get_distribution("gsitk") GSITK_VERSION = parse_version(gsitk_distro.version) + if not os.environ.get('DATA_PATH'): + os.environ['DATA_PATH'] = os.environ.get('SENPY_DATA', 'senpy_data') + from gsitk.datasets.datasets import DatasetManager from gsitk.evaluation.evaluation import Evaluation as Eval # noqa: F401 from gsitk.evaluation.evaluation import EvalPipeline # noqa: F401 from sklearn.pipeline import Pipeline modules = locals() GSITK_AVAILABLE = True + datasets = {} + manager = DatasetManager() + + for item in manager.get_datasets(): + for key in item: + if key in datasets: + continue + properties = item[key] + properties['@id'] = key + datasets[key] = properties + + def prepare(ds, *args, **kwargs): + return manager.prepare_datasets(ds, *args, **kwargs) + + except (DistributionNotFound, ImportError) as err: logger.debug('Error importing GSITK: {}'.format(err)) logger.warning(IMPORTMSG) GSITK_AVAILABLE = False GSITK_VERSION = () - DatasetManager = Eval = Pipeline = raise_exception + DatasetManager = Eval = Pipeline = prepare = raise_exception + datasets = {} diff --git a/senpy/meta.py b/senpy/meta.py index a8a5b6b..4f69931 100644 --- a/senpy/meta.py +++ b/senpy/meta.py @@ -1,3 +1,18 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ''' Meta-programming for the models. ''' diff --git a/senpy/models.py b/senpy/models.py index 447568e..5b42b86 100644 --- a/senpy/models.py +++ b/senpy/models.py @@ -1,3 +1,18 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ''' Senpy Models. diff --git a/senpy/plugins/__init__.py b/senpy/plugins/__init__.py index 06c8172..962488f 100644 --- a/senpy/plugins/__init__.py +++ b/senpy/plugins/__init__.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from future import standard_library standard_library.install_aliases() @@ -45,7 +61,7 @@ class PluginMeta(models.BaseMeta): plugin_type.add(name) alias = attrs.get('name', name).lower() attrs['_plugin_type'] = plugin_type - logger.debug('Adding new plugin class', name, bases, attrs, plugin_type) + logger.debug('Adding new plugin class: %s %s %s %s', name, bases, attrs, plugin_type) attrs['name'] = alias if 'description' not in attrs: doc = attrs.get('__doc__', None) @@ -94,7 +110,7 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)): Provides a canonical name for plugins and serves as base for other kinds of plugins. """ - logger.debug("Initialising {}".format(info)) + logger.debug("Initialising %s", info) super(Plugin, self).__init__(**kwargs) if info: self.update(info) @@ -164,8 +180,7 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)): def process_entries(self, entries, activity): for entry in entries: - self.log.debug('Processing entry with plugin {}: {}'.format( - self, entry)) + self.log.debug('Processing entry with plugin %s: %s', self, entry) results = self.process_entry(entry, activity) if inspect.isgenerator(results): for result in results: @@ -347,6 +362,9 @@ class Evaluable(Plugin): def evaluate_func(self, X, activity=None): raise Exception('Implement the evaluate_func function') + def evaluate(self, *args, **kwargs): + return evaluate([self], *args, **kwargs) + class SentimentPlugin(Analyser, Evaluable, models.SentimentPlugin): ''' @@ -831,6 +849,9 @@ def evaluate(plugins, datasets, **kwargs): if not hasattr(plug, 'as_pipe'): raise models.Error('Plugin {} cannot be evaluated'.format(plug.name)) + if not isinstance(datasets, dict): + datasets = gsitk_compat.prepare(datasets, download=True) + tuples = list(product(plugins, datasets)) missing = [] for (p, d) in tuples: @@ -844,12 +865,12 @@ def evaluate(plugins, datasets, **kwargs): new_ev = evaluations_to_JSONLD(results, **kwargs) for ev in new_ev: dataset = ev.evaluatesOn - model = ev.evaluates.rstrip('__' + dataset) + model = ev.evaluates cached_evs[(model, dataset)] = ev evaluations = [] - print(tuples, 'Cached evs', cached_evs) + logger.debug('%s. Cached evs: %s', tuples, cached_evs) for (p, d) in tuples: - print('Adding', d, p) + logger.debug('Adding %s, %s', d, p) evaluations.append(cached_evs[(p.id, d)]) return evaluations @@ -868,7 +889,7 @@ def evaluations_to_JSONLD(results, flatten=False): if row.get('CV', True): evaluation['@type'] = ['StaticCV', 'Evaluation'] evaluation.evaluatesOn = row['Dataset'] - evaluation.evaluates = row['Model'] + evaluation.evaluates = row['Model'].rstrip('__' + row['Dataset']) i = 0 if flatten: metric = models.Metric() diff --git a/senpy/plugins/misc/split_plugin.py b/senpy/plugins/misc/split_plugin.py index af13d65..faabb2a 100644 --- a/senpy/plugins/misc/split_plugin.py +++ b/senpy/plugins/misc/split_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy.plugins import Transformation from senpy.models import Entry from nltk.tokenize.punkt import PunktSentenceTokenizer diff --git a/senpy/plugins/postprocessing/emotion/centroids.py b/senpy/plugins/postprocessing/emotion/centroids.py index 492b389..14ccfa7 100644 --- a/senpy/plugins/postprocessing/emotion/centroids.py +++ b/senpy/plugins/postprocessing/emotion/centroids.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy.plugins import EmotionConversionPlugin from senpy.models import EmotionSet, Emotion, Error @@ -85,7 +101,13 @@ class CentroidConversion(EmotionConversionPlugin): def distance(centroid): return sum(distance_k(centroid, original, k) for k in dimensions) - emotion = min(centroids, key=lambda x: distance(centroids[x])) + distances = {k: distance(centroids[k]) for k in centroids} + + logger.debug('Converting %s', original) + logger.debug('Centroids: %s', centroids) + logger.debug('Distances: %s', distances) + + emotion = min(distances, key=lambda x: distances[x]) result = Emotion(onyx__hasEmotionCategory=emotion) result.onyx__algorithmConfidence = distance(centroids[emotion]) diff --git a/senpy/plugins/postprocessing/emotion/ekman2vad.senpy b/senpy/plugins/postprocessing/emotion/ekman2vad.senpy index d089a41..a08938d 100644 --- a/senpy/plugins/postprocessing/emotion/ekman2vad.senpy +++ b/senpy/plugins/postprocessing/emotion/ekman2vad.senpy @@ -9,30 +9,30 @@ centroids: anger: A: 6.95 D: 5.1 - V: 2.7 + P: 2.7 disgust: A: 5.3 D: 8.05 - V: 2.7 + P: 2.7 fear: A: 6.5 D: 3.6 - V: 3.2 + P: 3.2 happiness: A: 7.22 D: 6.28 - V: 8.6 + P: 8.6 sadness: A: 5.21 D: 2.82 - V: 2.21 + P: 2.21 centroids_direction: - emoml:big6 - - emoml:pad + - emoml:pad-dimensions aliases: # These are aliases for any key in the centroid, to avoid repeating a long name several times - A: emoml:pad-dimensions:arousal - V: emoml:pad-dimensions:valence - D: emoml:pad-dimensions:dominance + P: emoml:pad-dimensions_pleasure + A: emoml:pad-dimensions_arousal + D: emoml:pad-dimensions_dominance anger: emoml:big6anger disgust: emoml:big6disgust fear: emoml:big6fear diff --git a/senpy/plugins/postprocessing/emotion/maxEmotion_plugin.py b/senpy/plugins/postprocessing/emotion/maxEmotion_plugin.py index a024d55..80d74f0 100644 --- a/senpy/plugins/postprocessing/emotion/maxEmotion_plugin.py +++ b/senpy/plugins/postprocessing/emotion/maxEmotion_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from senpy import PostProcessing, easy_test diff --git a/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py b/senpy/plugins/sentiment/sentiment140_plugin.py similarity index 75% rename from senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py rename to senpy/plugins/sentiment/sentiment140_plugin.py index 1b7c5ce..a0a391b 100644 --- a/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py +++ b/senpy/plugins/sentiment/sentiment140_plugin.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import requests import json diff --git a/senpy/static/js/main.js b/senpy/static/js/main.js index 23af15c..0951bc2 100644 --- a/senpy/static/js/main.js +++ b/senpy/static/js/main.js @@ -174,10 +174,18 @@ function add_plugin_pipeline(){ function draw_datasets(){ html = ""; - repeated_html = ""+datasets[dataset]["@id"]; - html += "
" + ds = datasets[dataset] + + // html += repeated_html+datasets[dataset]["@id"]+"\">"+datasets[dataset]["@id"]; + html += ` + +
+ + +
+
+` } document.getElementById("datasets").innerHTML = html; } diff --git a/senpy/templates/index.html b/senpy/templates/index.html index a1cd8f4..055620e 100755 --- a/senpy/templates/index.html +++ b/senpy/templates/index.html @@ -233,28 +233,43 @@ In Data Science and Advanced Analytics (DSAA),
+

Automatically evaluate the classification performance of your plugin in several public datasets, and compare it with other plugins.

+

The datasets will be automatically downloaded if they are not already available locally. Depending on the size of the dataset and the speed of the plugin, the evaluation may take a long time.

-
-

Automatically evaluate the classification performance of your plugin in several public datasets, and compare it with other plugins.

-

The datasets will be automatically downloaded if they are not already available locally. Depending on the size of the dataset and the speed of the plugin, the evaluation may take a long time.

- - -
-
- -
- +
+
+
+ Select the plugin. +
+
+
+
- - -
+ +
+
+
+ Select the dataset. +
+
+
+
+
+
+
+ + +
+
+ +
- +
- - - - - - - - - - - - - - - - -
PluginDatasetAccuracyPrecision_macroRecall_macroF1_macroF1_weightedF1_microF1
+
+ + + + + + + + + + + + + + + + +
PluginDatasetAccuracyPrecision_macroRecall_macroF1_macroF1_weightedF1_microF1
+
diff --git a/senpy/testing.py b/senpy/testing.py index 6777a0d..420e90c 100644 --- a/senpy/testing.py +++ b/senpy/testing.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from past.builtins import basestring import os diff --git a/senpy/utils.py b/senpy/utils.py index d7756b8..ba771f9 100644 --- a/senpy/utils.py +++ b/senpy/utils.py @@ -1,3 +1,18 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from . import models, __version__ from collections import MutableMapping import pprint diff --git a/senpy/version.py b/senpy/version.py index c517050..b9dd463 100644 --- a/senpy/version.py +++ b/senpy/version.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import os import logging @@ -13,7 +29,7 @@ def read_version(versionfile=DEFAULT_FILE): return f.read().strip() except IOError: # pragma: no cover logger.error('Running an unknown version of senpy. Be careful!.') - return '0.0' + return 'devel' __version__ = read_version() diff --git a/setup.py b/setup.py index 42e23e0..f8f37da 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,29 @@ -from setuptools import setup +''' +Copyright 2014 GSI DIT UPM -with open('senpy/VERSION') as f: - __version__ = f.read().strip() - assert __version__ +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +from setuptools import setup +from os import path + +try: + with open('senpy/VERSION') as f: + __version__ = f.read().strip() + assert __version__ +except IOError: # pragma: no cover + print('Installing a development version of senpy. Proceed with caution!') + __version__ = 'devel' def parse_requirements(filename): @@ -12,6 +33,11 @@ def parse_requirements(filename): return [line for line in lineiter if line and not line.startswith("#")] +this_directory = path.abspath(path.dirname(__file__)) +with open(path.join(this_directory, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() + + install_reqs = parse_requirements("requirements.txt") test_reqs = parse_requirements("test-requirements.txt") extra_reqs = parse_requirements("extra-requirements.txt") @@ -25,6 +51,8 @@ setup( description=('A sentiment analysis server implementation. ' 'Designed to be extensible, so new algorithms ' 'and sources can be used.'), + long_description=long_description, + long_description_content_type='text/x-rst', author='J. Fernando Sanchez', author_email='balkian@gmail.com', url='https://github.com/gsi-upm/senpy', # use the URL to the github repo diff --git a/tests/test_api.py b/tests/test_api.py index e558046..aabf226 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import logging logger = logging.getLogger(__name__) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 1e33515..4b5144b 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import os import logging diff --git a/tests/test_cli.py b/tests/test_cli.py index bb1000d..fadf137 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import logging from functools import partial diff --git a/tests/test_client.py b/tests/test_client.py index 88bea6e..7ac74fe 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from unittest import TestCase from senpy.testing import patch_requests diff --git a/tests/test_extensions.py b/tests/test_extensions.py index 1107996..02e805e 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from __future__ import print_function import os from copy import deepcopy diff --git a/tests/test_models.py b/tests/test_models.py index d6c59b6..5583cf7 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import logging import jsonschema diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 3281573..6fa98b6 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + import os import pickle diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 5cb1ee4..6a3a340 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from __future__ import print_function import json diff --git a/tests/test_semantics.py b/tests/test_semantics.py index 580a7b8..9fb90d3 100644 --- a/tests/test_semantics.py +++ b/tests/test_semantics.py @@ -1,5 +1,21 @@ #!/usr/local/bin/python # -*- coding: utf-8 -*- +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from future.standard_library import install_aliases install_aliases() diff --git a/tests/test_test.py b/tests/test_test.py index 41360cc..e560d6b 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -1,3 +1,19 @@ +# +# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from unittest import TestCase import requests