I'm Max Mautner and I'm your man!

Pythonista in ad-tech--you can reach me at max@mustknow.io or @maxmautner

  • About
  • Links
  • Running
  • youtube
  • archive
  • 2014 - 275, 2015 - ?

    • 2 days ago
    • 1 notes
  • inercia:

An overview of the different Linux performance tools and the subsystems they work on.

    inercia:

    An overview of the different Linux performance tools and the subsystems they work on.

    Source: inercia
    • 4 months ago
    • 37 notes
  • Scraping w/ Celery in 6 mins

    • 5 months ago
    • #python
    • #ipython
    • #celery
    • #python programming
    • #tutorial
  • A short demo screencast I made about how to use python’s Celery library in a basic way

    • 5 months ago
    • #python
    • #ipython
    • #celery
    • #concurrency
    • #programming
  • Making database-backed dashboards in 3 minutes

    I’ll show you how to make a database-backed dashboard in under 3 minutes. First, install the following:

    • pandas (>=0.14.1) + matplotlib
    • the database driver of your choice (e.g. MySQLdb, psycopg2—whichever driver corresponds to the database your data lives in)

    With the prerequisites packages installed, we can populate a pandas DataFrame from read_sql:

    In [1]:
    import MySQLdb
    from pandas.io.sql import read_sql
    import pandas as pd
    
    db_connection = MySQLdb.connect(read_default_file='~/.my.cnf')
    query = """\
    SELECT 
        date(created_at) as date, 
        count(*) as count
    FROM events
    GROUP BY 1"""
    df = read_sql(query, db_connection)
    
    /usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pytz/__init__.py:29: UserWarning: Module argparse was already imported from /usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.pyc, but /usr/local/lib/python2.7/site-packages is being added to sys.path
      from pkg_resources import resource_stream
    
    
    In [2]:
    df.head() # taking a peak at the data
    
    Out[2]:
    datecount0 2014-01-02 111 2014-01-03 72 2014-01-05 953 2014-01-19 354 2014-02-09 11

    Sweet, we have some data, whoa!

    In [3]:
    df.plot()
    
    Out[3]:
    <matplotlib.axes.AxesSubplot at 0x112921f90>
    

    Whoops, we aren’t making use of a datetime index so

    1. We don’t have datetime ticks on the X-axis
    2. We aren’t seeing the “holes” in our timeseries data

    So let’s solve both of those:

    In [4]:
    df.date = pd.to_datetime(df.date)
    df.set_index('date', inplace=True)
    df = df.reindex(pd.date_range(min(df.index), max(df.index)), fill_value=0)
    
    In [5]:
    df.plot()
    
    Out[5]:
    <matplotlib.axes.AxesSubplot at 0x11293a210>
    

    Wow. We’re pretty much done. Oh yeah, let’s make this dashboard available—1) via web app, 2) via email.

    1. serving a dashboard via web app

    We’ll use Flask—this is what there is to it:

    In []:
    #!/usr/bin/env python
    
    from flask import Flask, make_response
    from cStringIO import StringIO
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    import matplotlib.pyplot as plt
    
    import MySQLdb
    from pandas.io.sql import read_sql
    import pandas as pd
    
    app = Flask(__name__)
    db_connection = MySQLdb.connect(read_default_file='/Users/max/.my.cnf.snailbo')
    
    @app.route('/')
    def index():
        return """\
    <html>
    <body>
    <img src="/plot.png">
    </body>
    </html>"""
    
    @app.route('/plot.png')
    def plot():
        query = """\
    SELECT
        date(created_at) as date,
        count(*) as count
    FROM events
    GROUP BY 1"""
        df = read_sql(query, db_connection)
        df.date = pd.to_datetime(df.date)
        df.set_index('date', inplace=True)
        df = df.reindex(pd.date_range(min(df.index), max(df.index)), fill_value=0)
    
        df.plot()
        canvas = FigureCanvas(plt.gcf())
        output = StringIO()
        canvas.print_png(output)
        response = make_response(output.getvalue())
        response.mimetype = 'image/png'
        return response
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    If you run this as a script, navigate to http://localhost:5000/ in your browser, you should see this:

    blah

    Awesome.

    2. send the “dashboard” as an email

    We could also embed this chart in an email—perhaps using cron to send it to ourselves every X hours?

    Here’s how:

    In [6]:
    #!/usr/bin/env python
    # http://stackoverflow.com/a/920928
    
    import smtplib
    from email.MIMEMultipart import MIMEMultipart
    from email.MIMEText import MIMEText
    from email.MIMEImage import MIMEImage
    from cStringIO import StringIO
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    import matplotlib.pyplot as plt
    import MySQLdb
    from pandas.io.sql import read_sql
    import pandas as pd
    
    sender = 'bob@example.com'
    recipient = 'bob@example.com'
    
    # Create the root message and fill in the from, to, and subject headers
    msg_root = MIMEMultipart('related')
    msg_root['Subject'] = 'test message'
    msg_root['From'] = sender
    msg_root['To'] = recipient
    msg_root.preamble = 'This is a multi-part message in MIME format.'
    
    # Encapsulate the plain and HTML versions of the message body in an
    # 'alternative' part, so message agents can decide which they want to display.
    msg_alt = MIMEMultipart('alternative')
    msg_root.attach(msg_alt)
    
    msg_text = MIMEText('This is the alternative plain text message.')
    msg_alt.attach(msg_text)
    
    # We reference the image in the IMG SRC attribute by the ID we give it below
    msg_text = MIMEText("""\
    <html>
    <body>
    <h1>awesome sauce</h1>
    <img src="cid:image1">
    <br>Nifty!
    </body>
    </html>""", 'html')
    msg_alt.attach(msg_text)
    
    # Create the chart
    query = """\
    SELECT
    date(created_at) as date,
    count(*) as count
    FROM events
    GROUP BY 1"""
    db_connection = MySQLdb.connect(read_default_file='/Users/max/.my.cnf.snailbo')
    df = read_sql(query, db_connection)
    df.date = pd.to_datetime(df.date)
    df.set_index('date', inplace=True)
    df = df.reindex(pd.date_range(min(df.index), max(df.index)), fill_value=0)
    
    df.plot()
    canvas = FigureCanvas(plt.gcf())
    output = StringIO()
    canvas.print_png(output)
    msg_image = MIMEImage(output.getvalue())
    
    # Define the image's ID as referenced above
    msg_image.add_header('Content-ID', '<image1>')
    msg_root.attach(msg_image)
    
    server = smtplib.SMTP('smtp.example.com')
    server.ehlo()
    server.starttls()
    server.login(sender, 'mypassword')
    server.sendmail(sender, recipient, msg_root.as_string())
    server.quit()
    
    Out[6]:
    (221, '2.0.0 closing connection y7sm19409562qai.30 - gsmtp')
    

    Lookin’ good:

    plot

    addendum

    There’s a few things we didn’t do that would’ve been extra-nifty:

    • use SQLAlchemy w/ pandas’ read_sql method—you can make some extremely “configurable” dashboards going in this direction
    • use Vincent or Bearcart instead of matplotlib (2 libraries created by the great Rob Story of datapad.io)
    • and a whole multitude of other visualizations besides a basic linechart

    Drop me a line if you’ve got suggestions for other posts!

    • 5 months ago
    • 1 notes
    • #python
    • #pandas
    • #matplotlib
    • #ipython
    • #data
  • Forms in emails

    I heard about a business named RebelMail a couple weeks back that offers a product to eCommerce stores: email templates that contain forms for customers to complete abandoned purchases from w/in their email clients(!!!). How the hell do they do it?

    Well it turns out that modern email clients (e.g. www.gmail.com, your iPhone’s email program, etc.) are browser-ish HTML-rendering environments—why wouldn’t they render a <form> tag?

    Well, these browser-ish environments have a lot of security concerns. Neither you or your email provider wants your data being stolen, and there are a lot of possible ways for bad guys to accomplish—most have to do with injecting content into your browser-ish environment from remote resources by embedding it in the body of an HTML email.

    But <form> tags? They’re pretty harmless—there’s nothing inherently “dynamic” or unsafe about them besides the submit button, which directs the user away from the current page and to a URL designated by the form. Here’s an example I sent to my Gmail account:

    On clicking the “Go!” button, the values of the form fields are populated in the URL as GET query parameters. I haven’t rigorously tested support for forms across email clients or if the experience can be “gracefully degraded” when they’re not supported (please let me know if you do!).

    You can check out the python code I used to send these form emails on Github.

    • 5 months ago
    • 1 notes
    • #email
    • #python
    • #ux
    • #userexperience
    • #marketing
    • #remarketing
    • #emailmarketing
  • Boston Data Science Meetup: March 2014
    http://nbviewer.ipython.org/github/mmautner/email_classifier/blob/master/gmail_importance.ipynb

    "Building your own Priority Inbox"

    A talk I gave in March about demoing an email “priority” classification model and putting it to use.

    • 6 months ago
  • Great Slides About Mobile Monetization

    Jon radoff mobile_models_looking from Jon Radoff
    • 6 months ago
    • #monetization
    • #mobiledev
    • #economics
  • Demand Media's Planet of the Algorithms
    Every week, according to Peter Handsman, the former CTO, Reese would come up with an idea for something new to peddle. They would draft a business plan, launch a website, and measure consumers’ subsequent interest in a product. Efforts to sell coins and watches failed. At one point, Reese tried manufacturing family portraiture using inexpensive subcontractor artists in places such as Russia. The concept wasn’t easy to expand. “A lot of people have ideas,” says Handsman. “Byron has the discipline to actually measure them. He was willing to come up with a ridiculous number of ideas, but he was also willing to abandon them if they were proven not to work.”
    • 6 months ago
  • Amazing weekend in Montreal—hanging out with cool folks from a bunch of different walks of life, got to see/meet famous Python folks and am excited by what I see being accomplished: “combinatorial innovation”
    Really cool stuff :)

    • 8 months ago
    • #PyCon
    • #python
    • #Montreal
© 2013–2015 I'm Max Mautner and I'm your man!
Next page
  • Page 1 / 5