{"id":9895,"date":"2023-05-04T18:28:11","date_gmt":"2023-05-05T01:28:11","guid":{"rendered":"https:\/\/tdengine.com\/?p=9895"},"modified":"2025-11-03T08:11:28","modified_gmt":"2025-11-03T16:11:28","slug":"easy-time-series-analysis-with-tdengine","status":"publish","type":"post","link":"https:\/\/tdengine.com\/easy-time-series-analysis-with-tdengine\/","title":{"rendered":"Easy Time-Series Analysis with TDengine"},"content":{"rendered":"\n<p>In this blog, we&#8217;ll show you some examples of the functions and extensions available in TDengine to perform simple time-series analyses.<\/p>\n\n\n\n<p><a href=\"#video\"><em>See video tutorial<\/em><\/a><\/p>\n\n\n\n<h2 class=\"gb-headline gb-headline-91dd51e4 gb-headline-text\">Getting Started with TDengine<\/h2>\n\n\n\n<p>To get started quickly and follow along, you can <a href=\"https:\/\/cloud.tdengine.com\">register for a free TDengine Cloud account.<\/a> The process takes just a minute and <strong>no credit card is required<\/strong>.<\/p>\n\n\n\n<p>After entering the confirmation code, as part of the registration process, <em>make sure you select the checkbox to create a sample database<\/em>. The sample database is synthetic data from smart meters and has voltage, current, and phase as measurements\/metrics and location as a tag\/label.<\/p>\n\n\n\n<p>When you first log in to TDengine Cloud, it will walk you through some of the novel concepts in TDengine. Specifically, it will talk about <a href=\"https:\/\/tdengine.com\/supertable\/\">supertables<\/a> and also the idea of &#8220;one table per device&#8221; in a <a href=\"https:\/\/tdengine.com\/what-is-a-time-series-database\/\">time-series database<\/a> (TSDB). At any time, you can click on the TDengine logo in the top left hand corner to bring up the walkthrough of these concepts.<\/p>\n\n\n\n<h2 class=\"gb-headline gb-headline-6ef36247 gb-headline-text\">Ingesting Beijing Multi-site Air Quality Data<\/h2>\n\n\n\n<p>In addition to data from the sample database, we will also use data from the <a href=\"https:\/\/archive.ics.uci.edu\/dataset\/501\/beijing+multi+site+air+quality+data\" rel=\"noopener\">Beijing Multi-site Air Quality Dataset<\/a>. Inserting the data from this dataset into TDengine is a good exercise in figuring out the different ways to ingest data into TDengine.<\/p>\n\n\n\n<p>The first step in ingesting this data is, of course, to design a schema to hold this data. The data looks as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-csv\"><code class=\"\" data-line=\"\">&quot;No&quot;,&quot;year&quot;,&quot;month&quot;,&quot;day&quot;,&quot;hour&quot;,&quot;PM2.5&quot;,&quot;PM10&quot;,&quot;SO2&quot;,&quot;NO2&quot;,&quot;CO&quot;,&quot;O3&quot;,&quot;TEMP&quot;,&quot;PRES&quot;,&quot;DEWP&quot;,&quot;RAIN&quot;,&quot;wd&quot;,&quot;WSPM&quot;,&quot;station&quot;\n1,2013,3,1,0,4,4,4,7,300,77,-0.7,1023,-18.8,0,&quot;NNW&quot;,4.4,&quot;Aotizhongxin&quot;\n2,2013,3,1,1,8,8,4,7,300,77,-1.1,1023.2,-18.2,0,&quot;N&quot;,4.7,&quot;Aotizhongxin&quot;\n3,2013,3,1,2,7,7,5,10,300,73,-1.1,1023.5,-18.2,0,&quot;NNW&quot;,5.6,&quot;Aotizhongxin&quot;\n4,2013,3,1,3,6,6,11,11,300,72,-1.4,1024.5,-19.4,0,&quot;NW&quot;,3.1,&quot;Aotizhongxin&quot;<\/code><\/pre>\n\n\n\n<p>Each station has its own .csv file. We can treat each station as a device, and so we come up with a schema as follows. First we create a database. We then create a supertable with the station as a tag\/label.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">CREATE DATABASE weather;\n\nCREATE STABLE weather.pollution (ts TIMESTAMP, pm25 FLOAT, pm10 FLOAT, so2 FLOAT, no2 FLOAT, co FLOAT, o3 FLOAT, temperature FLOAT, pressure FLOAT, dewp FLOAT, rain FLOAT, winddirection VARCHAR(8), windspeed FLOAT) TAGS (station VARCHAR(64));\n<\/code><\/pre>\n\n\n\n<h2 class=\"gb-headline gb-headline-c7fc65fb gb-headline-text\">ETL Using Python<\/h2>\n\n\n\n<p>As with any data, we need to do a little bit of ET (extraction and transformation) before loading the data. It&#8217;s really easy to write a Python script to ingest this data using the <a href=\"https:\/\/docs.tdengine.com\/tdengine-reference\/client-libraries\/python\/\">TDengine Python connector<\/a>. The simple Python script is shown below. Note that if you copy and paste this script, make sure the tabs are set correctly after the paste. Also note that this is not the most efficient way of ingesting data into your Cloud instance, but we are just trying to demonstrate some concepts here. You can also use this script to transform these data files into files that can be uploaded into TDengine through the TDengine CLI.<\/p>\n\n\n\n<pre class=\"wp-block-code language-python\"><code class=\"\" data-line=\"\">import sys\nimport os\nimport taosrest\nimport fnmatch\nfrom dotenv import load_dotenv\n\n&#039;&#039;&#039;Environment&#039;&#039;&#039;\nurl = os.environ&#091;&quot;TDENGINE_CLOUD_URL&quot;]\ntoken = os.environ&#091;&quot;TDENGINE_CLOUD_TOKEN&quot;]\n\n&#039;&#039;&#039; SQL statements &#039;&#039;&#039;\n&#039;&#039;&#039;createDatabase = &#039;create database if not exists weather&#039;&#039;&#039;\ncreateStable = &#039;CREATE STABLE if not exists weather.pollution (ts TIMESTAMP, pm2\n5 FLOAT, pm10 FLOAT,&#039;\ncreateStable += &#039;so2 FLOAT, no2 FLOAT, co FLOAT, o3 FLOAT,&#039;\ncreateStable += &#039;temperature FLOAT, pressure FLOAT, dewp FLOAT, rain FLOAT, wind\ndirection VARCHAR(8),&#039;\ncreateStable += &#039;windspeed FLOAT) TAGS (station VARCHAR(64))&#039;\n\n&#039;&#039;&#039; create connection &#039;&#039;&#039;\nconn = taosrest.connect(url=url, token=token)\nconn.execute(createStable)\n\npath = &quot;.\/data&quot;\nfileList = fnmatch.filter(os.listdir(path), &quot;*.csv&quot;)\n\n&#039;&#039;&#039;inputfilename = sys.argv&#091;1]\ntablename = sys.argv&#091;2]\n&#039;&#039;&#039;\n&#039;&#039;&#039; This counter is just used to increment sub-table name &#039;&#039;&#039;\ncounter=1\ntotalRows=0\ntotalLines=0\n\n\nfor eachFile in fileList:\n    tablename=&#039;p&#039;+ str(counter)\n    print(&quot;Inputfilename is:&quot;,eachFile)\n    print(&quot;Table name is:&quot;, tablename)\n\n    infile = open(path+&#039;\/&#039;+eachFile,&#039;r&#039;)\n    &#039;&#039;&#039; skip first header line of each file&#039;&#039;&#039;\n    next(infile)\n\n    for eachline in infile:\n        totalLines += 1\n        &#039;&#039;&#039; if there is an NA in the line, skip the line &#039;&#039;&#039;\n        if &#039;NA&#039; in eachline:\n            print(&#039;Skipping - &#039;+eachline)\n        else:\n            myfields = eachline.split(&#039;,&#039;)\n            insertstr = &#039;insert into weather.&#039;+tablename+&#039; using weather.pollution tags (&#039;+myfields\n&#091;-1].strip()\n            insertstr += &#039;) values (&#039;\n            &#039;&#039;&#039; next 2 lines create the timestamp from the year\/mon\/day\/hr fields in the files &#039;&#039;&#039;\n            insertstr += &#039;&quot;&#039; + &quot;-&quot;.join(myfields&#091;1:4])\n            insertstr += &#039; &#039;+ myfields&#091;4] + &#039;:00:00&quot;,&#039;\n            insertstr += &quot;,&quot;.join(myfields&#091;5:-1]) \n            insertstr += &#039;)&#039;\n            &#039;&#039;&#039;outfile.write(insertstr)&#039;&#039;&#039;\n            &#039;&#039;&#039;print(insertstr)&#039;&#039;&#039;\n            af=conn.execute(insertstr)\n            totalRows += af\n\n    infile.close()\n    &#039;&#039;&#039;outfile.close()&#039;&#039;&#039;\n    print(&#039;Inserted &#039;+str(totalRows)+&#039;so far\\n&#039;)\n    counter += 1<\/code><\/pre>\n\n\n\n<p><strong><em>Update (Nov 2023)<\/em><\/strong><\/p>\n\n\n\n<p><em>TDengine has made the Beijing Multi Site Air Quality dataset available in the new TDengine DB Mart. You can find the DB Mart link at the bottom of the left-hand pane. You can also access the DB Mart by changing the &#8220;Organization&#8221; to &#8220;Public&#8221; at the top of the page.<\/em><\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-d6e1d534\"><img decoding=\"async\" width=\"1099\" height=\"764\" class=\"gb-image gb-image-d6e1d534\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart.png?strip=all&sharp=1\" alt=\"\" title=\"dbmart\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart.png?strip=all&amp;sharp=1 1099w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart-300x209.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart-1024x712.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart-768x534.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart.png?strip=all&amp;sharp=1&amp;w=219 219w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart.png?strip=all&amp;sharp=1&amp;w=439 439w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart.png?strip=all&amp;sharp=1&amp;w=659 659w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/dbmart.png?strip=all&amp;sharp=1&amp;w=879 879w\" sizes=\"(max-width: 1099px) 100vw, 1099px\" \/><\/figure>\n\n\n\n<h2 class=\"gb-headline gb-headline-31d22f58 gb-headline-text\">TDengine SQL Functions<\/h2>\n\n\n\n<p>Now that your data is in the database, we can start using some of the <a href=\"https:\/\/docs.tdengine.com\/tdengine-reference\/sql-manual\/functions\/\">functions<\/a> and <a href=\"https:\/\/docs.tdengine.com\/tdengine-reference\/sql-manual\/time-series-extensions\/\">time-series extensions<\/a> to start doing some basic time-series analysis.<\/p>\n\n\n\n<p>With time-series data with perhaps millions of rows, we are usually interested in downsampling so that we can see data in reasonable time frames. With pollution data, for example, we want to see the exposure on a daily, weekly, or monthly basis. Let&#8217;s say we want to see the weekly exposure to PM2.5 &#8211; particles that are 2.5 microns or less and are able to travel into the lungs and cause respiratory diseases. An AQI (air quality index) of 0-50 is considered Good, above 150 is considered Unhealthy, above 200 Very Unhealthy, and above 300 is Hazardous by US standards.<\/p>\n\n\n\n<p>We can use the following SQL statement to quickly get this information. <code class=\"\" data-line=\"\">_wstart<\/code> is a &#8220;pseudo-column&#8221; and is the start of the downsampled interval, which in this case is a week. We also use the function <code class=\"\" data-line=\"\">AVG<\/code>, which automatically calculates the averages in the defined interval. Furthermore, we also want to see this by station and not mix the data. If you want to see the exposure by day, you simply change the interval to be &#8220;1d&#8221; or &#8220;1n&#8221; for month.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT _wstart, AVG(pm25), station FROM weather.pollution PARTITION BY station INTERVAL (1w);<\/code><\/pre>\n\n\n\n<figure class=\"gb-block-image gb-block-image-18eeb511\"><img decoding=\"async\" width=\"918\" height=\"353\" class=\"gb-image gb-image-18eeb511\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg.png?strip=all&amp;sharp=1 918w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg-300x115.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg-768x295.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg.png?strip=all&amp;sharp=1&amp;w=183 183w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg.png?strip=all&amp;sharp=1&amp;w=367 367w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg.png?strip=all&amp;sharp=1&amp;w=550 550w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-01-avg.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 918px) 100vw, 918px\" \/><\/figure>\n\n\n\n<p>This is what the data looks like when visualized in Grafana. TDengine has a <a href=\"https:\/\/grafana.com\/grafana\/plugins\/tdengine-datasource\/\" rel=\"noopener\">Grafana plugin<\/a>, which allows you to easily <a href=\"https:\/\/docs.tdengine.com\/third-party-tools\/visualization\/grafana\/\">visualize and monitor data<\/a>.<\/p>\n\n\n\n<p>For exposure, it&#8217;s usually better to see a time-weighted average. For this, TDengine provides the <code class=\"\" data-line=\"\">TWA<\/code> function, which you can use similarly to <code class=\"\" data-line=\"\">AVG<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT _wstart, TWA(pm25), station FROM weather.pollution PARTITION BY station INTERVAL (1w);<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">&lt;em&gt;If you use the DBMart database:&lt;\/em&gt;\n\nSELECT _wstart, TWA(pm25), station FROM beijingmultisiteaq.airquality PARTITION BY station INTERVAL (1w);<\/code><\/pre>\n\n\n\n<figure class=\"gb-block-image gb-block-image-2369abcb\"><img decoding=\"async\" width=\"908\" height=\"349\" class=\"gb-image gb-image-2369abcb\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa.png?strip=all&amp;sharp=1 908w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa-300x115.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa-768x295.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa.png?strip=all&amp;sharp=1&amp;w=181 181w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa.png?strip=all&amp;sharp=1&amp;w=363 363w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa.png?strip=all&amp;sharp=1&amp;w=544 544w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-02-twa.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 908px) 100vw, 908px\" \/><\/figure>\n\n\n\n<p>When performing time-series analysis and downsampling, one may have to deal with missing data. In this case, TDengine makes it easy by adding the <code class=\"\" data-line=\"\">FILL<\/code> clause to the query, and it allows you to choose how to deal with missing values. For example, I can choose in this case to do a linear <code class=\"\" data-line=\"\">FILL<\/code>, which fills it with the closest non-null value. <\/p>\n\n\n\n<p>You can also use a sliding window to look at the time series. The sliding window slides your interval window forward by the time unit specified. This is particularly useful for <a href=\"https:\/\/docs.tdengine.com\/advanced-features\/stream-processing\/\">stream processing<\/a>. For example, you can use a sliding window of 1 day, as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT _wstart, avg(pm25), twa(pm25),station FROM weather.pollution PARTITION BY station INTERVAL(1w) SLIDING (1d);<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">&lt;em&gt;If you use the DBMart database:&lt;\/em&gt;\n\nSELECT _wstart, avg(pm25), twa(pm25),station FROM beijingmultisiteaq.airquality PARTITION BY station INTERVAL(1w) SLIDING (1d);<\/code><\/pre>\n\n\n\n<p>There are several <a href=\"https:\/\/docs.tdengine.com\/tdengine-reference\/sql-manual\/functions\/#aggregate-functions\" data-type=\"URL\" data-id=\"https:\/\/docs.tdengine.com\/taos-sql\/function\/#aggregate-functions\">aggregate functions<\/a> supported by TDengine.<\/p>\n\n\n\n<p>If I want to see the 10 highest values of PM2.5 in the dataset, I can simply do the following. Note that I am ordering by the PM2.5 value in ascending order.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT ts, TOP(pm25,10) AS high, station FROM weather.pollution ORDER BY high ASC;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">&lt;em&gt;If you use the DBMart database:&lt;\/em&gt;\n\nSELECT ts, TOP(pm25,10) AS high, station FROM beijingmultisiteaq.airquality ORDER BY high ASC;<\/code><\/pre>\n\n\n\n<figure class=\"gb-block-image gb-block-image-ed671efd\"><img decoding=\"async\" width=\"1105\" height=\"337\" class=\"gb-image gb-image-ed671efd\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten.png?strip=all&amp;sharp=1 1105w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten-300x91.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten-1024x312.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten-768x234.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten.png?strip=all&amp;sharp=1&amp;w=221 221w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten.png?strip=all&amp;sharp=1&amp;w=442 442w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten.png?strip=all&amp;sharp=1&amp;w=663 663w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-03-top-ten.png?strip=all&amp;sharp=1&amp;w=884 884w\" sizes=\"(max-width: 1105px) 100vw, 1105px\" \/><\/figure>\n\n\n\n<p>The function <code class=\"\" data-line=\"\">DIFF<\/code>, which returns the difference between the current value and the previous value, is also very useful when setting up a time series as a supervised machine learning dataframe. Note that in this case you would have to select from the individual station, not the supertable. Subtracting the result of <code class=\"\" data-line=\"\">DIFF<\/code> from the current value gives you the previous value, in case it isn&#8217;t clear.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT ts, pm25-DIFF(pm25), pm25 FROM weather.p1;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">&lt;em&gt;If you use the DBMart database:&lt;\/em&gt;\n\nSELECT ts, pm25-DIFF(pm25), pm25 FROM beijingmultisiteaq.p1;<\/code><\/pre>\n\n\n\n<p>You can, of course, do the same thing if you are setting up a multivariate series as well. So for example, if you are looking at PM25, CO, NO2 and windspeed, you can do the following:<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT ts, pm25-DIFF(pm25), co-DIFF(co), no2-DIFF(no2), windspeed-DIFF(windspeed), pm25,co,no2,windspeed FROM weather.p1;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">&lt;em&gt;If you use the DBMart database:&lt;\/em&gt;\n\nSELECT ts, pm25-DIFF(pm25), co-DIFF(co), no2-DIFF(no2), windspeed-DIFF(windspeed), pm25,co,no2,windspeed FROM beijingmultisiteaq.p1;<\/code><\/pre>\n\n\n\n<p>Using the Python connector, you can get the results of any of the above queries into a Pandas dataframe.<\/p>\n\n\n\n<p>Moving averages are frequently used in time-series analysis. TDengine provides the <code class=\"\" data-line=\"\">MAVG<\/code> function, which takes the column and also the number of values over which the moving average is calculated. In our case, since the measurement is collected hourly, if we want to see the weekly moving average we can do the following:<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT  ts, MAVG(pm25,168) FROM weather.p1;<\/code><\/pre>\n\n\n\n<p><em>If you use the DBMart database:<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT  ts, MAVG(pm25,168) FROM beijingmultisiteaq.p1;<\/code><\/pre>\n\n\n\n<p>TDengine also provides a <code class=\"\" data-line=\"\">HISTOGRAM<\/code> function, which we could use to see how many measurements fall into the good, unhealthy, very unhealthy, and hazardous categories. We can look at this on a yearly basis to see whether the air quality is getting better. The <code class=\"\" data-line=\"\">HISTOGRAM<\/code> function returns a table\/grid.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT _wstart, HISTOGRAM(pm25, &quot;user_input&quot;,&quot;&#091;50,100,200,300,350]&quot;,0), station FROM weather.pollution PARTITION BY station INTERVAL(1y);<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">&lt;em&gt;If you use the DBMart database:&lt;\/em&gt;\n\nSELECT _wstart, HISTOGRAM(pm25, &quot;user_input&quot;,&quot;&#091;50,100,200,300,350]&quot;,0), station FROM beijingmultisiteaq.airquality PARTITION BY station INTERVAL(1y);<\/code><\/pre>\n\n\n\n<figure class=\"gb-block-image gb-block-image-f5d76755\"><img decoding=\"async\" width=\"1110\" height=\"438\" class=\"gb-image gb-image-f5d76755\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram.png?strip=all&amp;sharp=1 1110w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram-300x118.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram-1024x404.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram-768x303.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram.png?strip=all&amp;sharp=1&amp;w=222 222w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram.png?strip=all&amp;sharp=1&amp;w=444 444w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram.png?strip=all&amp;sharp=1&amp;w=666 666w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-04-histogram.png?strip=all&amp;sharp=1&amp;w=888 888w\" sizes=\"(max-width: 1110px) 100vw, 1110px\" \/><\/figure>\n\n\n\n<h2 class=\"gb-headline gb-headline-c1045c00 gb-headline-text\">Simple Analysis on TDengine Sample Database<\/h2>\n\n\n\n<p><strong><em>Update (Nov 2023)<\/em><\/strong><\/p>\n\n\n\n<p><em>In the TDengine DBMart you can now find a 10 billion row database of smart meter data as well as a real-time database with smart meter data.<\/em><\/p>\n\n\n\n<p>Let&#8217;s shift our attention to the sample database in TDengine Cloud. As we mentioned, this has synthetic data from smart meters in various cities.<\/p>\n\n\n\n<p>To see what the supertable looks like, we can do the following. The name of the database is <code class=\"\" data-line=\"\">test<\/code>, and <code class=\"\" data-line=\"\">meters<\/code> is the supertable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">DESC test.meters;<\/code><\/pre>\n\n\n\n<figure class=\"gb-block-image gb-block-image-fa9e4071\"><img decoding=\"async\" width=\"1455\" height=\"243\" class=\"gb-image gb-image-fa9e4071\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema.png?strip=all&amp;sharp=1 1455w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema-300x50.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema-1024x171.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema-768x128.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema.png?strip=all&amp;sharp=1&amp;w=582 582w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema.png?strip=all&amp;sharp=1&amp;w=873 873w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema.png?strip=all&amp;sharp=1&amp;w=1164 1164w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-05-schema.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<p>As you can see, for each location we have the current, voltage, and phase.<\/p>\n\n\n\n<p>If you wanted to get the hourly energy consumption in kWh, the following simple query would suffice. Note that we use the cosine function, <code class=\"\" data-line=\"\">COS<\/code>, that is provided by TDengine. We divide by 1000 to get the result in kW. Since this is an hourly sum, we are getting the approximate consumption in kWh. I also constrain the timestamp and exclude the first and last days because there is not enough data there.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT _wstart, SUM(current*voltage*COS(phase))\/1000 AS kWh, location FROM test.meters WHERE  ts&gt;&#039;2017-07-15&#039; and ts &lt; &#039;2017-07-24&#039; PARTITION BY location INTERVAL (1h);<\/code><\/pre>\n\n\n\n<p>When I visualize this in Grafana, I get the following.<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-406e6be1\"><img decoding=\"async\" width=\"911\" height=\"294\" class=\"gb-image gb-image-406e6be1\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana.png?strip=all&amp;sharp=1 911w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana-300x97.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana-768x248.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana.png?strip=all&amp;sharp=1&amp;w=182 182w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana.png?strip=all&amp;sharp=1&amp;w=364 364w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana.png?strip=all&amp;sharp=1&amp;w=546 546w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.071-06-grafana.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 911px) 100vw, 911px\" \/><\/figure>\n\n\n\n<p>You can get the difference between the maximum and minimum values of a column by using the <code class=\"\" data-line=\"\">SPREAD<\/code> function. Note that you can always constrain the time spans by using a <code class=\"\" data-line=\"\">WHERE<\/code> clause on the timestamp field.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">SELECT SPREAD(voltage), SPREAD(current), location FROM test.meters PARTITION BY location;<\/code><\/pre>\n\n\n\n<p>In addition to these, TDengine also provides functions like <code class=\"\" data-line=\"\">STDDEV<\/code> (standard deviation), <code class=\"\" data-line=\"\">MODE<\/code> (the value with the highest frequency), and several other useful functions for basic and easy time-series analysis.<\/p>\n\n\n\n<p>We hope this has been useful. If you have questions you can always visit the <a href=\"https:\/\/discord.gg\/MnSThqHzV2\" rel=\"noopener\">TDengine Discord channel<\/a>.  <\/p>\n\n\n\n<h2 class=\"gb-headline gb-headline-64db9941 gb-headline-text\" id=\"video\">Video Tutorial<\/h2>\n\n\n\n<p>The following video gives a walkthrough of the information discussed in this article.<\/p>\n\n\n<div class=\"gb-container gb-container-0ff8e5c1\">\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"TDengine Webinar - Making Time Series Data Analysis Easy with TDengine - Time Series Database Videos\" width=\"1200\" height=\"675\" src=\"https:\/\/www.youtube.com\/embed\/t4hnZSq1q0Y?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n<\/div>","protected":false},"excerpt":{"rendered":"<p>With SQL functions and extensions for time series, TDengine makes it easy to perform simple time-series analysis on your data.<\/p>\n","protected":false},"author":81,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[21],"tags":[],"ppma_author":[167],"class_list":["post-9895","post","type-post","status-publish","format-standard","hentry","category-engineering"],"authors":[{"term_id":167,"user_id":81,"is_guest":0,"slug":"chait","display_name":"Chait Diwadkar","avatar_url":{"url":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg","url2x":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg"},"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/9895","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/users\/81"}],"replies":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/comments?post=9895"}],"version-history":[{"count":15,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/9895\/revisions"}],"predecessor-version":[{"id":29460,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/9895\/revisions\/29460"}],"wp:attachment":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/media?parent=9895"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/categories?post=9895"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/tags?post=9895"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/ppma_author?post=9895"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}