A Darkness at Sethanon concludes Raymond Feist's Riftwar Saga - a trilogy that became a tetralogy when Feist decided to rewrite volume 1, splitting it into 2 books.
An invasion by the dark sorcerer Murmandamus threatens the world of Midkemia and ultimately all other worlds in the Riftwar universe. Murmandamus is determined to capture the city of Sethanon and the powerful lifestone buried beneath it. Our heroes from the previous novels (Pug, Tomas, Arutha, and others) gather the knowledge, weapons, and allies they need to try to defeat Murmandamus.
There are twists. Characters we thought dead are found alive, former foes become allies, and Murmandamus's motivation is not what it seems.
This book is larger in scope than its predecessor as the characters travel across dimensions and even travel backwards in time to the beginning of their universe. The time travel sequence gives the characters and the reader insights into the history of the universe and its many sentient and magical races.
Feist is better at action than at character development. Not that the characters are bad - it's just that he jumps them ahead abruptly, rather than letting us watch them mature and otherwise change. And this book has plenty of action, including several large battle scenes.
I'm glad I stuck with this series, as it got better as it went along.
The .NET framework provides the FtpWebRequest class to assist you in managing FTP commands.
A common use of this class is to upload a file to an FTP site.
To do this in C#, we create an FtpWebRequest object (the WebRequest object has a static factory method to do this) on the object, set our intended method, and specify the FTP site's login credentials, as shown below.
A T4 ("Text Template Transformation Toolkit") template is a text generation tool that mixes raw text and code to generate text as output.
In my last article, I showed how to create a Runtime Text Template and generate output from it via the following lines of C# code:
var tt = new myT4Template();
string outputText = tt.TransformText();
But sometimes, it is helpful to pass parameters from your C# code and use them in your template.
The steps to do this are:
Add a T4 Runtime Text Template to your Visual Studio project In the T4 template:
Add one or more parameter directives
Use the parameters in your template
In your C# code
Instantiate your T4 template object
Set the Session property to a new Dictionary
Pass the parameters in the Session object
Initialize the T4 template object
Generate the output text with the T4 template object's TransformText() method.
An example will help illustrate this.
I have a T4 Runtime Text Template named "ParamTemplate1.tt" and I want to add 2 parameters: firstName and lastName (both strings) and use them in my template.
At the top of the template file, add the following directive:
Notice that we included the Namespace in the parameters' data types.
Now, use the parameters in your template, as in the following code:
Hello <#=firstName #> <#=lastName #>!
In your C# program, add the following code
var pt1 = new ParamTemplate1();
pt1.Session = new Dictionary<string, object>();
pt1.Session["firstName"] = "David";
pt1.Session["lastName"] = "Giard";
pt1.Initialize();
var outputText1 = pt1.TransformText();
This passes the values "David" and "Giard" to the firstName and lastName parameters, respectively.
We can even pass in a custom data type, based on a type we create.
Imagine I have created a Person class with a FirstName and a LastName properties (both strings) and I want to pass a parameter of this type into my T4 template named "ParamTemplate2.tt" and use that parameter inside the template.
var pt2 = new ParamTemplate2();
pt2.Session = new Dictionary<string, object>();
var person = new Person()
{
FirstName = "Satya",
LastName = "Nadella"
};
pt2.Session["person"] = person;
pt2.Initialize();
var outputText2 = pt2.TransformText();
A Text Template Transformation Toolkit (or "T4") template is a text generation tool that mixes raw text and code to generate text as output.
Visual Studio contains tools to assist with the creation and execution of T4 templates.
To add a T4 template to your project, right-click on the project, and select Add | New Item from the context menu.
In the "Add New Item" dialog, the T4 templates are listed under the General category; but you can find them quickly by typing "T4" in the "Search Installed Templates" textbox.
There are two kinds of T4 templates: a Text Template and a Runtime Text Template. Each of these starts with a file with ".tt" extension, but is designed to generate at least one other file.
The Text Template regenerates the final output file every time you save the ".tt" file. This is a quick way to generate simple files, but it lacks flexibility.
The Runtime Text Template generates a VB or C# class that you can call from your code to generate the output file. We will focus on the Runtime Text Template in this article.
A Runtime Text Template consists of raw text (which will be exactly duplicated in the output file) and executable code, which the template will run. Any output of that code will be included in the output file. Code is delimited from the raw text by placing it between the <# and #> symbols. If you just want to output the results of an expression, you can surround that expression with the <#= and #> symbols.
For example, to output the current date and time, include the following line in your template.
<#=System.DateTime.Now #>
You can combine this with text as in the following example:
(This file was generated at <#=System.DateTime.Now #>!)
which would output something like the following:
(This file was generated at 06/25/2018 20:33:14!)
You can include more complex code between the <# and #> symbols, as in the following example:
<#for (System.Int32 I = 0; I < 5; I++)
{
// WriteLine is a utility function that outputs text.
WriteLine(I);
}
#>
The result of the above code is:
0
1
2
3
4
When you save a Runtime Text Template, Visual Studio generates code for a public .NET class with the same name as your tt file. You can instantiate this class as you would any other public class and call the TransformText() method to generate the output as a string.
For example, if I created a Runtime Text Template named "myT4Template.tt", the following code would generate the output and save it to a variable named "outputText".
var tt = new myT4Template();
string outputText = tt.TransformText();
T4 templates give you the flexibility to generate text and text files quickly and the flexibility to change the template without worrying about the associated data.
MySQL is a popular relational database. Although the product is owned by Oracle, which offers several versions for purchase, it is an open source project and they offer a free "Community Edition.
There are several ways to install and start running MySQL.
You can download a copy from https://www.mysql.com/downloads/. The community edition offers install files for various platforms (an MSI file for Windows) that allows you install the database engine locally.
My preferred option is to install a database on Azure and store my data in the cloud. Because Azure offers MySQL as a service, I don't need to create a Virtual Machine or install the database engine anywhere. To create a MySQL service is Azure, navigate to the Azure portal and select Create a Resource | Databases | Azure Database for MySQL; then complete the blade that displays.
Whether using a cloud service or installing locally, you will be prompted to create an admin user and password. Remember these.
Once you have access to MySQL, you can use the command line to create and manage databases. On a local machine, open a command prompt; in Azure, you can click the "Cloud Shell" button at the top of the Azure portal.
To begin working with MySQL, you need to connect to the database engine with the following command:
mysql -h hostname -u username -p
where hostname is the name of the MySQL instance and username is the name of the admin user.
For example, if I have an Azure MySQL instance named "dgmysql" and a user named "dgiard", I would type the following:
mysql -h dgmysql.mysql.database.azure.com -u dgiard@dgmysql -p
When prompted, enter the appropriate password for the user.
The following commands are usfule for working with databases.
# Create a new database
CREATE DATABASE databasename;
# List all databases available
SHOW DATABASES;
# Switch to a specific database
USE publicspeaking;
Now you can start using standard SQL commands to work with database objects and data in the current database.
For example,
# Create a new database
CREATE DATABASE databasename;
# List all databases available
SHOW DATABASES;
# Switch to a specific database
USE publicspeaking;
For the most part, MySQL follows ANSI SQL syntax.
If you are familiar with another relational database, such as Microsoft SQL Server, working with MySQL will feel natural.
On May 19, I delivered a presentation titled "How Cloud Computing Empowers a Data Scientist" at the Chicago AI & Data Science Conference.
I described ways that the cloud has accelerated the fields of data science, machine learning, and artificial intelligence; and I gave examples of Azure tools that facilitate development in these fields.
Often, I need to restart an ASP.NET web site. There are many reasons but the most common reason (for me) is that I have updated a page and ASP.NET is displaying a cached version of that page, which does not reflect my changes.
There are multiple ways to restart an ASP.NET site; but, I always found it easiest to copy any file into the site's "bin" folder. In fact, I typically keep an empty text file named "_a.txt" in my local Documents folder for just this purpose. It is a bit of a hassle to open my FTP client (I use Filezilla), just to copy 1 file; so I recently decided to automate this process.
The Windows command prompt has an "FTP" command that that takes a "-s" argument with which you can provide a file containing FTP commands.
So I created the following text file named "RestartBlog.ftp"
open blahblahblah.ftp.azurewebsites.windows.netmyusernamemypassword
put _a.txt /site/wwwroot/bin/_a.txt
close
quit
In my file, I replaced blahblahblah.ftp.azurewebsites.windows.net with the FTP Host Name of my site, myusername with the FTP User Name of my site, and mypassword with the password associated with that username.
This opens a connection to my FTP site, logsin in and copies a local file named "_a.txt" to the site's bin folder. It's possible that your site's "bin" folder is in a different path, so you may need to change this. Next, it closes the connection and exits FTP mode.
After saving this file, I created another file in the same folder containing the following text:
echo ".">_a.txt
ftp -s:RestartBlog.ftp
del _a.txt
pause
This creates a small text file named "_a.txt"; then calls my file with FTP commands. After executing the FTP commands, this batch deletes the text file it created.
I added the "pause" at the end so that the command window would not immediately close and I could view the results and any errors that occurred. This is optional.
Parsing information from a web page is not a trivial task. Fortunately, HTML has a defined structure and libraries exist to help us navigate that structure.
You can add this library to a project via NuGet. Simply right-click your project in the Visual Studio Solution Explorer, select "Manage NuGet Packages" (Fig. 1); then search for "HTML Agility Pack" and install the package (Fig. 2).
Fig. 1
Fig. 2
Once the package is installed, you can load your document into an HtmlAgilityPack.HtmlDocument and begin working with it.
There are 3 ways to load a web page into an HtmlDocument: from a file on disk; from a string of HTML, from a URL, and from whatever document is loaded in a browser.
Below are examples of each (taken from the HAP web site).
// From Filevar doc = new HtmlDocument();
doc.Load(filePath);
// From Stringvar doc = new HtmlDocument();
doc.LoadHtml(html);
// From Webvar url = "http://html-agility-pack.net/";
var web = new HtmlWeb();
var doc = web.Load(url);
// From Browservar web1 = new HtmlWeb();
var doc1 = web1.LoadFromBrowser(url, o =>
{
var webBrowser = (WebBrowser) o;
// WAIT until the dynamic text is setreturn !string.IsNullOrEmpty(webBrowser.Document.GetElementById("uiDynamicText").InnerText);
});
Listing 1
I am interested in this project because I have a web page that lists all my public presentations and I want to make this page data driven, so I won't have to update a text file every time I schedule a new presentation.
To populate a database with this information, I could either type in every presentation or grab it from my web page and parse out the relevant information in an HTML table. Since I have delivered nearly 500 presentations, re-typing each of these individually seemed like way too much work.
Here is a screenshot of my "Public Presentations" page:
Fig. 3
and here is a partial listing of the source HTML for that page.
id="talklist">
style="height:15.0pt">
style="height:15.0pt;width:62pt"width="83">
Date
width="458">
Topic
width="311">
Event
width="64">
Location
style="height:15.0pt">
Sep 20, 2018
Adding Image and Voice Intelligence to Your Apps with Microsoft Cognitive Services
VSLive
Chicago, IL
style="height:15.0pt">
Sep 20, 2018
Effective Data Visualization
VSLive
Chicago, IL
style="height:15.0pt">
Jun 22, 2018
Effective Data Visualization
Beer City Code
Grand Rapids, MI
Listing 2
I used the following code to load the data into an HtmlDocument:
var url = "/Schedule.aspx";
Console.WriteLine("Getting data from {0}...", url);
var web = new HtmlWeb();
var doc = web.Load(url);
Listing 3
The HtmlDocument has a DocumentNode property, which returns the root element of the HTML document as a node. Most of the time, I found myself working with nodes and collections of nodes. Each node has a SelectSingleNode and a SelectNodes method which returns a node and a collection of nodes, respectively. These take an XPATH argument, with which I was familiar from my days working with XML documents.
The following code retrieves a nodelist of all the
row nodes within the "talklist" table, shown in Listing 2.
var documentNode = doc.DocumentNode;
var tableNode = documentNode
.SelectSingleNode("//table[@id='talklist']");
var rowsNodesList = tableNode.SelectNodes("tr");
Listing 4
Finally, because each
node contains 4 nodes of
cells, I can iterate through each node, find all the cells, and the innertext of each cell. For good measure, I stripped off any non-printing characters. Because the title row contains
cells, instead of
cells, I want to check for this before extracting information.
The code for this is in Listing 5.
var rowCount = 1;
foreach (var row in rowsNodesList)
{
var cells = row.SelectNodes("td");
if (cells != null && cells.Count > 0)
{
var date = cells[0].InnerText;
date = date.Replace("\r\n", "").Trim();
var topic = cells[1].InnerText;
topic = topic.Replace("\r\n", "").Trim();
var eventName = cells[2].InnerText;
eventName = eventName.Replace("\r\n", "").Trim();
var location = cells[3].InnerText;
location = location.Replace("\r\n", "").Trim();
Console.WriteLine("Row: {0}", rowCount);
Console.WriteLine("Date: {0}", date);
Console.WriteLine("Topic: {0}", topic);
Console.WriteLine("Event: {0}", eventName);
Console.WriteLine("Location: {0}", location);
Console.WriteLine("--------------------");
rowCount++;
}
}
Listing 5
Here is the full code listing for my console app that retrieves the text of each cell.
usingHtmlAgilityPack;
usingSystem;
namespaceTestHAP
{
classProgram
{
staticvoidMain(string[] args)
{
var url = "/Schedule.aspx";
Console.WriteLine("Getting data from {0}...", url);
var web = new HtmlWeb();
var doc = web.Load(url);
var documentNode = doc.DocumentNode;
var tableNode = documentNode
.SelectSingleNode("//table[@id='talklist']");
var rowsNodesList = tableNode.SelectNodes("tr");
var rowCount = 1;
foreach (var row in rowsNodesList)
{
var cells = row.SelectNodes("td");
if (cells != null && cells.Count > 0)
{
var date = cells[0].InnerText;
date = date.Replace("\r\n", "").Trim();
var topic = cells[1].InnerText;
topic = topic.Replace("\r\n", "").Trim();
var eventName = cells[2].InnerText;
eventName = eventName.Replace("\r\n", "").Trim();
var location = cells[3].InnerText;
location = location.Replace("\r\n", "").Trim();
Console.WriteLine("Row: {0}", rowCount);
Console.WriteLine("Date: {0}", date);
Console.WriteLine("Topic: {0}", topic);
Console.WriteLine("Event: {0}", eventName);
Console.WriteLine("Location: {0}", location);
Console.WriteLine("--------------------");
rowCount++;
}
}
Console.ReadLine();
}
}
}
Although I don’t intend to use it for this, HAP also supports modifying the HTML you select with node methods like AppendChild(), InsertAfter(), and RemoveChild().
This tool will help me to retrieve and parse the hundreds of rows of data from my web page and insert them into a database.
Years ago, I created a list of my public speaking. In order to make it more readable, I added a bit of jQuery to style alternating rows with a light gray color. I used jQuery because, at that time, CSS did not support the ability to apply a style to every other row in a table.
I liked the results, but the code always struck me as awkward.
Eventually, CSS3 provided this ability, but not every browser immediately supported this style. So I waited. And then, I forgot about it. It was slow, but it worked.
It has been a few years and I had a bit of time, so today I removed the jQuery reference and JavaScript from this page and achieved the same results using the CSS
Here is the table opening tag. I applied a style to it only to make it easy to find, in case there are other tables on the same page.
class="tiger-stripe">
Each row has no background-color styling, because I applied that later. Here is a sample row.
style="height:15.0pt">
Jun 11, 2018
Building and Training your own Custom Image Recognition AI
Norwegian Developers Conference
Oslo, Norway
Originally, I had all this within the page's
:
I was able to accomplish the same results using the "even" argument of the "nth-child" selector. I replaced the code within the
above with the following:
I decided to replace the table’s class (“tiger-stripe”) with an id (“presentationlist”), since the class only exists to identify this table. Strictly speaking, this was not necessary, but it felt cleaner this way. Here is the new table tag.
id="presentationList">
Each row has no background-color styling, because I applied that later. Here is a sample row.
By eliminating the custom JavaScript and the jQuery reference, the code is much simpler and the page loads faster. I did not need to modify any HTML. CSS selectors take care of all the styling.
This is an example of using declarative programming, instead of imperative programming to accomplish a task. In other words, the new system tells the browser the results I want without explicitly specifying how I want it done.
Most of the Norway I saw is defined by towering cliffs, the result of glaciers gouging their way through the country thousands of years ago. Many of these filled with water to became the famous fjords of Norway.
This was the Norway I experienced when I made my first trip here this week.
I was invited to speak at the The Norwegian Developer Conference (“NDC”), so I flew to Oslo after speaking at IT Camp in Romania. I arrived in Oslo on a rainy Sunday night after a week in Romania.
The first 2 days in Oslo, I worked on a Machine Learning project for Bane Nor - the Norwegian national railway. This was a great experience for me, as Microsoft flew in engineers from all over the world and I had an experience to learn about the train industry from the customer and about data science from several experts.
NDC began on Wednesday, so I arrived bright and early to experience it all. The conference was amazing. Hundreds of speakers from all over the world come to Oslo each year for some high-quality sessions. I knew some of the speakers and I had the opportunity to meet many more.
Wednesday evening, the conference organizers treated all the speakers to a boat ride around the islands near Oslo, which was a great chance to meet new people.
My presentation - Building and Training your own Custom Image Recognition AI - was the last one of the day. I was happy to get it over with on Day 1, but I spent much of Wednesday preparing for it. In the end, it went very well. The bright stage lights prevented me from seeing the audience, but I received several good questions afterward, so I think the audience enjoyed it as much as I did.
The day after the conference, I booked a trip to Bergen. Oslo is near the eastern border of Norway and Bergen is on the west coast, so this all-day trip took me across the entire country. It consisted of 3 trains, a bus, and a boat. The boat ride was the most impressive as we traveled through the fjords of central Norway. The trip was designed to be more scenic than efficient and it took us from Oslo to Myrdal to Flåm to Gudvangen to Voss to Bergen. The fjord boat cruise took me to the northernmost point I have ever been, edging out my trip to Upsala, Sweden 3 years ago.
I only had one day in Bergen and I was exhausted from 2 weeks on the road. But I did a lot of walking around the city, and visited 2 art museums, and drank some local beers, and ate reindeer stew and whale steak, and sat by the harbor to watch the sun set at midnight. Scandinavian daylight last for over 20 hours this time of year, making it very difficult for me to pace myself.
I missed Father's Day in America (most European countries celebrate in March), but I will make time with my boys in the next few weeks.
In a few hours, I fly home, tired but content from 2 weeks abroad traveling thousands of miles. I feel like I need to return to Norway and see all the places I missed. Hopefully, NDC will make that happen next year.
There exists a competition in which a speaker must deliver a presentation in front of a slide deck that advances automatically every few seconds. The biggest challenge is knowing when the slide will change. Because most of us are not capable of simultaneously counting time and speaking, these talks often feature either awkward pauses waiting for the next slide or a rush to finish talking about a slide that disappeared a few seconds ago.
Prior to such a competition, one of the speakers asked me how to create a bar that would display across the bottom and gradually disappear as time expired. Here is how to do this:
Step 1: Create your slide
In PowerPoint, create your slides as you like each slide and set them to advance automatically. Fig. 1 shows an example of such a slide.
Fig. 1
Step 2: Draw a rectangle
Select one of your slides and insert a short wide rectangle shape at the bottom. From the Insert ribbon, select Shapes and click the rectangle shape, as shown in Fig. 1; then, drag your mouse along the bottom of the slide to draw the rectangle, as shown in Fig. 2. Make the rectangle exactly as wide as the slide.
Fig. 2
Fig. 3
Step 3: Animate the rectangle
Select the rectangle shape you just added. From the Animations ribbon, expand the list of animations and select "Wipe" from the "Exit" section, as shown in Fig. 4.
Fig. 4
By default, the "Wipe" animation will wipe the shape from the bottom. You want to wipe it from the right. Select the shape and, from the Animations ribbon, select Effect Options | From Right, as shown in Fig. 5.
Fig. 5
Finally, set the timing of the animation. In the "Timing" section of the Animations ribbon, set the following: Start: With Previous Duration: Set to the same duration as the slide timing Delay: 0
These are shown in Fig. 6.
Fig. 6
Step 4: Test your Transition
Press SHIFT+F5 to run this slide with the transition. You should see the rectangle slowly disappear from the right and completely disappear as the slide transitions to the next slide. Figures 7a, 7b, and 7c illustrate this.
Fig. 7a
Fig. 7b
Fig. 7c
Step 5: Copy Shape to Other Slides
When you are satisfied that the animation is working properly, copy / paste this shape to your other slides. The animations will copy along with the shape.
Achievement unlocked: I played Dungeons and Dragons last night for the first time in my life. And I did it in Transylvania!
I am writing this while sitting in the Cluj-Napoca airport, waiting for my flight to Bucharest and my connection to Oslo this evening.
This is the fifth consecutive year I have visited Romania to attend IT Camp. I enjoy it more each time I come - the conference, the people, and the country.
IT camp has become like a family reunion for me. I look forward to seeing old friends from Romania and from Europe and America. Most of them I only get to see once a year, so it is a real treat for me to come here. And, as always, the Hotel Grand Italia spoils me with their excellent service.
The conference continues to grow. Attendance was 500-600 this year (about 10% more than last year) and the speaker list grew to over 40. Session times were shortened to 45 minutes this year in order to accommodate the larger number of sessions. I delivered 2 presentations: "Own Your Own Career – Advice from a Veteran Consultant" and "How Cloud Computing Empowers a Data Scientist". I had a packed room for the first session, with many people standing in the back. A number of people approached me during the conference to ask more questions about my topics and to tell me they enjoyed my talks, which is always a treat.
In between sessions, I met new people, re-connected with old friends, recorded 4 interviews, learned a few things, and Tudy taught me how to play Dungeons and Dragons, even though I was so tired I nearly fell asleep an hour into the game.
Some inclement weather and a need to prepare my presentations kept me close to the hotel during the conference; but IT Camp always includes a field trip the day after the conference. This year, they took us to Sighișoara, a small city in central Romania most famous as the alleged birthplace of Vlad the Impaler, the inspiration for Count Dracula.
I am grateful to Mihai and Diana and Tudor and Noemi and the many volunteers who work hard to make IT Camp a success and to make me feel welcome. I got a lot out of this trip.
And how many people can say their first game of Dungeons and Dragons took place in Transylvania?
Silverthorn by Raymond Feist continues the Riftwar Saga, begun in Magician: Apprentice and Magician: Master.
The interplanetary war has ended, the rift between the world has closed, and Lyam has been named king.
But evil forces still lurk on Midkemia and an assassination attempt on Anita, the betrothed of Lyam's brother Arutha leaves her dying from a poison-dipped crossbow bolt. Most of this book follows Arutha and his companions in their quest to find the rare silverthorn plant, which promises to provide an antidote for the poison that is slowly killing Anita.
Pug, the title character of the first two novels is relegated to a minor role until the last few chapters, when he returns to Kelewan to seek a way to battle an evil magician hoping to take over and destroy Midkemia. The journey is perilous because Pug is an outlaw in Kelewan, thanks to his disruption of the High Magicians and a powerful warlord.
Feist does a good job of keeping the action flowing and the reader engaged. This book's story is more straightforward than that of its 2-volume predecessor. It does not suffer from the weakness of the earlier volumes - having characters summarize certain events, rather than letting the reader experience them directly. We follow the characters as the action unfolds.
Silverthorn stands well on its own; but is best enjoyed as part of the Riftwar Saga. I have already begun the next book in the series.
A warning: You are much more likely to enjoy Raymond Feist's "Magician: Master", if you first read his novel "Magician: Apprentice". This story was originally published as a single volume, titled simply "Magician"; but after the success of this novel, Feist convinced his publisher to allow him to expand the story into 2 books and release those separately. As a result, neither book stands well on its own, but together they form a solid story.
"Magician: Master" continues the story of Pug, who was captured by the Tsurani of Kelewan during the Riftwar and sold into slavery before rising to power via his magical abilities. He ultimately returns to his home world of Midkemia in attempt to gain a truce; but he is confounded by evil forces and by a senile king.
This book and its predecessor hold up well as a high fantasy novel. Numerous characters are developed and cross paths through multiple subplots.
The greatest weakness of the story is Feist's tendency to skip significant events, then relay them later in retrospect. A character dies, but we don't hear of it until his girlfriend relays the news; Pug transforms into a master magician, but we have very little insight into how this happens; he goes away and somehow is more powerful than his teachers when he returns. Showing the reader these actions would almost certainly have more impact than telling us about their results.
Despite its weaknesses, I enjoyed the 2-volume story and I have already begun reading the next book in the series.
I have twice begun a screencast web series; but each time it fizzled as I ran out of time, motivation, and/or subject matter.
This time, I'm optimistic and I have a lot to say.
So, starting tomorrow, I am re-booting "GCast", a technical screencast in which I will walk through a technical topic in a short video.
My new computer and a fresh install of Camtasia Studio and Adobe Premiere Elements will help.
I’ve recorded a few episodes and I’ll be releasing them here once a week, for now. The first 2 are about 20 minutes long, but I plan to keep most videos 5-10 minutes.
Tomorrow's episode is A Lap Around Microsoft Azure and talks about the advantages of cloud computing in general and Azure in particular.
"he Hollow Hills continues Mary Stewart's Arthurian Saga, begun with Merlin's early years in The Crystal Cave.
In this second volume, King Uther, hoping to avoid the shame of conceiving Arthur via Ygraine on the night her husband was killed, commits the infant to the care of Merlin. Merlin keeps the boy safe until it is time for him to become king.
Merlin places the future monarch in the care of trusted friends; then, travels across Europe, where he learns of the sword of King Maximus - a sword prophesied to belong to the rightful ruler of Britain. Merlin then begins his quest to find this sword and get it to Arthur.
In some ways, The Hollow Hills is even stronger than The Crystal Cave. It continues its focus on Merlin, but it is Merlin the man, cognizant of his destiny and responsibility that we see, and his story is compelling.
I love the very-human Merlin that Stewart creates. His only "magic" is his advanced education and his ability to (sometimes) see the future and the distant present; but that is enough to grow his legend as a powerful wizard.
I love the way Stewart brings to life the legend in a very real sense. Merlin seeks and discovers the legendary sword of a long dead king and leaves it where Arthur can find it, cementing the boy's claim to the crown of his father.
The story follows Merlin from Arthur's birth to his ascension to the throne after Uther's death. Because Arthur's story is so well known, there are few surprises; but Stewart's prose and her gift of building characters makes this an enjoyable read.
6/3 Today I am grateful for a walk along the Fox River in St. Charles yesterday
6/2 Today I am grateful to get my photos and videos transferred to and organized on my new computer.
6/1 Today I am grateful for the NBA Finals and the NHL Finals
5/30 Today I am grateful for a hot bath last night.
5/30 Today I am grateful for Taco Tuesday.
5/29 Today I am grateful for a walk around Irving Park last night.
5/28 Today I am grateful for a boat ride around Vancouver, BC yesterday
5/27 Today I am grateful to participate in a successful Imagine Cup Finals yesterday.
5/26 Today I a m grateful to make it safely to Vancouver after a full day of travel yesterday.
5/25 Today I am grateful to spend time this week with my boys while Nick was in town.
5/24 Today I am grateful to Nick for helping me set up my TV and DVD player yesterday.
5/23 Today I am grateful to watch the Cubs-Indians game at Wrigley Field last night.
5/22 Today I am grateful for online training courses
5/21 Today I am grateful to speak at the Chicago Data Science conference this weekend at the University of Chicago
5/20 Today I am grateful for an afternoon in Kenosha, WI
5/19 Today I am grateful for a long walk along the river in Chinatown last night.
5/18 Today I am grateful for -Dinner with Carol last night -Seeing the Chicago Symphony Orchestra for the first time
5/17 Today I am grateful for the Marvel Cinematic Universe
5/16 Today I am grateful for my first Personal Training session in months.
5/15 Today I am grateful to watch a series of storms yesterday through a large window from the shelter of my living room.
5/14 Today I am grateful to celebrate Mother's Day with someone else's mother.
5/13 Today I am grateful to see Freddy Cole at the Jazz Showcase last night.
5/12 Today I am grateful to attend and present at my first Toastmasters meeting in months.
5/11 Today I am grateful to finally catch up on my sleep.
5/10 Today I am grateful for all these free drinks and food that Starbucks now owes me.
5/9 Today I am grateful for: -Dinner with Tim last night -5 days in California
5/8 David Giard updated his status. Today I am grateful for: -The hospitality and generosity of Greg and Sharon and their family -Lunch with Gail yesterday -An afternoon with my Uncle Bill and Aunt Jean -A drive to the Pacific Ocean at Point Reyes National Seashore
5/7 Today I am grateful to Kim for a ride to San Rafael yesterday.
After creating an Azure Virtual Machine (VM), you may wish to add one or more disks. Maybe you want to increase your VM's storage; capacity or maybe you want to separate some data from the operating system disk. Either way, open your Virtual Machine properties in the Azure Portal and select "Disks", as shown in Fig. 1.
Fig. 1
The Disks blade displays, as shown in Fig. 2.
Fig. 2
Under the "Data Disks" section, click the "Name" dropdown of the first empty line; and select "Create Disk", as shown in Fig. 3.
Fig. 3
The "Create Managed Disk" blade displays (Fig. 4).
Fig. 4
Provide a disk name, resource group, region, type, and size. I generally prefer to create a disk in the same resource group and region as the VM. You can create a new empty disk, or one based on an existing snapshot.
Click the "Create" button to begin creating the disk, which will take a few minutes.
When finished, you should see your new disk listed in the VMs' Disks blade, as shown in Fig. 5.
Fig. 5
Repeat this process to add any more disks you like.
When you connect to the VM (via Remote Desktop for a Windows VM and via SSH for a Linux VM), you will see the extra disk and you will be able to read to and write from this disk.
Pug is an orphan on the Midkemia world. He is apprenticed to the wizard Kulgan, learns a bit of magic, saves a princess, and is granted a lordship. Then a war breaks out between the people of his homeworld of Midkemia and the Tsurani of Kelewan, who travel to his world through a "rift" in space.
"Magician: Apprentice" Tells the story of Pug, of his friend Tomas, who receives a magic gift from a dying dragon, and of the war between the invading Tsurani and the people of Midkemia. The stories are straightforward tales of high fantasy, in the tradition of Lord of the Rings, but simpler. Major themes are Pug's coming of age and the contrasting cultures of the two warring societies.
I enjoyed reading it, but found the book unsatisfying by itself. This book and "Magician: Master" were originally published as a single volume titled simply "Magician". Following the success of "Magician", Feist expanded the story and released it as two separate novels. But it the two together form a single story and Book 1 does not hold up well on its own. To appreciate the story, you must read both volumes.
Still, it is a good start to a good story and it is well told.
Creating a virtual machine (VM) may be the simplest thing to do in Azure. Azure VMs are very similar to on-premises VMs; in fact, they are built using the same technology - Microsoft Hyper-V.
You can create an Azure VM with just about any operating system, software, and data that you want. Hundreds of templates are available, pre-installed with a variety of configurations.
Azure supports Windows machines and Linux machines. (Did you know that about a third of the VMs in Azure are running Linux?)
The first step in creating an Azure VM is the same step when we create any Azure resource - Navigate to the Azure Portal, log in, and click the [Create a resource] button (Fig. 1) at the top left of the portal.
Fig. 1
From the list of resource categories, select the "Compute" category; then select the template on which you want to base your VM, as shown in Fig. 2.
Fig. 2
If you don't see the VM you want, you can search for it by typing a partial description in the search box. This will display matching templates from which you can select, as shown in Fig. 3.
Fig. 3
After selecting a template, a blade with a brief description displays. Fig. 4 shows the blade for an Ubuntu server. Click the [Create] button at the bottom of this blade.
Fig. 4
The "Create virtual machine" dialog displays with the first tab - "Configure basic settings" - active, as shown in Fig. 5. Complete the forms on this tab.
Fig. 5
At the name field, enter a unique name for your VM. Choose one that will make it easy for you to identify it when you return to the portal to manage this VM.
At the "VM disk type" dropdown, select "SSD" for a Solid State Drive or "HDD" for a standard hard drive.
At the "User name" field, enter a name for the local administrator account on this VM.
At "Authentication type", select either "SSH public key" or "Password" to specify how you want to log into this VM. I have selected "Password", which causes the "Password" fields to appear, requiring me to enter a password twice. Passwords must meet complexity requirements, including a length of at least of at least 12 characters and inclusion of numbers, alpha characters, and special characters.
If you have an Azure AD configured, you can configure your VM to use this for authentication by selecting "Enabled" under "Login with Azure Directory".
You will likely have only one Azure subscription; but if you have more than one, you can select the subscription with which to associate this VM.
At the "Resource group" fields, select an existing resource group or create a new one. Resource groups are a logical grouping of resources that you want to manage together. If there are existing Azure resources that will interact with this VM, you may want to place them all in the same Resource Group.
At the "Location" dropdown, select the region in which you want to create this VM. Location considerations include proximity to the people managing this VM; proximity to people using this VM; and proximity to any other resources that will interact with this VM.
When the tab is complete, click the [OK] button at the bottom.
The second tab - "Size" - will display, as shown in Fig. 6.
Fig. 6
On the "Size" tab, select the size machine you want to create. This is a long list, but you can filter or search to make it more manageable. Note that not every size is available in every region.
After highlighting the desired size, click the [Select] button at the bottom of the tab.
The "Settings" tab will display as shown in Fig. 7.
Fig. 7
Azure provides default values for every setting on this tab, so it is not necessary to change anything. However, you can configure your VM to connect to a specific virtual network or subnet or assign a specific IP address or change any of the settings you like.
When you are satisfied with the settings, click the [OK] button at the bottom of the tab.
The "Summary" tab displays what you have configured, in case you want to change anything at the last minute. It also validates that all your selections are complete and consistent. If the validation lists any problems, you should go back and correct these.
After validation passes and you are satisfied with your selection, click the [Create] button at the bottom of the blade.
After a few minutes, Azure will create and deploy the VM based on your configuration.
Once it is created, it is your VM and you can connect to it as you would any local VM. Use Remote Desktop to connect to a Windows VM or use SSH to connect to a Linux VM.
Two weeks ago, I attended my first Imagine Cup event - the US Finals in San Francisco. Yesterday, I attended my second.
The Canadian Imagine Cup Finals took place in Vancouver, BC. Six finalists competed for the right to advance to the International Finals in Redmond in July.
This event was smaller and shorter than the corresponding US event; but equal in energy. Every one of the teams showed great creativity, strong technical skills, and impressive presentations.
I was excited that all 6 finalists came from schools with which I work. First place went to SmartArm - a project that provides affordable prosthetic arms for amputees - a team I helped mentor at the UTHacks hackathon in January in Toronto.
I'm getting used to these Imagine Cup projects and competitions and I'm looking forward to the world finals.
One of the challenges of working with data is what to do with missing data.
A missing column in a dataset can be, but is not limited to the following:
No text or numbers between 2 column delimiters
An empty string ("")
A blank string (e.g., " ")
The number 0
A special indicator, such as "NA" or "NONE"
An inconsistent data type, such as a number where a string is expected
A value that makes no sense in the context of the data.
The last one requires some domain knowledge about the data, so it is often difficult to spot.
There are two strategies for dealing with missing data
Delete or ignore the entire row
Replace the column with a reasonable value.
If only a few rows contain missing data, it may be efficient to simply delete these rows.
But if many rows contain missing data, it probably makes sense to keep them as other columns may contain valuable information. In this case, we will want to replace the missing data with a reasonable value.
But what is a reasonable value?
Options include replacing the column with an average value, such as the mean or median of the non-missing values. Of course, this is only valid for numeric data that is ordinal, that is data in which higher numbers indicate a higher value and not simply a discrete category.
The Pandas library contains some simple functions for deleting rows and replacing values. The fillna function is the simplest way to do this.
# Replace all missing values with 0
df.fillna(0)
# Replace all missing values with the string 'Missing'
df.fillna('Missing')
You can delete invalid or missing rows by overwriting a dataset with a filtered version of that set, as in the following examples
# Delete all rows with area = 0
df = df[df.area !=0]
# Delete all rows with null area
df = df[df.area.notnull()]
But for values that are not missing, but are inappropriate (e.g., using 0 to represent a missing data point, when 0 could be a valid measurement), we can use the map function.
Below, we use the map function to want to replace any value in the 'area' column that has a value of 0 with the mean value for this column.
# Replace 0 area with the mean
mean_area = df['area'].mean()
df['area'] = df['area'].map({0: mean_area})
In this article, we discussed ways to use Pandas handle missing data in a dataframe.
I was working with a dataset in Azure ML Studio and I needed to replace values in a column.
Reasons for replacing value include:
Replacing codes with a more readable word or words
Consistency when combining 2 sets of data
Converting to numeric values in order to assign values to discrete strings
Converting to numeric values in order to work with an algorithm that only accepts numeric values
There is no built-in shape to do this, but you can do so with a couple lines of code.
I can demonstrate by creating a new ML studio experiment and dragging the "Automobile Price Data" sample dataset onto the experiment design surface, as shown in Fig. 1.
Fig. 1
If we click on this shape and select "Visualize" (Fig. 2), we can see the data in the dataset. Click the "drive-wheels" column to see details about the data in that column (Fig. 3).
Fig. 2
Fig. 3
You can see from the visualization that the "drive-wheels" column contains 3 distinct values: "fwd", "rwd", "4wd" Imagine I wanted to replace these with "FRONT", "REAR", and "FOUR", respectively. (Maybe to be consistent with a second dataset I plan to merge with this one.)
Drag an "Execute Python Script" shape to the Experiment and connect its input to the output of the data shape (Fig. 4).
Fig. 4
In the Properties of the "Execute Python Script" shape, replace the existing code with the following:
The azureml_main function is required by ML Studio. It accepts one parameter - a dataframe, which we name “dataframe1”
The first line of code maps the 3 existing drive-wheels values to 3 new values for every row and saves these 3 new values back to the dataset. By returning that dataframe, we make this updated data the output of this shape, so it can be used by later steps in our experiment.
After running this experiment, we can click the script shape and Visualize the output and see that each value in "drive-wheels" has been replaced, as shown in Fig 5.
Fig. 5
This article shows a simple way to replace values in a dataframe column with new values in Azure ML Studio.
Many Machine Learning solutions have the same steps in common. For example, you will need to retrieve data from one or more sources; you will want to split your data into training and testing subsets; and you will want to clean up your source data. I refer to these as "plumbing tasks” because they are common to so many projects; and spending time coding these tasks takes time away from working on your data and your solution.
Azure Machine Learning Studio can help.
Azure Machine Learning Studio or "ML Studio" is a graphical design tool for building machine learning solutions. It includes a design surface and a set of shapes to perform specific tasks.
To work with ML Studio, drag a shape onto the design surface, set some properties, and connect the inputs and/or outputs to other shapes to build the workflow of your solution. For example, you can drag an "Import Data" shape onto a form and set information about the data source and data type. This is "plumbing" code that you do not have to write.
If a shape for a desired task does not exist, there are shapes that allow you to write custom code. Supported languages are Python and R.
When you finish building and testing your solution, buttons at the bottom allow you to configure and deploy a web service, so that your model is accessible via a simple API. There is even a test page, allowing you to call this API from within your browser.
There are limits to the free version. You cannot configure the size and number of instances on which it will run, and you are limited to 10 GB storage. If you cannot work within these restrictions, you can sign up for an Azure account and pay for the resources you use. Current pricing is available at this link.
If you are looking for a quick and simple way to build a machine learning solution, Azure Machine Learning Studio may be the tool for you.
I have been working with North American universities since joining Microsoft almost 5 years ago.
The school year is winding down for most colleges, so I'd like to talk about ways that Microsoft is helping university students in North America.
Azure for Students
This offering provides $100 Azure cloud computing credit free to any student. It is a good opportunity to learn about cloud computing and test the services in Azure. You can sign up with an EDU email address at http://aka.ms/Azure4Students. No credit card is required.
$100 credit for 1 year
Access to all of Azure
No credit card required
Microsoft Student Partner
Microsoft Student Partners (or MSPs) serve as Microsoft advocates on campus, providing workshops and information about Microsoft technology. In exchange, they receive education from Microsoft and (sometimes) a few prizes. It is a good way to increase the networking, education, and employability of a student. You can learn more at https://imagine.microsoft.com/msp
Student Ambassador
This program is similar to the MSP program, but it is run by the Microsoft Recruiting organization.
Student Hackathons
Student hackathons have become very popular the last few years. At a hackathon, students get together on campus and build hardware and software projects in teams. Many hackathons offer prizes for the best projects and students from other universities are often welcome. Microsoft has sponsored a great many hackathons over the years, offering money, prizes, Azure credits, hardware, and mentors to answer student questions. I have personally been involved in dozens of hackathons the last 3 years.
Imagine Cup
Imagine Cup is an international competition for teams of students who build amazing projects and want to turn them into a business. The top teams in each participating country are invited to the national finals for a chance to pitch their projects to a panel of judges, who select a few teams to advance to the International Finals. The top prize for this competition is $100,000 US and a mentoring session with Microsoft CEO Satya Nadella. You can learn more about Imagine Cup at https://imagine.microsoft.com/Compete
DataFests
Recently, I have been involved in a few DataFests. A DataFest is a competition on campus in which students are provided a set of data they have not yet seen; and asked to provide insights into the data. Students are free to use any tools they want and many present summaries, visualizations, and predictive analyses about the data. For the 3 DataFests in which I was involved (two at the University of Toronto and one at Duke University), Microsoft provided funds for food, free Azure credits, a workshop to show how to use MS's data science tools, and mentors to answer student questions.
Internships
Microsoft offers opportunities for university students to intern with the company. Most take place in Redmond in the summer. This is a great chance to work with a product team, learn new skills, and enhance your resume. These internships are very competitive, so students are encouraged to apply early in the school year. You can learn more and apply for internships at https://careers.microsoft.com/us/en/students-and-graduates
Academic CSE Team
This is the team for which I have been working this past year. We have coordinated and executed many of Microsoft's programs around university education. My responsibilities have included talking with professors, TAs, and students about how to incorporate Azure into their classes, meeting with MSP, and mentoring at hackathons and DataFests.
And So...
Microsoft is committed to helping students learn about software and computer science. The above list is some of the opportunities for students provided by Microsoft. Microsoft’s new fiscal year begins in a few weeks and there is not guarantee these programs will remain the same next year. In fact it’s likely there will be some changes.I don't know what programs will be offered going forward, but I expect a continuing strong commitment from my employer.
Freddy Cole looked every bit of his 83 years as he was helped onto the stage last night at the Jazz Showcase in Chicago's Printer's Row.
Until he sat at the piano. At that point he was transformed. For an hour and a half, he showed a strength and grace that belied his 8 decades. His command of piano and vocals was a strong as a man half his age.
He launched from one song to the next, never taking a break to chat with the audience until the final few minutes.
Although Cole can claim 3 Grammy nominations, he will always be remembered as the younger brother of legendary singer Nat "King" Cole. But he does not shun that comparison, as his set included three of Nat's songs (Paper Moon, L-O-V-E, and A Blossom Fell) and he recently released an album of songs made famous by his brother. Close your eyes and the richness of Freddy's voice is reminiscent of his late brother's talents.
Cole stuck mostly to ballads, but pleased the local crowd near the end of his set with a rendition of Ray Price's swinging "On the South Side of Chicago", which brought an ovation from his hometown.
His piano and vocals were accompanied by drums, upright bass, and guitar. Of course, the attention was mostly on Cole, but his talented Adam Moezinia took many solos. Bassist Elias Bailey stayed in the background until the last few songs when he became more and more bold with his solos and complex playing.
In the end, Freddy Cole closed the set with a song called "Goodbye", accepted a standing ovation, and was helped from the stage, again transformed into a fragile old man. Until the night's second set.
Day 2 of Microsoft's annual Build conference began with a keynote presentation hosted by Corporate Vice President Joe Belfiore. This was much shorter than the Day 1 keynote and focused on Microsoft 365. The presentation was split into the following "chapters":
Windows
Windows Developers
Office Development
Microsoft Graph
For me, the most interesting topic was Adaptive Cards - a technology that allows you to add functionality to Office Applications, Microsoft Teams, or SharePoint. Organizations can create Cards that access user and group data in Microsoft Graph and share data across applications.
Many of the features discussed in the keynote are available by joining the Insiders Program and using early releases of Windows and Office. Information on the Insiders Program is here.
You can watch the full keynote below or click this link.
Below are my raw notes as I watched.
Windows Timeline Apps save data to Graph (in cloud) Data available across devices
Shipping in-box PC app Data from phone available on PC e.g., read and reply text from PC Sets (available in Windows insider build) Office / Graph / Web working together
Windows Developers Fluent Design System Decoupling parts of Windows to make it easier to add to apps UWP XAML Islands All Windows Application can access Fluent Design System No threading across processes Some controls designed for Win10 Edge-based Available in WPF app Microsoft is using ML to improve products e.g., Grammar checking in Word Windows UI Library (WinUI) available via NuGet .NET Core 3 preview available later this year MSIX Application Containers Simpler deployment Android Emulator compatible with Hyper-V Notepad now supports Linux line feeds Change to MS Store revenue sharing Consumer apps: Increase dev revenue share to 85% 95% if your campaign drives user to store (via web site or app)
Office Development Deployment Deploy custom functions to all users in your organization Deployment centralized Adaptive cards Post / update GitHub comments and issues from Outlook Payments from Outlook email message Build Your own cards at adaptivecard.io Customization to MS Teams Tab Extension Same as SharePoint extension Sample app: Click in teams to launch PowerBI report Build Adaptive Card for MS Teams MS Store has "Teams" section
Microsoft Graph Microsoft Graph Powers Microsoft 365 Users sign into app with Microsoft Graph identity Get user data across apps: Provide personalized experience Extend Graph group or user schema: Add new properties Microsoft Graph UWP Controls available today Open Source https://aka.ms/windowstoolkit
Microsoft held its annual Build conference this week in Seattle. Years ago, these large developer conferences were a chance for Microsoft to reveal everything they had been working on for the past year. In recent years, the company has been much more open, allowing users and customers to see products as they develop. But that did not mean that Microsoft did not have some big announcements this year.
The Day 1 keynote was hosted by CEO Satya Nadella and CVP Scott Guthrie and focused on developer tools and cloud computing. Microsoft Azure, Microsoft 365, and the Visual Studio ecosystem took center stage.
For me, the most interesting announcements were Azure Dev Spaces, Azure Cognitive Search, Azure Databricks, Kubernetes as a Service, and DevOps Projects.
Of course, the most exciting part came 3 minutes into the keynote, when they announced that the 2 youngest Build attendees were the 10- and 12-year-old daughters of my friends Tibi and Nicoleta!
You can watch the full keynote below or click this link.
Below are the notes I took during the keynote.
3 core pillars Privacy Privacy is a Human right Platforms Azure Microsoft 365
Key Technologies Ubiquitous computing Distributed, event-driven, and serverless Azure: 50+ regions 70+ certifications Open Sourcing Azure IoT Edge Artificial Intelligence Project Kinect for Azure Ultra-wide field of view
Multi-Device, Multi-Sense experience near field, far field mixed reality Microsoft 365 Cortana Cortana + Alexa integration
Microsoft Remote Assist Video calling Image sharing Mixed reality Integration with MS Teams Microsoft Layout Share and edit designs in real time Mixed Reality
Accessibility
Azure areas Dev Tools + DevOps Containers + Serverless Internet of Things Data Artificial Intelligence
Dev Tools + DevOps Visual Studio Live Share Works across PC and Mac Works across VS and VS Code Watch co-workers code, keyboard, mouse, and debugging context Work independently or together Secure connection Free! Open Source Microsoft is the single largest contributor to GitHub Launching today: Text API (Linting and Code analysis) App Center + GitHub integration Continuous integration app from within GitHub Testing on physical mobile devices hosted in Azure DevOps Branch from Kanban board tasks. Azure Portal: DevOps Projects CI/CD pipeline Integrated with VSTS project Deploy to App Services or Kubernetes Any language or platform
Containers + Serverless Kubernetes as a Service View health of each container Logging available Query, show charts Azure Dev Spaces Run and debug in a private space in Azure Azure Event Grid Serverless composition Workflow executed
Internet of Things IoT Edge VS Code project consisting of containers Containers running on local device Azure Function running on local device
Data Cosmos DB New pricing options Global Scale Multi-Master Write Support (much faster writes: trillions of reads & writes per second) Reduced Write Conflicts Graphical interface in Azure Portal: Deploy data globally
Artificial Intelligence Azure Cognitive Search Index and analyze data in Azure Azure Databricks Spark-based analytics
We stood in San Francisco's Creativity Museum, waiting for the announcement of the top six teams at the Imagine Cup US Finals. Twelve teams from universities across the country had qualified for the US Finals, earning a trip to California and a chance to pitch and demonstrate their software, hardware, and business ideas to a panel of judges. Coding, planning, pitching, and judging were complete on this warm California evening and all that remained was the award announcements. The top six teams would win cash prizes up to $10,000 and a chance to compete in the International Imagine Cup Finals held in Redmond, WA in July.
One by one, the finalists heard their names called and were called to the stage to accept their awards. The winners jumped with joy and hugged one another when they were called. It was a reward for months of planning and hard work.
I was able to be a part of this event for the first time this year, serving on the selection committee, advising as a team mentor, and assisting the judges during and after the presentations. I was impressed by the projects, which showed remarkable creativity; I was impressed by the presentations, which were far more polished than anything I could have done at their age; I was impressed by the judges and their commitment doing to a thorough evaluation and providing constructive feedback; and I was impressed by the team that pulled together this amazing event, involving hundreds of students, professors, mentors, judges, contractors, and volunteers.
Students elected to solve real world problems, including
Using cameras and artificial intelligence to assist wheelchair-bound people;
A drone that captures and analyzes images to detect potential forest fires;
A HoloLens app to assist with remote, real-time training;
Using Facial Recognition AI to reunite separated refugee families
Every student with whom I spoke told me that Imagine Cup was a great experience and I have to agree. I am looking forward to the Canadian Imagine Cup Finals in Vancouver, BC later this month!
The DataFest concept was created back in 2011 by the American Statistical Association. Students are provided a large data set and are given 2 full days to report on some useful insights and/or visualizations about the data.
I attended the ASA DataFest at the University of Toronto May 1-2. The event was organized by UT Professor Nathan Taback, who served as host.
The students – all from U of T - worked in teams of 2-4 and presented their findings on the evening of the second day. This was a judged competition with prizes for the top 4 teams.
Students had no knowledge of the data set before it was made available to them when they showed up at the venue. Data was provided by the Indeed job search engine and included information on job postings in the United States, Canada, and Germany.
Following Dr. Taback's opening remarks, I delivered a presentation on Data Science tools in Azure, including demos of Machine Learning Studio and Azure Notebooks. Over half the teams ended up using these tools in their analysis.
In addition to the opening ceremonies, I served as a mentor during the DataFest and a judge at the end. Several professors and students donated their time as mentors during the event and judges included professors and industry professionals. I also recruited local MVPs Atley Hunter and Vivek Patel, along with user group leader Ashraf Ghonaim to serve as mentors and/or judges.
Almost 200 students attended, and 19 teams presented their findings on Day 2.
The winning team used Azure ML Studio to split users into low, medium, and high salary ranges and determine the factors required to move from one level to the next level above.
Microsoft donated prizes and money for food to the event (along with my time) and Azure credits for the students to use.
5/6 Today I am grateful for: -My first time attending and mentoring at an Imagine Cup Finals event -A post-event party at the Creativity Museum in San Francisco last night
5/5 Today I am grateful to meet new people in far-away cities.
5/4 Today I am grateful to play ping pong for the first time in years.
5/3 Today I am grateful for a successful DataFest at the University of Toronto and that I was asked to present the awards at the end.
5/2 Today I am grateful for all the students who signed up for an Azure account after my presentation yesterday.
5/1 Today I am grateful for nice weather in Ontario yesterday.
4/30 Today I am grateful to make it safely to Waterloo, ON yesterday.
4/29 Today I am grateful for a visit to the Field Museum yesterday with Jeff and Lynn.
4/28 Today I am grateful to spend the week working with smart engineers to build a cool app for the Seattle Storm WNBA team.
4/27 Today I am grateful to see Infinity War last night.
4/26 Today I am grateful for a team dinner last night at the Hard Rock Café.
4/25 Today I am grateful for a visit to the Museum of Pop Culture last night in Seattle.
4/24 Today I am grateful to those who had a kind word for me yesterday when I was feeling down.
4/23 Today I am grateful for my new computer
4/22 Today I am grateful to Michael and his team for 10 years of hard work to host a consistently excellent Kalamazoo X conference.
4/21 Today I am grateful to wake up to this view.
4/20 Today I am grateful to deliver my final lecture of the UIUC academic year.
4/19 Today I am grateful to attend an OpenHack event in the Willis Tower and learn about IoT.
4/18 Today I am grateful for a Polsky Center mentor reception at The Kitchen in River North last night.
4/17 Today I am grateful to finish my taxes on time.
4/16 Today I am grateful to work from home on a bad weather day.
4/15 Today I am grateful for: -My first visit to the Museum of Broadcast Communications and the SNL exhibit -An excellent concert by the Average White Band last night in Hyde Park
4/13 Today I am grateful to return home safely after a week on the road.
4/12 Today I am grateful to see a Clippers - Lakers game at the Staples Center last night.
4/11 Today I am grateful for my first visit to the University of Southern California yesterday.
4/10 Today I am grateful for my first Azure University Tour stop in Austin, TX.
4/9 Today I am grateful for dinner and a Durham Bulls game yesterday with Christine and Cosby last night.
4/8 Today I am grateful for -an exciting overtime Hurricanes-Lightning hockey game last night -my first time at PNC Arena.
4/7 Today I am grateful for my first visit to Durham, NC.
4/6 Today I am grateful for my first bike ride of the year.
4/5 Today I am grateful I was able to find some badly-needed Advil in my unpacked boxes yesterday morning.
4/4 Today I am grateful for Superdawg with Tim last night
4/3 Today I am grateful for: -Lunch with Jimmy yesterday -Attending the NCAA Men's Basketball Final Four last night
4/2 Today I am grateful for Yelp, which helps me find a good meal in an unfamiliar city.
Everyone in Xanth has a magical power. Everyone except Bink.
Some of Xanth's citizens are accomplished wizards, while others can do simple tricks like make a spot appear on a wall. But, Xanthian law states that if you reach the age of 25 without showing evidence of some magical ability, you are forced to leave to the non-magical land of Mundane.
Bink is 25 and is banished to Mundane.
In Mundane, he meets the evil wizard Trent, who was banished decades before for trying to usurp the throne of Xanth. He then meets Fanchon, an intelligent ugly woman, who fled Xanth for reasons of her own. The three travel together, encounter adventures, overcome dangerous magical creatures and plants, collaborate, and fight. Bink wrestles with moral questions, such as: What does it mean to be an Evil Wizard; and is it ok to be attracted to females of another species if she has big breasts; and do I prefer dumb pretty women or smart ugly women or something in between or a variety?
A Spell for Chameleon was so popular that author Piers Anthony decided to convert his planned trilogy into an ongoing series that now extends
Anthony creates an imaginative world, filled with wonders and dangers and his characters are relatable, even if he spends a bit too much time explaining their motivations, rather than letting the reader discover these themselves.
Anthony also brings a lot of puns and other wordplay into his narrative, which can be entertaining.
The book is a fable with a couple morals - everyone is special and worthwhile in their own way, even if it is not obvious to others; and evil is defined by those in power, rather than by absolute laws. The messages are a bit heavy-handed, but they mostly work.
However, I found parts of the story to be troubling.
Anthony comes across as sexist, giving undue attention to the physical appearance of females. Also, Bink participates in a rape trial and the conclusions (the judge's remarks and Bink's opinions) made me more than a little uncomfortable.
A Spell for Chameleon reads like a children's book, but there is too much sex for it to be appropriate to children. Still, it had enough creativity to hold my attention.
It is a strange experience to read a book twice with a gap of nearly 50 years between.
I think I was in fourth grade when I first read A Wrinkle in Time by Madeleine L'Engle. This week, I read it again.
I enjoyed this book as a boy, but I didn't remember many of its details. I didn't recall the book's ending, but I recalled the characters and a few scenes here and there. This re-read was like visiting an old friend. I would read a passage and remember its effect on 9-year-old me. I believe this was the first time I heard the idea of more than 3 dimensions in the Universe and that one could consider time as a fourth dimension.
"A Wrinkle in Time" is the story of the Murray family. Mom and Dad are brilliant scientists and their four children are all brilliant, but some have quirks. Meg is an outsider, who struggles to fit in at school. And Charles Wallace is a 5-year-old with the mind of an adult, even though the rest of the town believes him to be mentally handicapped because he seldom speaks in public.
Meg and Charles - along with their new friend Calvin - set out to find their father, who has been missing since he set out on a secret government mission years ago. They are assisted by 3 mysterious ancient women. Their journey takes them across time, space, and dimensions, where they encounter other sentient races and an evil force determined to conquer the universe.
It is an adventure and a morality play, involving a galactic struggle of Good versus Evil. It's a delightful story with a good characters and imaginative worlds.
The ending is a bit rushed, but the journey is very rewarding.
It was a rewarding adventure for me to read this book twice with a gap of almost 5 decades between. I look forward to reading it again in another 50 years.
Gardens of the Moon by Steven Erikson is the first book in his 10-volume "Malazan Book of the Fallen" series. It takes place on the continent of Genabackis, where the Malazan empire holds power through force and where men and gods and magicians and dragons and various sentient races exist together - often in violent battle with one another. This first book follows many characters and plotlines, taking the reader through the battle for the continent.
I'm unsure exactly why Gardens of the Moon failed to hold my attention. It's a decent story and some of the characters are quite good (especially Anomander Rake, the giant ancient warrior wizard king). I liked when two storylines would finally converge as their characters crossed paths. And Erikson does a decent job of building an imaginative world filled with its own rules and politics and even a fortress that floats in the sky.
But I found the plethora of characters to be confusing. I found the non-linear storytelling confusing. I found the constant death and resurrection of characters and the multiple names of characters to be confusing.
I frequently found my mind wandering away from the story. More than a few times, I had to re-read a chapter to understand it. And I would often completely forget a character shortly after he disappeared from a story, so that I was confused by his appearance in a later chapter.
Complexity is fine if I am committed to reading (or re-reading) an entire series. But I was unfamiliar with this series before I picked up Volume One and Erikson did nothing to draw me in and encourage me to complete the journey.
I know that many people love this book and I wonder if it is more enjoyable in the context of the entire 10-volume series.
The last few years, I've been thinking a lot about forgiveness.
By far, the biggest things that keep me awake at night are the pain of wrongs inflicted on me and regret for the wrongs I've done to others. I have consciously tried to address both.
Requesting Forgiveness
A few years ago, I made a list of people to whom I felt I owed an apology. These are people I treated thoughtlessly; or I made a joke at their expense or at the expense of someone close to them.
Years had passed (in some cases decades) since I committed these transgressions. But I sought out every person on this list and I apologized to his or her face. One by one, I pulled them aside and told them exactly what I did wrong and I offered my apology. I made no excuses and I waited for them to accept my apology. Some told me it was unnecessary; some told me how much I had hurt them. I think all were surprised that I did it. But every one of them remembered the events I addressed and every one of them accepted my apology.
I completed this list about a year ago and I have slept better since then.
Here are my rules for effective apology.
Be specific. Rather than saying "I'm sorry for anything wrong I may have done", say something like "I was very insensitive to your feelings and I am sorry for that."
Be sincere. Don't apologize unless you mean it. People sense insincerity and will react accordingly.
Accept responsibility. Don't say "I'm sorry if you're mad." Say "I'm sorry for what I did" or "I'm sorry for hurting you"
Don't make excuses. There may be reasons why you behaved the way you did, but an apology is not a time to explain or justify your behavior. If they ask why you did what you did, then go ahead and answer. But generally, this is a separate issue from your wrongdoing and should not affect your apology. Qualifying an apology dilutes its sincerity.
Ask if they accept your apology. It doesn't work unless it is both offered and accepted. It might take them time to accept it; but at least you have started the process. I was lucky that all my apologies were accepted, but I was prepared for the possibility they might need time to think about it or might reject it completely. That is their choice.
Forgiving Others
During this same period, I listed the people who had wronged me; and, one at a time, I consciously and deliberately forgave them. I did not approach them in person; this was a personal thing for me. None of these people ever offered me a sincere apology or even acknowledged their wrongdoing. I suspect nearly all of them have forgotten what they did. Most of them probably did not even know the pain they caused. Some may be embarrassed by their actions and are hopeful that I have forgotten. But it was important for me to offer forgiveness. I was only harming myself by hanging onto my anger.
I don’t have to offer forgiveness to everyone. There are some bad people in the world and those people I try to exclude from my life and move on. The ones I target are good people who happened to do something unkind to me. Those are the ones that stay in my mind.
I have found both giving and receiving forgiveness to be liberating. A weight was lifted from me - almost in a physical sense - when I began and advanced this journey.
Lessons Learned
This process has taught me a few things.
Most emotional pain is not caused via deliberate malice; most is caused by thoughtlessness and self-absorption. Most of us are completely unaware of the damage we cause others through an unkind word or action. What seems trivial to us can leave scars in others.
Apologies are not easy - especially sincere ones; But, if done right, they help both the giver and the receiver.
Forgiveness is even harder than apologizing. At least, it is for me. Especially forgiving those who never ask for it. I'm not done forgiving. There are some people I want to forgive that I have not yet been able to, which troubles me. I'm working on this.
And finally, the hardest person I've found to forgive is myself. I've lost a lot of sleep over the years dwelling on regrets. But the process I've described here has helped.
The Crystal Cave by Mary Stewart tells the story of Merlin, the legendary wizard and advisor to the legendary King Arthur.
Unlike most books on this topic, this is the story of young Merlin - the bastard son of a Welsh princess, who discovers he has visions that predict the future or reveal events happening far away.
The Merlin of The Crystal Cave is more human than the one we encounter in most Arthurian stories. Here, Merlin has no powers other than his vision, a keen mind, and a solid education. But he uses these powers to become advisor to kings and to influence the coming of Arthur.
This book is a coming of age story in which young Merlin meets his first mentor, discovers his birth father, and searches for his place in the world. It is set against the backdrop of fifth century England, after the departure of the Romans and before the unification of Britain under a single king. As such, it is also an adventure story. And Stewart tells the adventure well, expertly building the character of Merlin and those he encounters.
Mary Stewart originally won fame as a writer of romance novels; but this departure from her familiar genre was a great success. The Crystal Cave offers a fresh take on an old story. I enjoyed it enough that I plan to read the sequels in this series.