1 00:00:00,580 --> 00:00:02,450 - [Instructor] For our last application 2 00:00:02,450 --> 00:00:06,240 demonstrating IoT pub/sub capabilities, 3 00:00:06,240 --> 00:00:10,150 we're going to go back to working with the PubNub service. 4 00:00:10,150 --> 00:00:13,060 And in this case we're going to take advantage 5 00:00:13,060 --> 00:00:16,320 of their pubnub module, which is a Python module 6 00:00:16,320 --> 00:00:19,610 for interacting with the PubNub service. 7 00:00:19,610 --> 00:00:21,030 And we're going to be using that 8 00:00:21,030 --> 00:00:25,450 to visualize data from their simulated Market Orders stream. 9 00:00:25,450 --> 00:00:29,170 And that Market Orders stream has fake stock information 10 00:00:29,170 --> 00:00:32,110 for several different companies. 11 00:00:32,110 --> 00:00:37,110 Now, you can also use the pubnub module to publish messages 12 00:00:38,270 --> 00:00:40,020 through the PubNub service. 13 00:00:40,020 --> 00:00:41,310 We don't cover that here, 14 00:00:41,310 --> 00:00:44,410 but this link shows you the documentation 15 00:00:44,410 --> 00:00:46,690 for the pubnub Python SDK. 16 00:00:46,690 --> 00:00:50,180 And it describes both, subscribing to existing streams, 17 00:00:50,180 --> 00:00:54,750 and creating your owns streams to publish messages as well. 18 00:00:54,750 --> 00:00:56,620 In order to use the pubnub module 19 00:00:56,620 --> 00:00:58,160 you will need to install it. 20 00:00:58,160 --> 00:01:00,200 So, this is the installation command. 21 00:01:00,200 --> 00:01:05,050 And we've provided a stocklistener.py file, or script, 22 00:01:05,050 --> 00:01:06,680 that is going to subscribe 23 00:01:06,680 --> 00:01:09,230 to that simulated Market Orders stream, 24 00:01:09,230 --> 00:01:14,230 and visualize the stock prices as they change in a bar plot. 25 00:01:14,330 --> 00:01:17,140 Now, the PubNub client that we're going to be working with 26 00:01:17,140 --> 00:01:19,100 is going to give us back JSON objects 27 00:01:19,100 --> 00:01:21,770 in the form of Python dictionaries, 28 00:01:21,770 --> 00:01:24,900 and we'll extract the information we need from that, 29 00:01:24,900 --> 00:01:28,760 and then go ahead and visualize that data. 30 00:01:28,760 --> 00:01:30,720 Now, before we actually get into 31 00:01:30,720 --> 00:01:33,110 the source code for this application, 32 00:01:33,110 --> 00:01:37,180 let's run our script and demonstrate it in action. 33 00:01:37,180 --> 00:01:39,210 So, we set up this script 34 00:01:39,210 --> 00:01:41,350 with the command-line argument that specifies 35 00:01:41,350 --> 00:01:44,660 the total number of messages you wish to receive 36 00:01:44,660 --> 00:01:46,010 from the PubNub service. 37 00:01:46,010 --> 00:01:49,640 So we'll just do 1,000 for demonstration purposes here. 38 00:01:49,640 --> 00:01:51,970 And as you'll see in a moment, 39 00:01:51,970 --> 00:01:54,750 it will pop open a matplotlib window 40 00:01:54,750 --> 00:01:56,840 with a seaborn bar plot. 41 00:01:56,840 --> 00:02:00,740 And as the stock prices are coming in, 42 00:02:00,740 --> 00:02:02,690 we're displaying the name of the company 43 00:02:02,690 --> 00:02:05,370 and the current stock price at the command line, 44 00:02:05,370 --> 00:02:08,820 but we're also updating a dictionary in memory, 45 00:02:08,820 --> 00:02:11,680 and that dictionary's data is being used 46 00:02:11,680 --> 00:02:14,500 to visualize the stock prices here 47 00:02:14,500 --> 00:02:16,110 in the context of the bar plot. 48 00:02:16,110 --> 00:02:18,360 So, the coloring of the bar plot 49 00:02:18,360 --> 00:02:20,800 is not important in this case, 50 00:02:20,800 --> 00:02:22,640 it's just for visual effect. 51 00:02:22,640 --> 00:02:24,500 But the nice thing that we're getting here 52 00:02:24,500 --> 00:02:28,990 is the dynamic, animated presentation of how those values 53 00:02:28,990 --> 00:02:31,870 are changing with respect to one another. 54 00:02:31,870 --> 00:02:35,990 And notice, by the way, that the y-axis is auto-scaling 55 00:02:35,990 --> 00:02:39,740 based on the current values of all the different stocks. 56 00:02:39,740 --> 00:02:44,090 So, the top end of the y-axis is always going to have to do 57 00:02:44,090 --> 00:02:46,270 with the value of the stock 58 00:02:46,270 --> 00:02:48,703 that has the current highest price. 59 00:02:49,990 --> 00:02:52,330 This is the PubNub webpage 60 00:02:52,330 --> 00:02:55,017 for their simulated Market Orders stream. 61 00:02:55,017 --> 00:02:57,870 And as you can see when you go to this webpage, 62 00:02:57,870 --> 00:02:59,270 it is going to show you 63 00:02:59,270 --> 00:03:02,890 simulated prices changing over time here. 64 00:03:02,890 --> 00:03:06,060 And if we go and scroll down further on the page, 65 00:03:06,060 --> 00:03:07,960 you'll see that it tells you the name 66 00:03:07,960 --> 00:03:09,950 of the channel that you need to use, 67 00:03:09,950 --> 00:03:12,220 and the subscription key that you need to use. 68 00:03:12,220 --> 00:03:15,320 And we'll need both of those in our source code in a moment. 69 00:03:15,320 --> 00:03:17,550 And it also shows you the format 70 00:03:17,550 --> 00:03:21,380 of the JavaScript Object Notation objects 71 00:03:21,380 --> 00:03:23,360 that represent each message. 72 00:03:23,360 --> 00:03:26,640 So, every message we receive has these five keys in it, 73 00:03:26,640 --> 00:03:29,220 but we're actually only going to take advantage, 74 00:03:29,220 --> 00:03:32,290 in this app, of the symbol, and the bid price 75 00:03:32,290 --> 00:03:35,920 so that we can visualize the stock price changes 76 00:03:35,920 --> 00:03:37,680 as they occur. 77 00:03:37,680 --> 00:03:39,660 So now that we've seen the animation, 78 00:03:39,660 --> 00:03:41,180 let's take a look at the script 79 00:03:41,180 --> 00:03:43,360 that implements the animation. 80 00:03:43,360 --> 00:03:44,910 Now, we've seen these imports 81 00:03:44,910 --> 00:03:47,060 in lines three through seven previously. 82 00:03:47,060 --> 00:03:49,890 Lines nine through 12 import the various types 83 00:03:49,890 --> 00:03:53,280 that we're going to need from the pubnub module. 84 00:03:53,280 --> 00:03:55,120 The first type here is a class 85 00:03:55,120 --> 00:03:57,320 that we'll be creating a subclass of 86 00:03:57,320 --> 00:04:00,050 to listen for messages from PubNub. 87 00:04:00,050 --> 00:04:02,540 We'll get two different kinds of messages from PubNub 88 00:04:02,540 --> 00:04:03,740 in this example. 89 00:04:03,740 --> 00:04:06,360 Administrative messages that have a constant 90 00:04:06,360 --> 00:04:09,390 in this PNStatusCategory type, 91 00:04:09,390 --> 00:04:12,570 and also messages from the live stream itself. 92 00:04:12,570 --> 00:04:14,830 We'll use the PNConfiguration object 93 00:04:14,830 --> 00:04:18,060 to specify the subscription key 94 00:04:18,060 --> 00:04:20,900 that's required for connecting to a stream 95 00:04:20,900 --> 00:04:22,670 in the context of PubNub. 96 00:04:22,670 --> 00:04:25,750 And the PubNub client will not only be used 97 00:04:25,750 --> 00:04:27,320 to register a listener 98 00:04:27,320 --> 00:04:29,900 for all the different messages that we'll receive, 99 00:04:29,900 --> 00:04:32,600 but also to subscribe to the stream 100 00:04:32,600 --> 00:04:36,500 to start the flow of messages into our app. 101 00:04:36,500 --> 00:04:37,930 Now, this list right here 102 00:04:37,930 --> 00:04:40,320 contains the names of the five different companies 103 00:04:40,320 --> 00:04:44,800 we'll be receiving from the simulated Market Orders stream. 104 00:04:44,800 --> 00:04:48,410 And we use that list to help create a data-frame here, 105 00:04:48,410 --> 00:04:50,520 where we'll store the names of the companies, 106 00:04:50,520 --> 00:04:54,120 and their most recent stock price in each case. 107 00:04:54,120 --> 00:04:58,220 So, we're using a dictionary to initialize this data frame. 108 00:04:58,220 --> 00:05:00,460 And remember, when you use a dictionary 109 00:05:00,460 --> 00:05:02,850 the keys become the column names, 110 00:05:02,850 --> 00:05:05,350 and the values associated with those keys 111 00:05:05,350 --> 00:05:08,660 are used to populate the rows within a given column. 112 00:05:08,660 --> 00:05:11,050 So, the company column will have the names 113 00:05:11,050 --> 00:05:13,390 of all the companies that we defined up above. 114 00:05:13,390 --> 00:05:16,090 And the price column will initially have zeros 115 00:05:16,090 --> 00:05:18,630 for each of those companies. 116 00:05:18,630 --> 00:05:21,180 Now, the most important piece in this example 117 00:05:21,180 --> 00:05:24,490 is our subclass of SubscribeCallback. 118 00:05:24,490 --> 00:05:27,440 In that subclass, we've defined an __init__ method, 119 00:05:27,440 --> 00:05:29,210 which is going to receive the data frame 120 00:05:29,210 --> 00:05:31,810 containing the names of the companies 121 00:05:31,810 --> 00:05:33,370 and their current stock prices. 122 00:05:33,370 --> 00:05:36,580 And we'll keep updating that as we receive messages. 123 00:05:36,580 --> 00:05:40,670 And limit=1000, this is the total number of messages 124 00:05:40,670 --> 00:05:44,630 we wish to receive before we unsubscribe from the stream 125 00:05:44,630 --> 00:05:47,430 in this demonstration example. 126 00:05:47,430 --> 00:05:50,350 We store the data frame and keep track of the order count, 127 00:05:50,350 --> 00:05:51,870 because we need to be able to check 128 00:05:51,870 --> 00:05:56,190 whether we've reached the maximum number of orders as well. 129 00:05:56,190 --> 00:05:58,250 Now, we've overwritten two different methods 130 00:05:58,250 --> 00:05:59,790 in our subclass. 131 00:05:59,790 --> 00:06:01,570 One is the status method, 132 00:06:01,570 --> 00:06:03,820 which will receive administrative messages 133 00:06:03,820 --> 00:06:05,840 from the PubNub service. 134 00:06:05,840 --> 00:06:07,470 And we're looking in this example 135 00:06:07,470 --> 00:06:10,210 for two different status messages. 136 00:06:10,210 --> 00:06:12,050 You'll notice that the status object 137 00:06:12,050 --> 00:06:16,830 we receive as a parameter has a category attribute. 138 00:06:16,830 --> 00:06:20,250 And that category attribute will be one of the constants 139 00:06:20,250 --> 00:06:23,050 in the PNStatusCategory type. 140 00:06:23,050 --> 00:06:25,590 So if we get this type of category, 141 00:06:25,590 --> 00:06:28,190 that means we've subscribed to a stream. 142 00:06:28,190 --> 00:06:31,360 And if we get this one it means that we've unsubscribed. 143 00:06:31,360 --> 00:06:33,820 And in each case, we'll display a message 144 00:06:33,820 --> 00:06:35,340 at the command line. 145 00:06:35,340 --> 00:06:38,050 This is the key method for receiving messages 146 00:06:38,050 --> 00:06:39,820 from the live stream itself. 147 00:06:39,820 --> 00:06:43,280 It receives the client object, the pubnub client object, 148 00:06:43,280 --> 00:06:45,980 and the the particular message from the stream. 149 00:06:45,980 --> 00:06:47,810 Within that message parameter 150 00:06:47,810 --> 00:06:49,780 there is a dictionary called message 151 00:06:49,780 --> 00:06:52,680 that has the dictionary representation 152 00:06:52,680 --> 00:06:55,920 of the JSON object that was sent to us. 153 00:06:55,920 --> 00:06:59,140 And within that JSON object there's a symbol key 154 00:06:59,140 --> 00:07:01,910 and a bid_price key, as well as several other keys. 155 00:07:01,910 --> 00:07:04,970 We only need these two in this example. 156 00:07:04,970 --> 00:07:07,290 So, the symbol and bid_price are captured 157 00:07:07,290 --> 00:07:09,170 and then displayed at the command line. 158 00:07:09,170 --> 00:07:12,870 Then we update at the particular row and column 159 00:07:12,870 --> 00:07:15,430 within our data frame, the bid_price. 160 00:07:15,430 --> 00:07:19,080 So, you'll notice we figure out the symbol's row 161 00:07:19,080 --> 00:07:22,210 by passing it to the data frame's index method, 162 00:07:22,210 --> 00:07:25,520 which looks for that symbol in the index, 163 00:07:25,520 --> 00:07:28,070 and then figures out the current row, 164 00:07:28,070 --> 00:07:29,660 or the appropriate row. 165 00:07:29,660 --> 00:07:32,720 And based on that, we update the corresponding column 166 00:07:32,720 --> 00:07:34,340 with that bid_price. 167 00:07:34,340 --> 00:07:36,580 We also add one to the order count. 168 00:07:36,580 --> 00:07:39,330 And if we've reached the maximum number of orders, 169 00:07:39,330 --> 00:07:42,320 we tell the PubNub client to unsubscribe 170 00:07:42,320 --> 00:07:45,830 from all the different streams that we subscribed to. 171 00:07:45,830 --> 00:07:48,590 In this example, there's only one channel 172 00:07:48,590 --> 00:07:52,100 that we will subscribe to, the Market Orders live stream. 173 00:07:52,100 --> 00:07:54,130 So, if we had multiples, 174 00:07:54,130 --> 00:07:56,270 this would unsubscribe from all of them. 175 00:07:56,270 --> 00:07:58,610 And of course, they also give you the ability 176 00:07:58,610 --> 00:08:01,323 to unsubscribe from one as well. 177 00:08:02,330 --> 00:08:03,970 Our update method just defines 178 00:08:03,970 --> 00:08:05,220 what's going to be displayed 179 00:08:05,220 --> 00:08:07,090 in every frame of the animation. 180 00:08:07,090 --> 00:08:10,380 And we've done similar things to this multiple times now. 181 00:08:10,380 --> 00:08:14,060 But just as a reminder, this clears the current bar plot 182 00:08:14,060 --> 00:08:16,810 from the figure object, from the axes. 183 00:08:16,810 --> 00:08:18,960 cla stands for clear axes. 184 00:08:18,960 --> 00:08:21,240 This creates a new bar plot 185 00:08:21,240 --> 00:08:24,040 with the current data from the companies data frame, 186 00:08:24,040 --> 00:08:26,240 using the x-axis to plot the companies, 187 00:08:26,240 --> 00:08:29,010 and the y-axis to plot the prices. 188 00:08:29,010 --> 00:08:31,010 Then we set the x and y labels. 189 00:08:31,010 --> 00:08:32,550 And we use a tight_layout 190 00:08:32,550 --> 00:08:34,920 to make sure that everything fits nicely 191 00:08:34,920 --> 00:08:37,910 within the figure window. 192 00:08:37,910 --> 00:08:41,123 And then finally, we have the main part of our application, 193 00:08:42,090 --> 00:08:46,470 in which we set the background of our seaborn plot. 194 00:08:46,470 --> 00:08:47,760 We create the window 195 00:08:47,760 --> 00:08:50,140 in which the animation will be displayed. 196 00:08:50,140 --> 00:08:51,980 We configure the animation, 197 00:08:51,980 --> 00:08:53,940 which we've done a number of times previously, 198 00:08:53,940 --> 00:08:57,030 but again, remember, it requires the figure object 199 00:08:57,030 --> 00:09:00,200 that specifies where to display the animation frames, 200 00:09:00,200 --> 00:09:01,610 and the method to call 201 00:09:01,610 --> 00:09:04,580 to create those animation frames as well. 202 00:09:04,580 --> 00:09:08,560 This version of plt.show, with the block=False argument, 203 00:09:08,560 --> 00:09:10,460 says to display the window, 204 00:09:10,460 --> 00:09:14,580 but don't block and wait for that window to be closed. 205 00:09:14,580 --> 00:09:18,880 Move on to the next statement in the example, in this case. 206 00:09:18,880 --> 00:09:21,750 And we'll call plt.show again later 207 00:09:21,750 --> 00:09:24,700 to keep the window on the screen. 208 00:09:24,700 --> 00:09:28,210 config, we create the PNConfiguration object. 209 00:09:28,210 --> 00:09:30,550 That's where we store the subscription key. 210 00:09:30,550 --> 00:09:31,780 And in this case, 211 00:09:31,780 --> 00:09:35,060 we did put the subscription key directly into our code, 212 00:09:35,060 --> 00:09:37,180 but that's not normally what we would do. 213 00:09:37,180 --> 00:09:39,740 The only reason we chose to do that here 214 00:09:39,740 --> 00:09:41,860 is because this is a public key 215 00:09:41,860 --> 00:09:44,000 that they publish on their website 216 00:09:44,000 --> 00:09:45,360 for you to play around with 217 00:09:45,360 --> 00:09:48,430 and learn about streaming with PubNub. 218 00:09:48,430 --> 00:09:49,263 We, 219 00:09:49,263 --> 00:09:52,130 can create, excuse me, the PubNub client object, 220 00:09:52,130 --> 00:09:54,150 by passing the configuration 221 00:09:54,150 --> 00:09:57,010 into the constructor for the PubNub client. 222 00:09:57,010 --> 00:09:58,810 Once we have the pubnub object, 223 00:09:58,810 --> 00:10:02,640 we register a listener of our StockListener type, 224 00:10:02,640 --> 00:10:04,900 giving it the data frame that specifies 225 00:10:04,900 --> 00:10:09,140 the location in which we'll update the stock prices. 226 00:10:09,140 --> 00:10:13,320 And separately, the total number of orders to listen to, 227 00:10:13,320 --> 00:10:16,326 and we get that as a command line argument. 228 00:10:16,326 --> 00:10:20,020 And if it's not a correct command line argument, 229 00:10:20,020 --> 00:10:23,120 we'll use the number 1,000 by default. 230 00:10:23,120 --> 00:10:26,630 So, the last step is to actually subscribe to the channel. 231 00:10:26,630 --> 00:10:28,720 And as you can see here, 232 00:10:28,720 --> 00:10:31,930 we call the pubnub client's subscribe method, 233 00:10:31,930 --> 00:10:35,750 and then we access the resulting object's channels method 234 00:10:35,750 --> 00:10:39,610 to specify the name of the channel we wish to subscribe to. 235 00:10:39,610 --> 00:10:42,580 And this, again, was published on the PubNub page. 236 00:10:42,580 --> 00:10:44,170 And then we call execute, 237 00:10:44,170 --> 00:10:46,880 at which point we now subscribe to the channel 238 00:10:46,880 --> 00:10:49,040 and begin receiving messages. 239 00:10:49,040 --> 00:10:53,600 Now, if we did not have this last call to plt.show, 240 00:10:53,600 --> 00:10:55,310 the program would actually terminate 241 00:10:55,310 --> 00:10:57,500 and the window would simply go away. 242 00:10:57,500 --> 00:10:59,760 In this case, calling show with no arguments 243 00:10:59,760 --> 00:11:01,650 keeps that window on the screen, 244 00:11:01,650 --> 00:11:04,753 and that's what enables you to see the animation.