Using MP4Box to concatenate many .h264 files into one MP4 file
The general form of the concatenate command for MP4Box is:
MP4Box -add <filename>.ext -cat <filename>.ext output.ext1
When you have more than a couple of output files, you’re going to want to automate that -cat part as much as possible because let’s face it, writing out that statement even more than a couple of times will get really old really fast.
The method below works but I’m sure that there is a better way to do it.
- echo out the command you want to run. In this case:
(echo -n "MP4Box "; for i in *.h264; do echo -n " -cat $i"; done; echo -n " hummingbird.mp4") >> com.txt
- Edit the file
com.txtcreated in (1) so that you can change the first-catto-add
vim com.txt
- While still in
vimediting thecom.txtfile add the#!/bin/shto the first line. When finished, exit vim2 - Change the mode of the file so it can run
chmod +x com.txt
- Run the file:
./com.txt
Why am I doing all of this? I have a Raspberry Pi with a Camera attachment and a motion sensor. I’d like to watch the hummingbirds that come to my hummingbird feeder with it for a day or two and get some sweet video. We’ll see how it goes.
My Map Art Project
I’d discovered a python package called osmnx which will take GIS data and allow you to draw maps using python. Pretty cool, but I wasn’t sure what I was going to do with it.
After a bit of playing around with it I finally decided that I could make some pretty cool Fractures.
I’ve got lots of Fracture images in my house and I even turned my diplomas into Fractures to hang up on the wall at my office, but I hadn’t tried to make anything like this before.
I needed to figure out what locations I was going to do. I decided that I wanted to do 9 of them so that I could create a 3 x 3 grid of these maps.
I selected 9 cities that were important to me and my family for various reasons.
Next writing the code. The script is 54 lines of code and doesn’t really adhere to PEP8 but that just gives me a chance to do some reformatting / refactoring later on.
In order to get the desired output I needed several libraries:
osmnx (as I’d mentioned before)
matplotlib.pyplot
numpy
PIL
If you’ve never used PIL before it’s the ‘Python Image Library’ and according to it’s home page it
adds image processing capabilities to your Python interpreter. This library supports many file formats, and provides powerful image processing and graphics capabilities.
OK, let’s import some libraries!
import osmnx as ox, geopandas as gpd, os
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
Next, we establish the configurations:
ox.config(log_file=True, log_console=False, use_cache=True)
The ox.config allows you to specify several options. In this case, I’m:
- Specifying that the logs be saved to a file in the log directory
- Suppress the output of the log file to the console (this is helpful to have set to
Truewhen you’re first running the script to see what, if any, errors you have. - The
use_chache=Truewill use a local cache to save/retrieve http responses instead of calling API repetitively for the same request URL
This option will help performance if you have the run the script more than once.
OSMX has many different options to generate maps. I played around with the options and found that the walking network within 750 meters of my address gave me the most interesting lines.
AddressDistance = 750
AddressDistanceType = 'network'
AddressNetworkType = 'walk'
Now comes some of the most important decisions (and code!). Since I’ll be making this into a printed image I want to make sure that the image and resulting file will be of a high enough quality to render good results. I also want to start with a white background (although a black background might have been kind of cool). I also want to have a high DPI. Taking these needs into consideration I set my plot variables:
PlotBackgroundColor = '#ffffff'
PlotNodeSize = 0
PlotFigureHeight = 40
PlotFigureWidth = 40
PlotFileType = 'png'
PlotDPI = 300
PlotEdgeLineWidth = 10.0
I played with the PlotEdgeLineWidth a bit until I got a result that I liked. It controls how thick the route lines are and is influenced by the PlotDPI. For the look I was going for 10.0 worked out well for me. I’m not sure if that means a 30:1 ratio for PlotDPI to PlotEdgeLineWidth would be universal but if you like what you see then it’s a good place to start.
One final piece was deciding on the landmarks that I was going to use. I picked nine places that my family and I had been to together and used addresses that were either of the places that we stayed at (usually hotels) OR major landmarks in the areas that we stayed. Nothing special here, just a text file with one location per line set up as
Address, City, State
For example:
1234 Main Street, Anytown, CA
So we just read that file into memory:
landmarks = open('/Users/Ryan/Dropbox/Ryan/Python/landmarks.txt', 'r')
Next we set up some scaffolding so we can loop through the data effectively
landmarks = landmarks.readlines()
landmarks = [item.rstrip() for item in landmarks]
fill = (0,0,0,0)
city = []
The loop below is doing a couple of things:
- Splits the landmarks array into base elements by breaking it apart at the commas (I can do this because of the way that the addresses were entered. Changes may be needed to account for my complex addresses (i.e. those with multiple address lines (suite numbers, etc) or if local addresses aren’t constructed in the same way that US addresses are)
- Appends the second and third elements of the
partsarray and replaces the space between them with an underscore to convertAnytown, CAtoAnytown_CA
<!-- -->
This next line isn’t strictly necessary as it could just live in the loop, but it was helpful for me when writing to see what was going on. We want to know how many items are in the landmarks
rows = len(landmarks)
Now to loop through it. A couple of things of note:
The package includes several graph_from_... functions. They take as input some type, like address, i.e. graph_from_address (which is what I’m using) and have several keyword arguments.
In the code below I’m using the ith landmarks item and setting the distance, distance_type, network_type and specifying an option to make the map simple by setting simplify=‘true’
To add some visual interest to the map I’m using this line
ec = ['#cc0000' if data['length'] >=100 else '#3366cc' for u, v, key, data in G.edges(keys=True, data=True)]
If the length of the part of the map is longer than 100m then the color is displayed as #cc0000 (red) otherwise it will be #3366cc (blue)
The plot_graph is what does the heavy lifting to generate the image. It takes as input the output from the graph_from_address and ec to identify what and how the map will look.
Next we use the PIL library to add text to the image. It takes into memory the image file and saves out to a directory called /images/. My favorite part of this library is that I can choose what font I want to use (whether it’s part of the system fonts or a custom user font) and the size of the font. For my project I used San Francisco at 512.
Finally, there is an exception for the code that adds text. The reason for this is that when I was playing with adding text to the image I found that for 8 of 9 maps having the text in the upper left hand corner worker really well. It was just that last one (San Luis Obispo, CA) that didn’t.
So, instead of trying to find a different landmark, I decided to take a bit of artistic license and put the San Luis Obispo text in the upper right hard corner.
Once the script is all set simply typing python MapProject.py in my terminal window from the directory where the file is saved generated the files.
All I had to do what wait and the images were saved to my /images/ directory.
Next, upload to Fracture and order the glass images!
I received the images and was super excited. However, upon opening the box and looking at them I noticed something wasn’t quite right
[caption id="attachment_188" align="alignnone" width="2376"]![Napa with the text slightly off the image]images/uploads/2018/01/Image-12-16-17-6-55-AM.jpeg){.alignnone .size-full .wp-image-188 width="2376" height="2327"} Napa with the text slightly off the image[/caption]
As you can see, the name place is cut off on the left. Bummer.
No reason to fret though! Fracture has a 100% satisfaction guarantee. So I emailed support and explained the situation.
Within a couple of days I had my bright and shiny fractures to hang on my wall
[caption id="attachment_187" align="alignnone" width="2138"]![Napa with the text properly displaying]images/uploads/2018/01/IMG_9079.jpg){.alignnone .size-full .wp-image-187 width="2138" height="2138"} Napa with the text properly displaying[/caption]
So that my office wall is no longer blank and boring:
![]()
but interesting and fun to look at

My Mac session with Apple
For Christmas I bought myself a 2017 13-inch MacBook Pro with Touch Bar. Several bonuses were associated with the purchase:
- A \$150 Apple Gift Card because I bought the MacBook Pro on Black Friday and Apple had a special going (w00t!)
- The Credit Card I use to make ALL of my purchases at Apple has a 3% cash back (in the form of iTunes cards)
- A free 30 minute online / phone session with an ‘Apple Specialist’
Now I didn’t know about item number 3 when I made the purchase, but was greeted with an email informing me of my great luck.
This is my fifth Mac1 and I don’t remember ever getting this kind of service before. So I figured, what the hell and decided to snooze the email until the day after Christmas to remind myself to sign up for the session.
When I entered the session I was asked to optionally provide some information about myself. I indicated that I had been using a Mac for several years and considered myself an intermediate user.
My Apple ‘Specialist’ was Jaime. She confirmed the optional notes that I entered and we were off to the races.
Now a lot of what she told me about Safari (blocking creepy tracking behavior, ability to mute sound from auto play videos, default site to display in reader view) I knew from the WWDC Keynote that I watched back in June, but I listened just in case I had missed something from that session (or the 10s / 100s of hours of podcasts I listened to about the Keynote).
One thing that I had heard about was the ability to pin tabs in Safari. I never really knew what that meant and figured it wasn’t anything that I needed.
I was wrong. Holy crap is pinning tabs in Safari a useful feature! I can keep all of my most used sites pinned and get to them really quickly and they get auto refreshed! Sweet!
The other super useful thing I found out about was the Split Screen feature that allows you to split apps on your screen (in a very iOS-y way!).
Finally, Jaime reviewed how to customize the touch bar! This one was super useful as I think there are 2 discoverability issues with it:
- The option to
Customize Touch Baris hidden in theViewmenu which isn’t somewhere I’d look for it - To Customize the Touch Bar you drag down from the Main Screen onto the Touch Bar.
After the call I received a nice follow up email from Apple / Jaime
Now that you're more familiar with your new Mac, here are some additional resources that can help you go further.
Apple Support Find answers to common questions, watch video tutorials, download user guides, and share solutions with the Apple community. Visit Support
Today at Apple Discover inspiring programs happening near you. Visit Today at Apple
Accessories From the Apple accessories page, you can learn about all kinds of new and innovative products that work with iPhone, iPad, Mac and more. Visit Accessories
How to use the Touch Bar on your MacBook Pro - https://support.apple.com/en- us/HT207055
Use Mission Control on your Mac - https://support.apple.com/en- us/HT204100
Use two Mac apps side by side in Split View - https://support.apple.com/en- us/HT204948
Websites preferences - https://support.apple.com/ guide/safari/websites- preferences-ibrwe2159f50
I’m glad that I had the Mac session and I will encourage anyone that buys a Mac in the future to schedule one.
- They are in order of purchase: 2012 15-inch MacBook Pro, 2014 27-inch 5K iMac, 2015 MacBook, 2016 13-inch 2 Thunderbolt MacBook Pro; 2017 13-inch MacBook Pro with Touch Bar ↩︎
Setting the Timezone on my server
When I scheduled my last post on December 14th to be published at 6pm that night I noticed that the schedule time was a bit … off:

I realized that the server times as still set to GMT and that I had missed the step in the Linode Getting Started guide to Set the Timezone.
No problem, just found the Guide, went to this section and ran the following command:
sudo dpkg-reconfigure tzdata
I then selected my country (US) and my time zone (Pacific-Ocean) and now the server has the right timezone.
Setting up the site with SSL
I’ve written about my migration from Squarespace to Wordpress earlier this year. One thing I lost with that migration when I went to Wordpress in AWS was having SSL available. While I’m sure Van Hoet will “well actually” me on this, I never could figure out how to set it up ( not that I tried particularly hard ).
The thing is now that I’m hosting on Linode I’m finding some really useful tutorials. This one showed me exactly what I needed to do to get it set up.
Like any good planner I read the how to several times and convinced myself that it was actually relatively straight forward to do and so I started.
Step 1 Creating the cert files¶
Using this tutorialI was able to create the required certificates to set up SSL. Of course, I ran into an issue when trying to run this command
chmod 400 /etc/ssl/private/example.com.key
I did not have persmision to chmod on that file. After a bit of Googling I found that I can switch to interactive root mode by running the command
sudo -i
It feels a bit dangerous to be able to just do that (I didn’t have to enter a password) but it worked.
Step 2¶
OK, so the tutorial above got me most(ish) of the way there, but I needed to sign my own certificate. For that I used this tutorial. I followed the directions but kept coming up with an error:
Problem binding to port 443: Could not bind to the IPv4 or IPv6
I rebooted my Linode server. I restarted apache. I googled and I couldn’t find the answer I was looking for.
I wanted to give up, but tried Googling one more time. Finally! An answer so simple it couldn’t work. But then it did.
Stop Apache, run the command to start Apache back up and boom. The error went away and I had a certificate.
However, when I tested the site using SSL LabsI was still getting an error / warning for an untrusted site.
🤦🏻♂️
—
OK ... take 2
I nuked my linode host to start over again.
First things first ... we need to needed to secure my server. Next, we need to set up the server as a LAMP and Linode has this tutorial to walk me through the steps of setting it up.
I ran into an issue when I restarted the Apache service and realized that I had set my host name but hadn’t update the hosts file. No problem though. Just fire up vim and make the additional line:
127.0.0.1 milo
Next, I used this tutorial to create a self signed certificate and this to get the SSL to be set up.
One thing that I expected was that it would just work. After doing some more reading what I realized was that a self signed certificate is useful for internal applications. Once I realized this I decided to not redirect to SSL (i.e. part 443) for my site but instead to just use the ssl certificate it post from Ulysses securely.
Why go to all this trouble just too use a third party application to post to a WordPress site? Because Ulysses is an awesome writing app and I love it. If you’re writing and not using it, I’d give it a try. It really is a nice app.
So really, no good reason. Just that. And, I like to figure stuff out.
OK, so Ulysses is great. But why the need for an SSL certificate? Mostly because when I tried to post to Wordpress from Ulysses without any certificates ( self signed or not ) I would get a warning that my traffic was unencrypted and could be snooped. I figured, better safe than sorry.
Now with the ssl cert all I had to do was trust my self signed certificate and I was set1
- Mostly. I still needed to specify the domain with www otherwise it didn’t work. ↩︎
Installing fonts in Ulysses
One of the people I follow online, Federico Viticci, is an iOS power user, although I would argue that phrase doesn’t really do him justice. He can make the iPad do things that many people can’t get Macs to do.
Recently he posted an article on a new font he is using in Ulysses and I wanted to give it a try. The article says:
Installing custsom fonts in Ulysses for iOS is easy: go to the GitHub page, download each one, and open them in Ulysses (with the share sheet) to install them.
Simple enough, but it wasn’t clicking for me. I kept thinking I had done something wrong. So I thought I’d write up the steps I used so I wouldn’t forget the next time I need to add a new font.
Downloading the Font¶
- Download the font to somewhere you can get it. I chose to save it to iCloud and use the
Filesapp - Hit Select in the
Filesapp - Click
Share - Select
Open in Ulysses - The custom font is now installed and being used.
Checking the Font:¶
- Click the ‘A’ in the writing screen (this is the font selector) located in the upper right hand corner of Ulysses

- Notice that the Current font indicates it’s a custom font (in This case iA Writer Duospace:

Not that hard, but there’s no feedback telling you that you have been successful so I wasn’t sure if I had done it or not.
Switching to Linode
Switching to Linode
I’ve been listening to a lot of Talk Python to me lately ... I mean a lot. Recently there was a coupon code for Linode that basically got you four months free with a purchase of a single month, so I thought, ‘what the hell’?
Anyway, I have finally been able to move everything from AWS to Linode for my site and I’m able to publish from my beloved Ulysses.
Initially there was an issue with xmlrpc which I still haven’t fully figured out.
I tried every combination of everything and finally I’m able to publish.
I’m not one to look a gift horse in the mouth so I’ll go ahead and take what I can get. I had meant to document a bit more / better what I had done, but since it basically went from not working to working, I wouldn’t know what to write at this point.
The strangest part is that from the terminal the code I was using to test the issue still returns and xmlrpc faultCode error of -32700 but I’m able to connect now.
I really wish i understood this better, but I’m just happy that I’m able to get it all set and ready to go.
Next task ... set up SSL!
Making Background Images
I'm a big fan of podcasts. I've been listening to them for 4 or 5 years now. One of my favorite Podcast Networks, Relay just had their second anniversary. They offer memberships and after listening to hours and hours of All The Great Shows I decided that I needed to become a member.
One of the awesome perks of Relay membership is a set of Amazing background images.
This is fortuitous as I've been looking for some good backgrounds for my iMac, and so it seemed like a perfect fit.
On my iMac I have several spaces configured. One for Writing, one for Podcast and one for everything else. I wanted to take the backgrounds from Relay and have them on the Writing space and the Podcasting space, but I also wanted to be able to distinguish between them. One thing I could try to do would be to open up an image editor (Like Photoshop, Pixelmater or Acorn) and add text to them one at a time (although I'm sure there is a way to script them) but I decided to see if I could do it using Python.
Turns out, I can.
This code will take the background images from my /Users/Ryan/Relay 5K Backgrounds/ directory and spit them out into a subdirectory called Podcasting
from PIL import Image, ImageStat, ImageFont, ImageDraw
from os import listdir
from os.path import isfile, join
# Declare Text Attributes
TextFontSize = 400
TextFontColor = (128,128,128)
font = ImageFont.truetype("~/Library/Fonts/Inconsolata.otf", TextFontSize)
mypath = '/Users/Ryan/Relay 5K Backgrounds/'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
onlyfiles.remove('.DS_Store')
rows = len(onlyfiles)
for i in range(rows):
img = Image.open(mypath+onlyfiles[i])
width, height = img.size
draw = ImageDraw.Draw(img)
TextXPos = 0.6 * width
TextYPos = 0.85 * height
draw.text((TextXPos, TextYPos),'Podcasting',TextFontColor,font=font)
draw.text
img.save('/Users/Ryan/Relay 5K Backgrounds/Podcasting/'+onlyfiles[i])
print('/Users/Ryan/Relay 5K Backgrounds/Podcasting/'+onlyfiles[i]+' successfully saved!')
This was great, but it included all of the images, and some of them are really bright. I mean, like really bright.
So I decided to use something I learned while helping my daughter with her Science Project last year and determine the brightness of the images and use only the dark ones.
This lead me to update the code to this:
from PIL import Image, ImageStat, ImageFont, ImageDraw
from os import listdir
from os.path import isfile, join
def brightness01( im_file ):
im = Image.open(im_file).convert('L')
stat = ImageStat.Stat(im)
return stat.mean[0]
# Declare Text Attributes
TextFontSize = 400
TextFontColor = (128,128,128)
font = ImageFont.truetype("~/Library/Fonts/Inconsolata.otf", TextFontSize)
mypath = '/Users/Ryan/Relay 5K Backgrounds/'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
onlyfiles.remove('.DS_Store')
darkimages = []
rows = len(onlyfiles)
for i in range(rows):
if brightness01(mypath+onlyfiles[i]) <= 65:
darkimages.append(onlyfiles[i])
darkimagesrows = len(darkimages)
for i in range(darkimagesrows):
img = Image.open(mypath+darkimages[i])
width, height = img.size
draw = ImageDraw.Draw(img)
TextXPos = 0.6 * width
TextYPos = 0.85 * height
draw.text((TextXPos, TextYPos),'Podcasting',TextFontColor,font=font)
draw.text
img.save('/Users/Ryan/Relay 5K Backgrounds/Podcasting/'+darkimages[i])
print('/Users/Ryan/Relay 5K Backgrounds/Podcasting/'+darkimages[i]+' successfully saved!')
I also wanted to have backgrounds generated for my Writing space, so I tacked on this code:
for i in range(darkimagesrows):
img = Image.open(mypath+darkimages[i])
width, height = img.size
draw = ImageDraw.Draw(img)
TextXPos = 0.72 * width
TextYPos = 0.85 * height
draw.text((TextXPos, TextYPos),'Writing',TextFontColor,font=font)
draw.text
img.save('/Users/Ryan/Relay 5K Backgrounds/Writing/'+darkimages[i])
print('/Users/Ryan/Relay 5K Backgrounds/Writing/'+darkimages[i]+' successfully saved!')
The print statements at the end of the for loops were so that I could tell that something was actually happening. The images were VERY large (close to 10MB for each one) so the PIL library was taking some time to process the data and I was concerned that something had frozen / stopped working
This was a pretty straightforward project, but it was pretty fun. It allowed me to go from this:

To this:

For the text attributes I had to play around with them for a while until I found the color, font and font size that I liked and looked good (to me).
The Positioning of the text also took a bit of experimentation, but a little trial and error and I was all set.
Also, for the brightness level of 65 I just looked at the images that seemed to work and found a threshold to use. The actual value may vary depending on the look you're doing for.
Presenting Data - Referee Crew Calls in the NFL
One of the great things about computers is their ability to take tabular data and turn them into pictures that are easier to interpret. I'm always amazed when given the opportunity to show data as a picture, more people don't jump at the chance.
For example, this piece on ESPN regarding the difference in officiating crews and their calls has some great data in it regarding how different officiating crews call games.
One thing I find a bit disconcerting is:
- ~~One of the rows is missing data so that row looks 'odd' in the context of the story and makes it look like the writer missed a big thing ... they didn't~~ (it's since been fixed)
- This tabular format is just begging to be displayed as a picture.
Perhaps the issue here is that the author didn't know how to best visualize the data to make his story, but I'm going to help him out.
If we start from the underlying premise that not all officiating crews call games in the same way, we want to see in what ways they differ.
The data below is a reproduction of the table from the article:
REFEREE DEF. OFFSIDE ENCROACH FALSE START NEUTRAL ZONE TOTAL
Triplette, Jeff 39 2 34 6 81 Anderson, Walt 12 2 39 10 63 Blakeman, Clete 13 2 41 7 63 Hussey, John 10 3 42 3 58 Cheffers, Cartlon 22 0 31 3 56 Corrente, Tony 14 1 31 8 54 Steratore, Gene 19 1 29 5 54 Torbert, Ronald 9 4 31 7 51 Allen, Brad 15 1 28 6 50 McAulay, Terry 10 4 23 12 49 Vinovich, Bill 8 7 29 5 49 Morelli, Peter 12 3 24 9 48 Boger, Jerome 11 3 27 6 47 Wrolstad, Craig 9 1 31 5 46 Hochuli, Ed 5 2 33 4 44 Coleman, Walt 9 2 25 4 40 Parry, John 7 5 20 6 38
The author points out:
Jeff Triplette's crew has called a combined 81 such penalties -- 18 more than the next-highest crew and more than twice the amount of two others
The author goes on to talk about his interview with Mike Pereira (who happens to be ~~pimping~~ promoting his new book).
While the table above is helpful it's not an image that you can look at and ask, "Man, what the heck is going on?" There is a visceral aspect to it that says, something is wrong here ... but I can't really be sure about what it is.
Let's sum up the defensive penalties (Defensive Offsides, Encroachment, and Neutral Zone Infractions) and see what the table looks like:
REFEREE DEF Total OFF Total TOTAL
Triplette, Jeff 47 34 81 Anderson, Walt 24 39 63 Blakeman, Clete 22 41 63 Hussey, John 16 42 58 Cheffers, Cartlon 25 31 56 Corrente, Tony 23 31 54 Steratore, Gene 25 29 54 Torbert, Ronald 20 31 51 Allen, Brad 22 28 50 McAulay, Terry 26 23 49 Vinovich, Bill 20 29 49 Morelli, Peter 24 24 48 Boger, Jerome 20 27 47 Wrolstad, Craig 15 31 46 Hochuli, Ed 11 33 44 Coleman, Walt 15 25 40 Parry, John 18 20 38
Now we can see what might actually be going on, but it's still a bit hard for those visual people. If we take this data and then generate a scatter plot we might have a picture to show us the issue. Something like this:

The horizontal dashed blue lines represent the average defensive calls per crew while the vertical dashed blue line represents the average offensive calls per crew. The gray box represents the area containing plus/minus 2 standard deviations from the mean for both offensive and defensive penalty calls.
Notice anything? Yeah, me too. Jeff Triplette's crew is so far out of range for defensive penalties it's like they're watching a different game, or reading from a different play book.
What I'd really like to be able to do is this same analysis but on a game by game basis. I don't think this would really change the way that Jeff Triplette and his crew call games, but it may point out some other inconsistencies that are worth exploring.
Code for this project can be found on my GitHub Repo
Dropbox Files Word Cloud
In one of my previous posts I walked through how I generated a wordcloud based on my most recent 20 tweets. I though it would be neat to do this for my Dropbox file names as well. just to see if I could.
When I first tried to do it (as previously stated, the Twitter Word Cloud post was the first python script I wrote) I ran into some difficulties. I didn't really understand what I was doing (although I still don't really understand, I at least have a vague idea of what the heck I'm doing now).
The script isn't much different than the Twitter word cloud. The only real differences are:
- the way in which the
wordsvariable is being populated - the mask that I'm using to display the cloud
In order to go get the information from the file system I use the glob library:
import glob
The next lines have not changed
import matplotlib.pyplot as plt
from wordcloud import WordCloud, STOPWORDS
from scipy.misc import imread
Instead of writing to a 'tweets' file I'm looping through the files, splitting them at the / character and getting the last item (i.e. the file name) and appending it to the list f:
f = []
for filename in glob.glob('/Users/Ryan/Dropbox/Ryan/**/*', recursive=True):
f.append(filename.split('/')[-1])
The rest of the script generates the image and saves it to my Dropbox Account. Again, instead of using a Twitter logo, I'm using a Cloud image I found here
words = ' '
for line in f:
words= words + line
stopwords = {'https'}
logomask = imread('mask-cloud.png')
wordcloud = WordCloud(
font_path='/Users/Ryan/Library/Fonts/Inconsolata.otf',
stopwords=STOPWORDS.union(stopwords),
background_color='white',
mask = logomask,
max_words=1000,
width=1800,
height=1400
).generate(words)
plt.imshow(wordcloud.recolor(color_func=None, random_state=3))
plt.axis('off')
plt.savefig('/Users/Ryan/Dropbox/Ryan/Post Images/dropbox_wordcloud.png', dpi=300)
plt.show()
And we get this:

Page 12 / 13