{"id":52224,"date":"2025-09-05T00:00:00","date_gmt":"2025-09-05T07:00:00","guid":{"rendered":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/blog\/business-intelligence-case-study-with-griddb-and-python\/"},"modified":"2025-09-05T00:00:00","modified_gmt":"2025-09-05T07:00:00","slug":"business-intelligence-case-study-with-griddb-and-python","status":"publish","type":"post","link":"https:\/\/www.griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/","title":{"rendered":"Business Intelligence Case Study with GridDB and Python"},"content":{"rendered":"<p>How do you know your business is going in the right direction?<\/p>\n<p>With so much data we have in hand, it is very easy for the problem to get lost while cleaning and analysing it. To be honest, I was in the same place. As a Data Analyst\/Data Scientist, I would try to look at the data first and make sense of it. But it not only consumed more time but also made it difficult to explain why I took certain decisions.<\/p>\n<p>A recent project on Business Intelligence taught me the power of problem-solving. For this tutorial, we will be exploring a dataset of a global store. To keep it general, I am going to assume our problem statment here is <code>How to grow our business<\/code>. Then, we will work through the problem statement in a tree-like framework. This will help in structuring our analysis and also get specific KPIs.<\/p>\n<h2>Framework<\/h2>\n<p>The framework breaks this down into two major strategic approaches:<\/p>\n<h3>1&#46; Increase Our Profit<\/h3>\n<p>There are a few ways to boost profits. This framework highlights three core strategies:<\/p>\n<p>\u27a4 Increase the Quantity Sold More units sold usually means more revenue. To do this effectively: &#8211; <strong>Ensure demand is met<\/strong> \u2013 Make sure your supply chain and stock levels can support customer demand. We need more data to do this. &#8211; <strong>Focus on region-specific performance<\/strong> \u2013 Some regions may outperform others, and identifying where sales are strongest helps target efforts better.<\/p>\n<p>\u27a4 Sell More Products with High Profit Margins Not all products are created equal. Shifting focus toward high-margin items can improve profitability. &#8211; Use <strong>product-level<\/strong> data to identify top performers. &#8211; Understand <strong>regional trends<\/strong> to see where high-margin products sell best.<\/p>\n<p>\u27a4 Reduce the Shipping Cost Shipping and logistics can quietly erode margins, so optimizing this is crucial. &#8211; Analyze which <strong>products<\/strong> incur the highest shipping costs. &#8211; Check which <strong>regions<\/strong> are driving those costs and explore smarter routing or fulfillment solutions.<\/p>\n<h3>2&#46; Decrease Our Discount<\/h3>\n<p>While discounts can drive volume, excessive discounting eats into profits. Two key tactics can help manage this:<\/p>\n<p>\u27a4 Identify Best-Selling Products Best-sellers may not need heavy discounting to move. &#8211; Reassess discount policies at the <strong>product<\/strong> level. &#8211; Consider performance variations across <strong>regions<\/strong>.<\/p>\n<p>\u27a4 Uncover Bottlenecks Inefficiencies or slow-moving inventory often trigger unnecessary discounts. &#8211; Identifying and addressing <strong>bottlenecks<\/strong> can reduce dependency on markdowns.<\/p>\n<p>Now that we have a basic framework in place, let us look at each aspect and decide on the metrics.<\/p>\n<h2>What metrics are we tracking?<\/h2>\n<ol>\n<li>Key Performance Indicators (KPIs):\n<ul>\n<li>total sales<\/li>\n<li>total profit<\/li>\n<li>average shipping cost<\/li>\n<li>average discount<\/li>\n<\/ul>\n<\/li>\n<li>Profitability:\n<ul>\n<li>products with most profit<\/li>\n<li>regions with most profit<\/li>\n<\/ul>\n<\/li>\n<li>Discount:\n<ul>\n<li>best selling products (do not need discounts)<\/li>\n<li>bottlenecks due to discounts (low or negative profit margin)<\/li>\n<\/ul>\n<\/li>\n<li>Market segmentation: Identifying customer patterns\n<ul>\n<li>quantity sold by region<\/li>\n<li>profit by region<\/li>\n<li>shipping cost by region<\/li>\n<li>discount by region<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2>Pre-requisites<\/h2>\n<p>The following libraries are required for the below code to execute:<\/p>\n<ol>\n<li>Pandas<\/li>\n<li>GridDB Python Client<\/li>\n<li>Seaborn<\/li>\n<li>Matplotlib<\/li>\n<\/ol>\n<p>GridDB&#8217;s <a href=\"https:\/\/github.com\/griddb\/python_client\">GitHub Page<\/a> covers the installation in detail. Please go through it to be able to interact with the GridDB Server in Python.<\/p>\n<p>Great! Let&#8217;s go ahead and import the libraries<\/p>\n<h2>Importing Libraries<\/h2>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">#this is optional - in case of errors during execution even though the python client is installed, it is likely that this could be a path issue and can be resolved as follows\nimport sys\nsys.path.append(\"\/home\/shripriya\/python_client\")\n#sys.path.append(&lt;path_to_python_client>)&lt;\/path_to_python_client><\/code><\/pre>\n<\/div>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">import griddb_python as griddb\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt<\/code><\/pre>\n<\/div>\n<h2>Dataset<\/h2>\n<p>We will be using an open-source SuperStore dataset that can be downloaded from <a href=\"https:\/\/powerbidocs.com\/2019\/11\/28\/power-bi-sample-data-set-for-practice\/\">here<\/a>. Once we download it and put it in the same folder as the code, we can then use pandas to read it and clean it.<\/p>\n<h2>Reading the Dataframe<\/h2>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">superstore_df = pd.read_excel(\"global_superstore_2016.xlsx\", sheet_name=\"Orders\")\nsuperstore_df.head()<\/code><\/pre>\n<\/div>\n<div style=\"overflow-x: scroll;overflow-y: hidden;\">\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }<\/p>\n<p>    .dataframe tbody tr th {\n        vertical-align: top;\n    }<\/p>\n<p>     .dataframe tbody td {\n       padding: 10px;\n       margin: 10px;\n    }\n  <\/style>\n<table border=\"1\" class=\"dataframe\">\n<thead>\n<tr style=\"text-align: center;\">\n<th>\n        <\/th>\n<th>\n          Row ID\n        <\/th>\n<th>\n          Order ID\n        <\/th>\n<th>\n          Order Date\n        <\/th>\n<th>\n          Ship Date\n        <\/th>\n<th>\n          Ship Mode\n        <\/th>\n<th>\n          Customer ID\n        <\/th>\n<th>\n          Customer Name\n        <\/th>\n<th>\n          Segment\n        <\/th>\n<th>\n          Postal Code\n        <\/th>\n<th>\n          City\n        <\/th>\n<th>\n          Product ID\n        <\/th>\n<th>\n          Category\n        <\/th>\n<th>\n          Sub-Category\n        <\/th>\n<th>\n          Product Name\n        <\/th>\n<th>\n          Sales\n        <\/th>\n<th>\n          Quantity\n        <\/th>\n<th>\n          Discount\n        <\/th>\n<th>\n          Profit\n        <\/th>\n<th>\n          Shipping Cost\n        <\/th>\n<th>\n          Order Priority\n        <\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<th>\n          0\n        <\/th>\n<td>\n          40098\n        <\/td>\n<td>\n          CA-2014-AB10015140-41954\n        <\/td>\n<td>\n          2014-11-11\n        <\/td>\n<td>\n          2014-11-13\n        <\/td>\n<td>\n          First Class\n        <\/td>\n<td>\n          AB-100151402\n        <\/td>\n<td>\n          Aaron Bergman\n        <\/td>\n<td>\n          Consumer\n        <\/td>\n<td>\n          73120.0\n        <\/td>\n<td>\n          Oklahoma City\n        <\/td>\n<td>\n          TEC-PH-5816\n        <\/td>\n<td>\n          Technology\n        <\/td>\n<td>\n          Phones\n        <\/td>\n<td>\n          Samsung Convoy 3\n        <\/td>\n<td>\n          221.980\n        <\/td>\n<td>\n          2\n        <\/td>\n<td>\n          0.0\n        <\/td>\n<td>\n          62.1544\n        <\/td>\n<td>\n          40.77\n        <\/td>\n<td>\n          High\n        <\/td>\n<\/tr>\n<tr>\n<th>\n          1\n        <\/th>\n<td>\n          26341\n        <\/td>\n<td>\n          IN-2014-JR162107-41675\n        <\/td>\n<td>\n          2014-02-05\n        <\/td>\n<td>\n          2014-02-07\n        <\/td>\n<td>\n          Second Class\n        <\/td>\n<td>\n          JR-162107\n        <\/td>\n<td>\n          Justin Ritter\n        <\/td>\n<td>\n          Corporate\n        <\/td>\n<td>\n          NaN\n        <\/td>\n<td>\n          Wollongong\n        <\/td>\n<td>\n          FUR-CH-5379\n        <\/td>\n<td>\n          Furniture\n        <\/td>\n<td>\n          Chairs\n        <\/td>\n<td>\n          Novimex Executive Leather Armchair, Black\n        <\/td>\n<td>\n          3709.395\n        <\/td>\n<td>\n          9\n        <\/td>\n<td>\n          0.1\n        <\/td>\n<td>\n          -288.7650\n        <\/td>\n<td>\n          923.63\n        <\/td>\n<td>\n          Critical\n        <\/td>\n<\/tr>\n<tr>\n<th>\n          2\n        <\/th>\n<td>\n          25330\n        <\/td>\n<td>\n          IN-2014-CR127307-41929\n        <\/td>\n<td>\n          2014-10-17\n        <\/td>\n<td>\n          2014-10-18\n        <\/td>\n<td>\n          First Class\n        <\/td>\n<td>\n          CR-127307\n        <\/td>\n<td>\n          Craig Reiter\n        <\/td>\n<td>\n          Consumer\n        <\/td>\n<td>\n          NaN\n        <\/td>\n<td>\n          Brisbane\n        <\/td>\n<td>\n          TEC-PH-5356\n        <\/td>\n<td>\n          Technology\n        <\/td>\n<td>\n          Phones\n        <\/td>\n<td>\n          Nokia Smart Phone, with Caller ID\n        <\/td>\n<td>\n          5175.171\n        <\/td>\n<td>\n          9\n        <\/td>\n<td>\n          0.1\n        <\/td>\n<td>\n          919.9710\n        <\/td>\n<td>\n          915.49\n        <\/td>\n<td>\n          Medium\n        <\/td>\n<\/tr>\n<tr>\n<th>\n          3\n        <\/th>\n<td>\n          13524\n        <\/td>\n<td>\n          ES-2014-KM1637548-41667\n        <\/td>\n<td>\n          2014-01-28\n        <\/td>\n<td>\n          2014-01-30\n        <\/td>\n<td>\n          First Class\n        <\/td>\n<td>\n          KM-1637548\n        <\/td>\n<td>\n          Katherine Murray\n        <\/td>\n<td>\n          Home Office\n        <\/td>\n<td>\n          NaN\n        <\/td>\n<td>\n          Berlin\n        <\/td>\n<td>\n          TEC-PH-5267\n        <\/td>\n<td>\n          Technology\n        <\/td>\n<td>\n          Phones\n        <\/td>\n<td>\n          Motorola Smart Phone, Cordless\n        <\/td>\n<td>\n          2892.510\n        <\/td>\n<td>\n          5\n        <\/td>\n<td>\n          0.1\n        <\/td>\n<td>\n          -96.5400\n        <\/td>\n<td>\n          910.16\n        <\/td>\n<td>\n          Medium\n        <\/td>\n<\/tr>\n<tr>\n<th>\n          4\n        <\/th>\n<td>\n          47221\n        <\/td>\n<td>\n          SG-2014-RH9495111-41948\n        <\/td>\n<td>\n          2014-11-05\n        <\/td>\n<td>\n          2014-11-06\n        <\/td>\n<td>\n          Same Day\n        <\/td>\n<td>\n          RH-9495111\n        <\/td>\n<td>\n          Rick Hansen\n        <\/td>\n<td>\n          Consumer\n        <\/td>\n<td>\n          NaN\n        <\/td>\n<td>\n          Dakar\n        <\/td>\n<td>\n          TEC-CO-6011\n        <\/td>\n<td>\n          Technology\n        <\/td>\n<td>\n          Copiers\n        <\/td>\n<td>\n          Sharp Wireless Fax, High-Speed\n        <\/td>\n<td>\n          2832.960\n        <\/td>\n<td>\n          8\n        <\/td>\n<td>\n          0.0\n        <\/td>\n<td>\n          311.5200\n        <\/td>\n<td>\n          903.04\n        <\/td>\n<td>\n          Critical\n        <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\n    5 rows \u00d7 23 columns\n  <\/p>\n<\/div>\n<p>We can see that our dataset has a lot of information on the orders being placed. Not all information is useful here. Remember our framework comes in handy here. So, we will go ahead and drop some columns that are not needed.<\/p>\n<h2>Exploratory Data Analysis (EDA)<\/h2>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">#do not need Order ID, Postal Code, Order Priority\ncols = [\"Order ID\", \"Postal Code\", \"Order Priority\"]\nsuperstore_df.drop(columns=cols, inplace=True)<\/code><\/pre>\n<\/div>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">superstore_df.info()<\/code><\/pre>\n<\/div>\n<pre><code>&lt;class 'pandas.core.frame.DataFrame'&gt;\nRangeIndex: 51290 entries, 0 to 51289\nData columns (total 21 columns):\n #   Column         Non-Null Count  Dtype         \n---  ------         --------------  -----         \n 0   Row ID         51290 non-null  int64         \n 1   Order Date     51290 non-null  datetime64[ns]\n 2   Ship Date      51290 non-null  datetime64[ns]\n 3   Ship Mode      51290 non-null  object        \n 4   Customer ID    51290 non-null  object        \n 5   Customer Name  51290 non-null  object        \n 6   Segment        51290 non-null  object        \n 7   City           51290 non-null  object        \n 8   State          51290 non-null  object        \n 9   Country        51290 non-null  object        \n 10  Region         51290 non-null  object        \n 11  Market         51290 non-null  object        \n 12  Product ID     51290 non-null  object        \n 13  Category       51290 non-null  object        \n 14  Sub-Category   51290 non-null  object        \n 15  Product Name   51290 non-null  object        \n 16  Sales          51290 non-null  float64       \n 17  Quantity       51290 non-null  int64         \n 18  Discount       51290 non-null  float64       \n 19  Profit         51290 non-null  float64       \n 20  Shipping Cost  51290 non-null  float64       \ndtypes: datetime64[ns](2), float64(4), int64(2), object(13)\nmemory usage: 8.2+ MB\n<\/code><\/pre>\n<p>Since there are no null-values, we do not need to worry about that. However, the date columns here are not of the correct datatype. So we will quickly change that before inserting the data into GridDB.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># Prepare the DataFrame\nsuperstore_df[\"Order Date\"] = pd.to_datetime(superstore_df[\"Order Date\"]).dt.to_pydatetime()\nsuperstore_df[\"Ship Date\"] = pd.to_datetime(superstore_df[\"Ship Date\"]).dt.to_pydatetime()\n\n# Rename columns to match GridDB naming (no spaces or dashes)\nsuperstore_df.columns = [col.replace(\" \", \"_\").replace(\"-\", \"_\") for col in superstore_df.columns]<\/code><\/pre>\n<\/div>\n<pre><code>\/tmp\/ipykernel_1024\/114528052.py:2: FutureWarning: The behavior of DatetimeProperties.to_pydatetime is deprecated, in a future version this will return a Series containing python datetime objects instead of an ndarray. To retain the old behavior, call `np.array` on the result\n  superstore_df[\"Order Date\"] = pd.to_datetime(superstore_df[\"Order Date\"]).dt.to_pydatetime()\n\/tmp\/ipykernel_1024\/114528052.py:3: FutureWarning: The behavior of DatetimeProperties.to_pydatetime is deprecated, in a future version this will return a Series containing python datetime objects instead of an ndarray. To retain the old behavior, call `np.array` on the result\n  superstore_df[\"Ship Date\"] = pd.to_datetime(superstore_df[\"Ship Date\"]).dt.to_pydatetime()\n<\/code><\/pre>\n<p>Let us verify that the datatypes are indeed changed.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">superstore_df.info()<\/code><\/pre>\n<\/div>\n<pre><code>&lt;class 'pandas.core.frame.DataFrame'&gt;\nRangeIndex: 51290 entries, 0 to 51289\nData columns (total 21 columns):\n #   Column         Non-Null Count  Dtype         \n---  ------         --------------  -----         \n 0   Row_ID         51290 non-null  int64         \n 1   Order_Date     51290 non-null  datetime64[ns]\n 2   Ship_Date      51290 non-null  datetime64[ns]\n 3   Ship_Mode      51290 non-null  object        \n 4   Customer_ID    51290 non-null  object        \n 5   Customer_Name  51290 non-null  object        \n 6   Segment        51290 non-null  object        \n 7   City           51290 non-null  object        \n 8   State          51290 non-null  object        \n 9   Country        51290 non-null  object        \n 10  Region         51290 non-null  object        \n 11  Market         51290 non-null  object        \n 12  Product_ID     51290 non-null  object        \n 13  Category       51290 non-null  object        \n 14  Sub_Category   51290 non-null  object        \n 15  Product_Name   51290 non-null  object        \n 16  Sales          51290 non-null  float64       \n 17  Quantity       51290 non-null  int64         \n 18  Discount       51290 non-null  float64       \n 19  Profit         51290 non-null  float64       \n 20  Shipping_Cost  51290 non-null  float64       \ndtypes: datetime64[ns](2), float64(4), int64(2), object(13)\nmemory usage: 8.2+ MB\n<\/code><\/pre>\n<p>Okay, everything looks great! We can now put our clean data into GridDB.<\/p>\n<h2>Putting the data into GridDB<\/h2>\n<p>Before putting the data into GridDB, we need to create something called <code>containers<\/code>. Containers are like tables in any database. We define our schema that includes the name and data type of the column. Once we create the containers, it is very easy to put the data using the GridDB python client.<\/p>\n<h3>Container Creation<\/h3>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">factory = griddb.StoreFactory.get_instance()\nhost = \"127.0.0.1:10001\"\ncluster = \"myCluster\"\ndb_user = \"admin\"\ndb_password = \"admin\"\n\ntry:\n    gridstore = factory.get_store(notification_member=host, cluster_name=cluster, username=db_user, password=db_password)\n    print(\"Connection successful\")\n\n    conInfo = griddb.ContainerInfo(\n        name=\"sales_data\",\n        column_info_list=[\n            [\"Row_ID\", griddb.Type.LONG],\n            [\"Order_Date\", griddb.Type.TIMESTAMP],\n            [\"Ship_Date\", griddb.Type.TIMESTAMP],\n            [\"Ship_Mode\", griddb.Type.STRING],\n            [\"Customer_ID\", griddb.Type.STRING],\n            [\"Customer_Name\", griddb.Type.STRING],\n            [\"Segment\", griddb.Type.STRING],\n            [\"City\", griddb.Type.STRING],\n            [\"State\", griddb.Type.STRING],\n            [\"Country\", griddb.Type.STRING],\n            [\"Region\", griddb.Type.STRING],\n            [\"Market\", griddb.Type.STRING],\n            [\"Product_ID\", griddb.Type.STRING],\n            [\"Category\", griddb.Type.STRING],\n            [\"Sub_Category\", griddb.Type.STRING],\n            [\"Product_Name\", griddb.Type.STRING],\n            [\"Sales\", griddb.Type.DOUBLE],\n            [\"Quantity\", griddb.Type.INTEGER],\n            [\"Discount\", griddb.Type.DOUBLE],\n            [\"Profit\", griddb.Type.DOUBLE],\n            [\"Shipping_Cost\", griddb.Type.DOUBLE]\n        ],\n        type=griddb.ContainerType.COLLECTION\n    )\n\n    container = gridstore.put_container(conInfo)\n    print(\"Container creation successful\")\n\n\nexcept griddb.GSException as e:\n    for i in range(e.get_error_stack_size()):\n        print(\"[\", i, \"]\")\n        print(\"Error Code:\", e.get_error_code(i))\n        print(\"Location:\", e.get_location(i))\n        print(\"Message:\", e.get_message(i))\n<\/code><\/pre>\n<\/div>\n<pre><code>Connection successful\nContainer creation successful\n<\/code><\/pre>\n<p>If executed correctly, the above code should print out <code>Connection successful<\/code> followed by <code>Container creation successful<\/code>. If not, the exception message will help you troubleshoot. More information on the error codes are available <a href=\"https:\/\/www.toshiba-sol.co.jp\/en\/pro\/griddb\/docs-en\/v4_3_2\/GridDB_ErrorCodes.html\">here<\/a>.<\/p>\n<p>Once our container is in place, let us put the data into it using the following code.<\/p>\n<h3>Inserting the data<\/h3>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">container.put_rows(superstore_df)\nprint(\"Data insertion successful\")<\/code><\/pre>\n<\/div>\n<pre><code>Data insertion successful\n<\/code><\/pre>\n<p>It is very important to verify if our data has been inserted correctly before moving onto the data analysis. Let&#8217;s go ahead and do that.<\/p>\n<h3>Reading the data<\/h3>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">try:\n        gridstore = factory.get_store(notification_member=host, cluster_name=cluster, username=db_user, password=db_password)\n        cont = gridstore.get_container(\"sales_data\")\n        if cont is None:\n            print(\"Does not exist\")\n        print(\"Connection successful\")\n        \n        query_str=\"SELECT *\"\n        query = cont.query(query_str)\n        rs = query.fetch()\n        df = rs.fetch_rows()\n        print(df.info())\n\nexcept griddb.GSException as e:\n    for i in range(e.get_error_stack_size()):\n        print(\"[\",i,\"]\")\n        print(e.get_error_code(i))\n        print(e.get_location(i))\n        print(e.get_message(i))<\/code><\/pre>\n<\/div>\n<pre><code>Connection successful\n&lt;class 'pandas.core.frame.DataFrame'&gt;\nRangeIndex: 51290 entries, 0 to 51289\nData columns (total 21 columns):\n #   Column         Non-Null Count  Dtype         \n---  ------         --------------  -----         \n 0   Row_ID         51290 non-null  int64         \n 1   Order_Date     51290 non-null  datetime64[ns]\n 2   Ship_Date      51290 non-null  datetime64[ns]\n 3   Ship_Mode      51290 non-null  object        \n 4   Customer_ID    51290 non-null  object        \n 5   Customer_Name  51290 non-null  object        \n 6   Segment        51290 non-null  object        \n 7   City           51290 non-null  object        \n 8   State          51290 non-null  object        \n 9   Country        51290 non-null  object        \n 10  Region         51290 non-null  object        \n 11  Market         51290 non-null  object        \n 12  Product_ID     51290 non-null  object        \n 13  Category       51290 non-null  object        \n 14  Sub_Category   51290 non-null  object        \n 15  Product_Name   51290 non-null  object        \n 16  Sales          51290 non-null  float64       \n 17  Quantity       51290 non-null  int64         \n 18  Discount       51290 non-null  float64       \n 19  Profit         51290 non-null  float64       \n 20  Shipping_Cost  51290 non-null  float64       \ndtypes: datetime64[ns](2), float64(4), int64(2), object(13)\nmemory usage: 8.2+ MB\nNone\n<\/code><\/pre>\n<p>Great! Everything looks great. We can now go ahead with our analysis.<\/p>\n<h1>Analysis<\/h1>\n<p>I will define a function that takes the container, query, and output data type as an input and returns the result of the query. This helps me avoid writing the same code over and over again.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">def fetch_data(container, query_str, data_type):\n    rs = container.query(query_str)\n    row_set = rs.fetch()\n    if row_set.has_next():\n        result = row_set.next()\n        result = result.get(type=data_type)\n    \n        return result<\/code><\/pre>\n<\/div>\n<h2>1&#46; Key Performance Indicators (KPI)<\/h2>\n<p>Referencing the framework, we are tracking 4 KPIs &#8211;<\/p>\n<ol>\n<li>Total Sales<\/li>\n<li>Total Profit<\/li>\n<li>Avg Shipping Cost<\/li>\n<li>Avg Discount<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">total_sales = fetch_data(cont, 'SELECT SUM(Sales) FROM sales_data', griddb.Type.DOUBLE)\ntotal_profit = fetch_data(cont, 'SELECT SUM(Profit) FROM sales_data', griddb.Type.DOUBLE)\navg_shipping = fetch_data(cont, 'SELECT AVG(Shipping_Cost) FROM sales_data', griddb.Type.DOUBLE)\navg_discount = fetch_data(cont, 'SELECT AVG(Discount) FROM sales_data', griddb.Type.DOUBLE)\n\nprint(f\"  KPIs\")\nprint(f\"Total Sales: ${total_sales:,.2f}\")\nprint(f\"Total Profit: ${total_profit:,.2f}\")\nprint(f\"Avg Shipping Cost: ${avg_shipping:.2f}\")\nprint(f\"Avg Discount: {avg_discount:.2%}\")<\/code><\/pre>\n<\/div>\n<pre><code>  KPIs\nTotal Sales: $12,642,501.91\nTotal Profit: $1,467,457.29\nAvg Shipping Cost: $26.48\nAvg Discount: 14.29%\n<\/code><\/pre>\n<p>Let&#8217;s go ahead and plot these.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">fig, axes = plt.subplots(2, 2, figsize=(10, 6))\nfig.suptitle(\"Key Performance Indicators (KPIs)\", fontsize=20, fontweight='bold')\n\naxes = axes.flatten()\nfor ax in axes:\n    ax.axis('off')\n\n# KPIs\nkpi_labels = [\"Total Sales\", \"Total Profit\", \"Avg Shipping Cost\", \"Avg Discount\"]\nkpi_values = [\n    f\"${total_sales:,.2f}\",\n    f\"${total_profit:,.2f}\",\n    f\"${avg_shipping:.2f}\",\n    f\"{avg_discount:.2%}\"\n]\nkpi_colors = [\"#4CAF50\", \"#2196F3\", \"#FF9800\", \"#9C27B0\"]\n\n# Fill KPI cards\nfor i, ax in enumerate(axes):\n    ax.text(0.5, 0.6, kpi_labels[i], fontsize=16, ha='center', va='center', weight='bold', color='gray')\n    ax.text(0.5, 0.4, kpi_values[i], fontsize=24, ha='center', va='center', weight='bold', color=kpi_colors[i])\n    ax.set_facecolor(\"#f7f7f7\")\n    ax.set_frame_on(True)\n    ax.patch.set_linewidth(1)\n    ax.patch.set_edgecolor(\"#ddd\")\n\n# Add vertical and horizontal dividers \nfig.subplots_adjust(hspace=0.3, wspace=0.3)\nfig_width, fig_height = fig.get_size_inches()\n\n# Vertical line in the center\nfig.lines.append(plt.Line2D([0.5, 0.5], [0.05, 0.95], color=\"lightgray\", linewidth=1, linestyle=\"--\", transform=fig.transFigure))\n# Horizontal line in the center\nfig.lines.append(plt.Line2D([0.05, 0.95], [0.5, 0.5], color=\"lightgray\", linewidth=1, linestyle=\"--\", transform=fig.transFigure))\n\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()<\/code><\/pre>\n<\/div>\n<p><a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_46_0.png\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_46_0.png\" alt=\"\" width=\"989\" height=\"593\" class=\"aligncenter size-full wp-image-32369\" srcset=\"\/wp-content\/uploads\/2025\/09\/data_insertion_46_0.png 989w, \/wp-content\/uploads\/2025\/09\/data_insertion_46_0-300x180.png 300w, \/wp-content\/uploads\/2025\/09\/data_insertion_46_0-768x460.png 768w, \/wp-content\/uploads\/2025\/09\/data_insertion_46_0-600x360.png 600w\" sizes=\"(max-width: 989px) 100vw, 989px\" \/><\/a><\/p>\n<p>For the next set of metrics, we will be using multiple columns of the database. Let&#8217;s go ahead and retrieve it as a dataframe so that we can easily leverage pandas <code>groupby<\/code> and <code>aggregation<\/code>.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">query_str=\"SELECT *\"\nquery = cont.query(query_str)\nrs = query.fetch()\ndf = rs.fetch_rows()<\/code><\/pre>\n<\/div>\n<h2>2&#46; Profitability<\/h2>\n<p>We are tracking 2 key things here:<\/p>\n<ol>\n<li>products with most profit<\/li>\n<li>regions with most profit<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">top_products_profit = df.groupby('Product_Name')['Profit'].sum().sort_values(ascending=False).head(10)\nregion_profit = df.groupby('Region')['Profit'].sum().sort_values(ascending=False)<\/code><\/pre>\n<\/div>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">fig, axes = plt.subplots(1, 2, figsize=(16, 6))\nsns.barplot(x=top_products_profit.values, y=top_products_profit.index,\n            hue=top_products_profit.index, palette=\"viridis\", legend=False, ax=axes[0])\naxes[0].set_title(\"Top 10 Products by Profit\")\naxes[0].set_xlabel(\"Profit\")\naxes[0].set_ylabel(\"Product\")\n\nsns.barplot(x=region_profit.values, y=region_profit.index,\n            hue=region_profit.index, palette=\"magma\", legend=False, ax=axes[1])\naxes[1].set_title(\"Profit by Region\")\naxes[1].set_xlabel(\"Profit\")\naxes[1].set_ylabel(\"Region\")\nplt.tight_layout()\nplt.show()<\/code><\/pre>\n<\/div>\n<p><a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_52_0.png\"><img decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_52_0.png\" alt=\"\" width=\"1589\" height=\"590\" class=\"aligncenter size-full wp-image-32370\" srcset=\"\/wp-content\/uploads\/2025\/09\/data_insertion_52_0.png 1589w, \/wp-content\/uploads\/2025\/09\/data_insertion_52_0-300x111.png 300w, \/wp-content\/uploads\/2025\/09\/data_insertion_52_0-1024x380.png 1024w, \/wp-content\/uploads\/2025\/09\/data_insertion_52_0-768x285.png 768w, \/wp-content\/uploads\/2025\/09\/data_insertion_52_0-1536x570.png 1536w, \/wp-content\/uploads\/2025\/09\/data_insertion_52_0-600x223.png 600w\" sizes=\"(max-width: 1589px) 100vw, 1589px\" \/><\/a><\/p>\n<h2>3&#46; Discount<\/h2>\n<p>Similarly, for the second branch in our tree framework &#8211; Discount, we will be tracking 2 metrics:<\/p>\n<ol>\n<li>Best selling products (products that do not need discounts)<\/li>\n<li>Bottlenecks due to discounts (products with low or negative profit margin)<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">top_sales_no_discount = df[df['Discount'] == 0].groupby('Product_Name')['Sales'].sum().sort_values(ascending=False).head(10)\ndf['Profit_Margin'] = df['Profit'] \/ df['Sales']\nlow_margin = df[(df['Discount'] > 0.3) & (df['Profit_Margin'] &lt; 0)].groupby('Product_Name')['Profit'].sum().sort_values().head(10)<\/code><\/pre>\n<\/div>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">fig, axes = plt.subplots(1, 2, figsize=(16, 6))\nsns.barplot(x=top_sales_no_discount.values, y=top_sales_no_discount.index,\n            hue=top_sales_no_discount.index, palette=\"cubehelix\", legend=False, ax=axes[0])\naxes[0].set_title(\"Top Selling Products Without Discount\")\naxes[0].set_xlabel(\"Sales\")\naxes[0].set_ylabel(\"Product\")\n\nsns.barplot(x=low_margin.values, y=low_margin.index,\n            hue=low_margin.index, palette=\"coolwarm\", legend=False, ax=axes[1])\naxes[1].set_title(\"High Discount, Low\/Negative Profit\")\naxes[1].set_xlabel(\"Profit\")\naxes[1].set_ylabel(\"Product\")\nplt.tight_layout()\nplt.show()<\/code><\/pre>\n<\/div>\n<p><a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_56_0.png\"><img decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_56_0.png\" alt=\"\" width=\"1589\" height=\"590\" class=\"aligncenter size-full wp-image-32371\" srcset=\"\/wp-content\/uploads\/2025\/09\/data_insertion_56_0.png 1589w, \/wp-content\/uploads\/2025\/09\/data_insertion_56_0-300x111.png 300w, \/wp-content\/uploads\/2025\/09\/data_insertion_56_0-1024x380.png 1024w, \/wp-content\/uploads\/2025\/09\/data_insertion_56_0-768x285.png 768w, \/wp-content\/uploads\/2025\/09\/data_insertion_56_0-1536x570.png 1536w, \/wp-content\/uploads\/2025\/09\/data_insertion_56_0-600x223.png 600w\" sizes=\"(max-width: 1589px) 100vw, 1589px\" \/><\/a><\/p>\n<h2>4&#46; Market Segmentation<\/h2>\n<p>Lastly, we also want to look at the market segmentation and we can segment it by 4 parts:<\/p>\n<ol>\n<li>Quantity sold by region<\/li>\n<li>Profit by region<\/li>\n<li>Shipping cost by region<\/li>\n<li>Discount by region<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">quantity_by_region = df.groupby('Region')['Quantity'].sum().sort_values(ascending=False)\nshipping_by_region = df.groupby('Region')['Shipping_Cost'].mean().sort_values(ascending=False)\ndiscount_by_region = df.groupby('Region')['Discount'].mean().sort_values(ascending=False)<\/code><\/pre>\n<\/div>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">fig, axes = plt.subplots(2, 2, figsize=(18, 12))\n\n# Plot 1: Quantity Sold by Region\nsns.barplot(x=quantity_by_region.values, y=quantity_by_region.index,\n            hue=quantity_by_region.index, palette=\"crest\", legend=False, ax=axes[0, 0])\naxes[0, 0].set_title(\"Quantity Sold by Region\")\naxes[0, 0].set_xlabel(\"Quantity\")\naxes[0, 0].set_ylabel(\"Region\")\n\n# Plot 2: Profit by Region\nsns.barplot(x=region_profit.values, y=region_profit.index,\n            hue=region_profit.index, palette=\"flare\", legend=False, ax=axes[0, 1])\naxes[0, 1].set_title(\"Profit by Region\")\naxes[0, 1].set_xlabel(\"Profit\")\naxes[0, 1].set_ylabel(\"Region\")\n\n# Plot 3: Shipping Cost by Region\nsns.barplot(x=shipping_by_region.values, y=shipping_by_region.index,\n            hue=shipping_by_region.index, palette=\"rocket\", legend=False, ax=axes[1, 0])\naxes[1, 0].set_title(\"Shipping Cost by Region\")\naxes[1, 0].set_xlabel(\"Shipping Cost\")\naxes[1, 0].set_ylabel(\"Region\")\n\n# Plot 4: Discount by Region\nsns.barplot(x=discount_by_region.values, y=discount_by_region.index,\n            hue=discount_by_region.index, palette=\"ch:start=.2,rot=-.3\", legend=False, ax=axes[1, 1])\naxes[1, 1].set_title(\"Average Discount by Region\")\naxes[1, 1].set_xlabel(\"Discount\")\naxes[1, 1].set_ylabel(\"Region\")\n\nplt.tight_layout()\nplt.show()\n<\/code><\/pre>\n<\/div>\n<p><a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_60_0.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/09\/data_insertion_60_0.png\" alt=\"\" width=\"1790\" height=\"1189\" class=\"aligncenter size-full wp-image-32372\" srcset=\"\/wp-content\/uploads\/2025\/09\/data_insertion_60_0.png 1790w, \/wp-content\/uploads\/2025\/09\/data_insertion_60_0-300x199.png 300w, \/wp-content\/uploads\/2025\/09\/data_insertion_60_0-1024x680.png 1024w, \/wp-content\/uploads\/2025\/09\/data_insertion_60_0-768x510.png 768w, \/wp-content\/uploads\/2025\/09\/data_insertion_60_0-1536x1020.png 1536w, \/wp-content\/uploads\/2025\/09\/data_insertion_60_0-600x399.png 600w\" sizes=\"(max-width: 1790px) 100vw, 1790px\" \/><\/a><\/p>\n<h1>Insights<\/h1>\n<p>The problem statement was how can we grow our business. We focused on increasing our profits and reducing our discounts. So what insights can we draw from the visualizations above?<\/p>\n<ol>\n<li>Some specific smartphones are one of the most profitable products for our business, so I would focus on if the demand is met and how can we improve the customer experience for that domain.<\/li>\n<li>Africa and Central Asia stores are facing overall losses. Therefore, we need to consider reducing the scale of the store. More information is needed in this regard.<\/li>\n<li>The second point is validated when we also look at the average discount in Africa and Asia &#8211; the discount is one of the highest while profit margins are negative. These regions, therefore, are contributing negatively to our overall business.<\/li>\n<li>Western Europe seems to be our biggest market with higher demand and higher profit margins. So, we should be doubling down here to grow our business.<\/li>\n<\/ol>\n<h1>Conclusion<\/h1>\n<p>In this tutorial, we developed an end-to-end BI solution for a superstore. The tutorial dived into problem-solving, critical thinking followed by detailed analysis and providing actionable insights. This is close to a real-world application of how a Data\/Business Analyst would approach a business problem.<\/p>\n<p>We also saw that in order to have a centralized data repository, we can make use of GridDB. With GridDB&#8217;s python client, the integration of pandas for analysis seemed effortless.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while cleaning and analysing it. To be honest, I was in the same place. As a Data Analyst\/Data Scientist, I would try to look at [&hellip;]<\/p>\n","protected":false},"author":41,"featured_media":52225,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[121],"tags":[],"class_list":["post-52224","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Business Intelligence Case Study with GridDB and Python | GridDB: Open Source Time Series Database for IoT<\/title>\n<meta name=\"description\" content=\"How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Business Intelligence Case Study with GridDB and Python | GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"og:description\" content=\"How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while\" \/>\n<meta property=\"og:url\" content=\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\" \/>\n<meta property=\"og:site_name\" content=\"GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/griddbcommunity\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-09-05T07:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.griddb.net\/wp-content\/uploads\/2025\/12\/framework.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2360\" \/>\n\t<meta property=\"og:image:height\" content=\"1640\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"griddb-admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:site\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"griddb-admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\"},\"author\":{\"name\":\"griddb-admin\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\"},\"headline\":\"Business Intelligence Case Study with GridDB and Python\",\"datePublished\":\"2025-09-05T07:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\"},\"wordCount\":1388,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2025\/12\/framework.png\",\"articleSection\":[\"Blog\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\",\"url\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\",\"name\":\"Business Intelligence Case Study with GridDB and Python | GridDB: Open Source Time Series Database for IoT\",\"isPartOf\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2025\/12\/framework.png\",\"datePublished\":\"2025-09-05T07:00:00+00:00\",\"description\":\"How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage\",\"url\":\"\/wp-content\/uploads\/2025\/12\/framework.png\",\"contentUrl\":\"\/wp-content\/uploads\/2025\/12\/framework.png\",\"width\":2360,\"height\":1640},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.griddb.net\/en\/#website\",\"url\":\"https:\/\/www.griddb.net\/en\/\",\"name\":\"GridDB: Open Source Time Series Database for IoT\",\"description\":\"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL\",\"publisher\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.griddb.net\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.griddb.net\/en\/#organization\",\"name\":\"Fixstars\",\"url\":\"https:\/\/www.griddb.net\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"contentUrl\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"width\":200,\"height\":83,\"caption\":\"Fixstars\"},\"image\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/griddbcommunity\/\",\"https:\/\/x.com\/GridDBCommunity\",\"https:\/\/www.linkedin.com\/company\/griddb-by-toshiba\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\",\"name\":\"griddb-admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"caption\":\"griddb-admin\"},\"url\":\"https:\/\/www.griddb.net\/en\/author\/griddb-admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Business Intelligence Case Study with GridDB and Python | GridDB: Open Source Time Series Database for IoT","description":"How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/","og_locale":"en_US","og_type":"article","og_title":"Business Intelligence Case Study with GridDB and Python | GridDB: Open Source Time Series Database for IoT","og_description":"How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while","og_url":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/","og_site_name":"GridDB: Open Source Time Series Database for IoT","article_publisher":"https:\/\/www.facebook.com\/griddbcommunity\/","article_published_time":"2025-09-05T07:00:00+00:00","og_image":[{"width":2360,"height":1640,"url":"https:\/\/www.griddb.net\/wp-content\/uploads\/2025\/12\/framework.png","type":"image\/png"}],"author":"griddb-admin","twitter_card":"summary_large_image","twitter_creator":"@GridDBCommunity","twitter_site":"@GridDBCommunity","twitter_misc":{"Written by":"griddb-admin","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#article","isPartOf":{"@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/"},"author":{"name":"griddb-admin","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233"},"headline":"Business Intelligence Case Study with GridDB and Python","datePublished":"2025-09-05T07:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/"},"wordCount":1388,"commentCount":0,"publisher":{"@id":"https:\/\/www.griddb.net\/en\/#organization"},"image":{"@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2025\/12\/framework.png","articleSection":["Blog"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/","url":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/","name":"Business Intelligence Case Study with GridDB and Python | GridDB: Open Source Time Series Database for IoT","isPartOf":{"@id":"https:\/\/www.griddb.net\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage"},"image":{"@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2025\/12\/framework.png","datePublished":"2025-09-05T07:00:00+00:00","description":"How do you know your business is going in the right direction? With so much data we have in hand, it is very easy for the problem to get lost while","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/griddb.net\/en\/blog\/business-intelligence-case-study-with-griddb-and-python\/#primaryimage","url":"\/wp-content\/uploads\/2025\/12\/framework.png","contentUrl":"\/wp-content\/uploads\/2025\/12\/framework.png","width":2360,"height":1640},{"@type":"WebSite","@id":"https:\/\/www.griddb.net\/en\/#website","url":"https:\/\/www.griddb.net\/en\/","name":"GridDB: Open Source Time Series Database for IoT","description":"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL","publisher":{"@id":"https:\/\/www.griddb.net\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.griddb.net\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.griddb.net\/en\/#organization","name":"Fixstars","url":"https:\/\/www.griddb.net\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/","url":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","contentUrl":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","width":200,"height":83,"caption":"Fixstars"},"image":{"@id":"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/griddbcommunity\/","https:\/\/x.com\/GridDBCommunity","https:\/\/www.linkedin.com\/company\/griddb-by-toshiba"]},{"@type":"Person","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233","name":"griddb-admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","caption":"griddb-admin"},"url":"https:\/\/www.griddb.net\/en\/author\/griddb-admin\/"}]}},"_links":{"self":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts\/52224","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/comments?post=52224"}],"version-history":[{"count":0,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts\/52224\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/media\/52225"}],"wp:attachment":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/media?parent=52224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/categories?post=52224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/tags?post=52224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}