Monday, December 29, 2014

MATLAB Script: Dealing Cards

I played cards (poorly) over Christmas, and thought a good way to break through the end-of-year boredom at work would be to write a quick MATLAB script that dealt a given number of cards from a randomly shuffled deck to a given number of players.

I got to use the eval command again, which is always exciting.



% Randomly shuffle a deck of cards
% Distribute to a given number of players (user input)
% Display the hands for each player

% Assign a number for each card
% 1-13 are for Spades (in order)
% 14-26 are for Clubs
% 27-39 are for Diamonds
% 40-52 are for Hearts
card_names{1} = '2 of Spades';
card_names{2} = '3 of Spades';
card_names{3} = '4 of Spades';
card_names{4} = '5 of Spades';
card_names{5} = '6 of Spades';
card_names{6} = '7 of Spades';
card_names{7} = '8 of Spades';
card_names{8} = '9 of Spades';
card_names{9} = '10 of Spades';
card_names{10} = 'Jack of Spades';
card_names{11} = 'Queen of Spades';
card_names{12} = 'King of Spades';
card_names{13} = 'Ace of Spades';
card_names{14} = '2 of Clubs';
card_names{15} = '3 of Clubs';
card_names{16} = '4 of Clubs';
card_names{17} = '5 of Clubs';
card_names{18} = '6 of Clubs';
card_names{19} = '7 of Clubs';
card_names{20} = '8 of Clubs';
card_names{21} = '9 of Clubs';
card_names{22} = '10 of Clubs';
card_names{23} = 'Jack of Clubs';
card_names{24} = 'Queen of Clubs';
card_names{25} = 'King of Clubs';
card_names{26} = 'Ace of Clubs';
card_names{27} = '2 of Diamonds';
card_names{28} = '3 of Diamonds';
card_names{29} = '4 of Diamonds';
card_names{30} = '5 of Diamonds';
card_names{31} = '6 of Diamonds';
card_names{32} = '7 of Diamonds';
card_names{33} = '8 of Diamonds';
card_names{34} = '9 of Diamonds';
card_names{35} = '10 of Diamonds';
card_names{36} = 'Jack of Diamonds';
card_names{37} = 'Queen of Diamonds';
card_names{38} = 'King of Diamonds';
card_names{39} = 'Ace of Diamonds';
card_names{40} = '2 of Hearts';
card_names{41} = '3 of Hearts';
card_names{42} = '4 of Hearts';
card_names{43} = '5 of Hearts';
card_names{44} = '6 of Hearts';
card_names{45} = '7 of Hearts';
card_names{46} = '8 of Hearts';
card_names{47} = '9 of Hearts';
card_names{48} = '10 of Hearts';
card_names{49} = 'Jack of Hearts';
card_names{50} = 'Queen of Hearts';
card_names{51} = 'King of Hearts';
card_names{52} = 'Ace of Hearts';
% Shuffle the deck
shuffled_deck = randperm(52);
% Ask the user for # of players and # of cards per player
clc
num_players = input('How many players? Enter here: ');
num_cards = input('How many cards per player? Enter here: ');
disp('----------------------')
% Create empty player hand arrays
for k=1:1:num_players
  plyr_hnd_cmd_str = ['player_hand_',num2str(k), '= [];'];
  eval(plyr_hnd_cmd_str);
end
% Deal cards out to the players
total_cards_to_deal = num_players * num_cards;
i=1;
while i < total_cards_to_deal
    for j=1:1:num_players
        deal_card_cmd_str = ['player_hand_', num2str(j), ' = [player_hand_',num2str(j), ', shuffled_deck(i)];'];
        eval(deal_card_cmd_str);
        i = i+1;
    end    
end
% Display each player's hand
for k=1:1:num_players
    disp_plyr_str = ['Player ', num2str(k), '''s hand: '];
    disp(disp_plyr_str)
    for m=1:1:num_cards
        card_name_cmd_str = ['card_name_str = card_names{player_hand_', num2str(k), '(m)};'];
        eval(card_name_cmd_str)
        disp(card_name_str)
    end
    disp('----------------------')
end

Friday, December 19, 2014

MATLAB Script: Best MBA Course

Upon the completion of my MBA at BU, I wanted to figure out what my top 5 courses (out of 18) were.

So I wrote a brute-force, unsexy MATLAB script that did a round-robin style side-by-side comparison.

It worked out fairly well. I even put in a very rudimentary tiebreaker loop wherein the user is asked to break the tie. Basically a "head to head" comparison, which, upon further review, should be pre-determined, since the user had already done a head-to-head comparison of the two courses before. Room for improvement.

Here it is:



% Determines the ranking of best MBA courses taken
% The user picks the best course by choosing between all combinations
clc
clear
num_courses = 18;
% Create the list of courses
% Course 1: "OB 712: Managing Organizations"
course_names{1} = 'OB 712: Managing Organizations';
% Course 2: "AC 711: Accounting"
course_names{2} = 'AC 711: Accounting';
% Course 3: "MK 724: Marketing Management"
course_names{3} = 'MK 724: Marketing Management';
% Course 4: "QM 717: Statistics"
course_names{4} = 'QM 717: Statistics';
% Course 5: "FE 730: Economics"
course_names{5} = 'FE 730: Economics';
% Course 6: "IS 711: IT Strategies"
course_names{6} = 'IS 711: IT Strategies';
% Course 7: "OM 726: Operations"
course_names{7} = 'OM 726: Operations';
% Course 8: "SI 751: Strategy"
course_names{8} = 'SI 751: Strategy';
% Course 9: "PL 700: Law and Ethics"
course_names{9} = 'PL 700: Law and Ethics';
% Course 10: "ES 700: Executive Presentation"
course_names{10} = 'ES 700: Executive Presentation';
% Course 11: "OB 853: Negotiations"
course_names{11} = 'OB 853: Negotiations';
% Course 12: "OM 880: Product Design"
course_names{12} = 'OM 880: Product Design';
% Course 13: "MK 864: Pricing"
course_names{13} = 'MK 864: Pricing';
% Course 14: "MK 862: High-Tech Marketing"
course_names{14} = 'MK 862: High-Tech Marketing';
% Course 15: "IS 827: Platforms"
course_names{15} = 'IS 827: Platforms';
% Course 16: "OB 848: Leadership"
course_names{16} = 'OB 848: Leadership';
% Course 17: "MK 852: Marketing Analytics"
course_names{17} = 'MK 852: Marketing Analytics';
% Course 18: "SI 845: Technology Strategy"
course_names{18} = 'SI 845: Technology Strategy';
% ES 700 and FE 722 were not considered for ranking
% Create a 2x18 matrix of randomly determined combinations
rand_course_combs = rand_combs(num_courses);
%rand_course_combs = rand_combs2(num_courses); %Alternative algorithm that doesn't use Statistics Toolbox
len_rand_course_combs = length(rand_course_combs);
points_array = zeros(1,num_courses);
better_course = 0;
for i=1:1:len_rand_course_combs
    course_1_name = char(course_names(rand_course_combs(i,1)));
    course_2_name = char(course_names(rand_course_combs(i,2)));
    prompt_str = [course_1_name, ' (1) vs. ',course_2_name, ' (2): '];
    clc
    pct_cmplt = 100*((i-1)/len_rand_course_combs);
    pct_cmplt_str = [num2str(pct_cmplt), '% complete'];
    disp(pct_cmplt_str)
    disp('Which one was better?');
    better_course = input(prompt_str);
       if better_course == 1
           points_array(rand_course_combs(i,1)) = points_array(rand_course_combs(i,1)) + 1;
       elseif better_course == 2
           points_array(rand_course_combs(i,2)) = points_array(rand_course_combs(i,2)) + 1;
       end
end
% Sort, rank, and display
sorted_points_array = sort(points_array,'descend');
len_points_array = length(points_array);
rank_array = zeros(1,len_points_array);
i=1;
while i <= len_points_array
   rank_array_temp = find(points_array == sorted_points_array(i));
   if length(rank_array_temp) > 1
       % Assumes only two values will be "tied". Should probably make this
       % more robust.
       % Break the tie by prompting user to pick the better option
       disp('Found a tie!')
       disp('Need to break it!')
       disp('Which one was better?')
       course_1_name = course_names{rank_array_temp(1)};
       course_2_name = course_names{rank_array_temp(2)};
       prompt_str = [course_1_name, ' (1) vs. ',course_2_name, ' (2): '];
       tiebreaker_choice = input(prompt_str);
       if tiebreaker_choice == 1
      rank_array(i) = rank_array_temp(1);
      rank_array(i+1) = rank_array_temp(2);
       else
        rank_array(i) = rank_array_temp(2);
        rank_array(i+1) = rank_array_temp(1);
       end
       i=i+2;
   else
   rank_array(i) = find(points_array == sorted_points_array(i));
   i=i+1;
   end
end
len_rank_array = length(rank_array);
for j = 1:1:len_rank_array
    clc
   disp_str = [num2str(j), ': ',course_names{rank_array(j)}, ' : ',num2str(sorted_points_array(j)), ' points.']; 
    disp(disp_str)
end



Here were the results:

1: OM 880: Product Design : 17 points.
2: IS 827: Platforms : 16 points.
3: OB 853: Negotiations : 15 points.
4: MK 862: High-Tech Marketing : 14 points.
5: MK 852: Marketing Analytics : 13 points.
6: MK 724: Marketing Management : 12 points.
7: SI 751: Strategy : 11 points.
8: FE 730: Economics : 10 points.
9: AC 711: Accounting : 9 points.
10: SI 845: Technology Strategy : 8 points.
11: OB 712: Managing Organizations : 7 points.
12: MK 864: Pricing : 6 points.
13: ES 700: Executive Presentation : 4 points.
14: OM 726: Operations : 4 points.
15: OB 848: Leadership : 3 points.
16: IS 711: IT Strategies : 2 points.
17: QM 717: Statistics : 2 points.
18: PL 700: Law and Ethics : 0 points.

Thursday, July 3, 2014

Improved Script

I decided to get a little fancy with the "random PDF" script from my last post.

I modified the random selection element to be weighted based on age. I decided to start simple by simply dividing file age (in days) by 10 (I'll try using the log next), rounding up (using math.ceil) and randomly selecting from a new array (called "weighted_files") which gives more weight to older files. Basically: I want it to be more likely that an older file is opened rather than a newer file. My reading model has now shifted to a FIFO scheme.

Here it is, with a few comments.


#! /usr/bin/python
import time, os, math, sys, shutil
from random import randint
clear = lambda: os.system('cls')
clear()
path = '[DIRECTORY WITH PDF FILES IN IT]'

pdf_files = [f for f in os.listdir(path) if f.endswith('.pdf')]

len_pdf_files = len(pdf_files)
if len_pdf_files == 0:
print ("No PDF files found!")
time.sleep(3)
sys.exit(0)

pdf_ages = []

for pdf_file in pdf_files:
os.rename(os.path.join(path, pdf_file), os.path.join(path, pdf_file.replace(' ', '_')))
file_loc = path + '/' + pdf_file
file_age_sec = time.time() - os.path.getmtime(file_loc)
file_age_days = file_age_sec/60/60/24
file_age_days_round = math.floor(file_age_days)
pdf_ages.append(file_age_days_round)
# Build an array for use in weighted random selection
weighted_files = []
weighting_factor = 10 # arbitrary
for index, pdf_age in enumerate(pdf_ages):
weight = math.ceil(pdf_age/weighting_factor)

# Adds the index number (from pdf_ages, which should match pdf_files) in proportion to the weight
for i in range(0,weight):
weighted_files.append(index) 

len_weighted_files = len(weighted_files)
rand_int = randint(0,len_weighted_files-1)
pdf_files_index = weighted_files[rand_int]
file_name_str = str(pdf_files[pdf_files_index])

file_loc_str = path + '/' + file_name_str
pdf_cmd_str = "start " + file_loc_str
os.system(pdf_cmd_str)
valid_command = 0
while valid_command == 0 :
delete_file = input("Delete file? Y or N: ")
if (delete_file == "Y" or delete_file == "y"):
os.remove(file_loc_str)
print (file_name_str + " has been deleted! Good work!")
new_num_pdfs = len_pdf_files - 1
new_num_pdfs_str = str(new_num_pdfs)
print ("There are now ",new_num_pdfs_str," PDFs remaining.")
time.sleep(3)
valid_command = 1
sys.exit(0)
elif (delete_file == "N" or delete_file == "n"):
print ("Fine then, but next time, delete it!")
dest_folder_loc_str = 'D:/Users/a27m8mt/Documents/Things_To_Read/Hold_for_later/'
shutil.move(file_loc_str,dest_folder_loc_str)
print (file_name_str + " has been moved to the holding folder: " + dest_folder_loc_str)
time.sleep(3)
valid_command = 1
sys.exit(0)
else:
print ("Invalid command. Try again.")

Tuesday, July 1, 2014

Python Script for Opening a Random PDF in a Given Directory

I decided to try learning Python, so I came up with a script of nominal utility. It's probably quite ugly, but it works.

It opens a random PDF from a directory full of PDFs that I've accumulated over the past year or so of articles I've been meaning to read. At the end, it prompts the user to save or delete the file. It's quite cathartic.

If you have a similar problem, I hope this helps clean up your life a bit. Red text is where you fill in the blanks. If you're using Windows, make sure to use forward slashes for the path (e.g. 'D:/users/something/something')


import os, sys, time
from random import randint
# Clear the command window
clear = lambda: os.system('cls')
clear()
# Set the path with all of the PDF files
path = '[directory with all of the files]'
# Create a list of all of the PDF files
pdf_files = [f for f in os.listdir(path) if f.endswith('.pdf')]
# Clean up the names by removing spaces
for pdf_file in pdf_files:
os.rename(os.path.join(path, pdf_file), os.path.join(path, pdf_file.replace(' ', '_')))
len_pdf_files = len(pdf_files)
# Generate a random number
rand_int = randint(0,len_pdf_files)
# Get the name of a random PDF file
file_name_str = str(pdf_files[rand_int])
file_loc_str = path + '/' + file_name_str
# Generate a string to be used to open the file
pdf_cmd_str = "start " + file_loc_str # Windows uses "start"
# Open the file
os.system(pdf_cmd_str)
# Prompt the user to delete the file
delete_file = input("Delete file? Y or N: ")
if delete_file == "Y":
os.remove(file_loc_str)
print (file_name_str + " deleted! Good work!")
# Give the user a few seconds to read the statement
time.sleep(3)
sys.exit(0)
else:
# Chastise them for being a hoarder, while enabling them
print ("Fine then, but next time, delete it!")
time.sleep(3)
sys.exit(0)

Monday, October 7, 2013

TED: Michael Porter on the role of business in solving social problems

Nothing really ground-breaking, but I like Porter's points.


Thursday, August 8, 2013

Prisoner's Dilemma Example and the Coalition Game

A great example of the Prisoner's Dilemma via kottke.org

 


Note Nick's insistence upon picking "Steal" right away, which put Ibraham in the position of taking a serious leap of faith. I was surprised by the ending.

I actually had a similar experience last night in my Negotiations class. We played "The Coalition Game," which went like so:

Three teams (A, B, and C) discuss how to split a pot of money. The pot of money can only be split between two teams (the coalition), leaving one team out. The pot of money to be split depends on which two teams form the coalition. The pot cannot be split evenly, and neither team can accept less than $0.01.

If Team A and Team B form a coalition, they split $20.

If Team A and Team C form a coalition, they split $16.

If Team B and Team C form a coalition, they split $12.

A coalition is formed only when two teams, after 9 rounds of one-on-one negotiations (order is below), agree on how to split the pot with one other team. After the final round of discussions, each team, individually and secretly, submits a slip of paper with their perceived agreement with another team. If none of the slips match, there is no coalition, and every one walks away with nothing.

Each team negotiates with one other team at a time, with the other team outside the room.

The order of negotiations is:

AB
AC
BC
(repeated twice more)

I was on Team A. We started off by offering Team B $13, with us taking $7.

We ended up outside the coalition at the end. It was a good exercise in trust and simple human greed.

Saturday, June 22, 2013

The Police - "Invisible Sun"

One of my favorite songs by The Police. The video is unavailable online in the U.S. RSS folks: click through if you can't see the video below.

Monday, June 10, 2013

Function Version: Determining Effective APY

In case you're more into functions:


function effective_apy_midpoint = effective_APY_midpoint(P,Y,n)

% Determines the effective annual yield (APY) given a principal amount, 
% a final amount, and number of years.

% Need to calculate r (APY)
% Use mid-point algorithm
% Formula is Y = P(1+r)^n

% Assume range of r from 0 to 5 (0 % to 300%)

r_min = 0;
r_max = 5;
current_guess = r_min;
Y_temp = P*(1+current_guess)^n;
num_guesses = 1;
while Y_temp ~= Y
    if (Y_temp > Y*0.9999) && (Y_temp < Y*1.0001)
        break
    end
    if Y_temp < Y
    next_guess = (current_guess + r_max)/2;
    else next_guess = (r_min + current_guess)/2;
    end
    Y_temp = P*(1+next_guess)^n;
    if Y_temp < Y
        r_min = next_guess;
    else r_max = next_guess;
    end
    current_guess = next_guess;
    num_guesses = num_guesses + 1;
    Y_temp = P*(1+current_guess)^n;
end
effective_apy_midpoint = current_guess;

MATLAB Script: Determining Effective APY

It's ugly, and I'm not sure if "midpoint algorithm" is the actual name of the algorithm. I remember doing this for my Numerical Methods course.



% Determines the effective annual yield (APY) given a principal amount, 
% a final amount, and number of years.
clear
clc
P = input('Enter principal (beginning) amount: ');
Y = input('Enter final (ending) amount: ');
n = input('Enter number of years: ');

% Need to calculate r (APY)
% Use mid-point algorithm
% Formula is Y = P(1+r)^n

% Assume range of r from 0 to 5 (0 % to 300%)

r_min = 0;
r_max = 5;
current_guess = r_min;
Y_temp = P*(1+current_guess)^n;
num_guesses = 1;
tic
while Y_temp ~= Y
    if (Y_temp > Y*0.9999) && (Y_temp < Y*1.0001)
        break
    end
    if Y_temp < Y
    next_guess = (current_guess + r_max)/2;
    else next_guess = (r_min + current_guess)/2;
    end
    Y_temp = P*(1+next_guess)^n;
    if Y_temp < Y
        r_min = next_guess;
    else r_max = next_guess;
    end
    current_guess = next_guess;
    num_guesses = num_guesses + 1;
    Y_temp = P*(1+current_guess)^n;
end
toc
r = current_guess;
effective_apy = r*100;
output_str = ['Effective APY = ',num2str(effective_apy),'%'];
num_guess_str = ['With ',num2str(num_guesses),' guesses.'];
disp(output_str)
disp(num_guess_str)

Wednesday, June 5, 2013

Product Idea: Restaurant-Provided Credit Card Roulette

As a male in his mid 20s, I have come across the "Credit Card Roulette" phenomenon a few times. Personally, I think it's better to just alternate picking up the check, but I can see the appeal of random chance when it comes to paying for a meal.

For those unfamiliar with the game, the basic form involves all diners in a party throwing their credit cards in a hat or similar container. A random credit card is drawn from the hat, sometimes by the server to maintain objectivity, and that card is used to pay for the entire meal. Usually this game is played at the end of the meal to prevent people from ordering more food than they normally would if they were paying for it themselves.

But that particular moral hazard got me thinking: what if restaurants provided a system that would let patrons play Credit Card Roulette before the meal began in exchange for a 10% discount off the entire meal? The restaurant would stand to gain, of course, if non-paying diners ordered more than 10% of what they would normally order.

I'm sure some clever game designer could figure out exactly what the interface to diners would be (kiosk at the table, smartphone app, etc.), but the concept would cater well to the gambling culture in America.


Benefits:

- At worst, the losing player would be treating their friends or colleagues to a meal they enjoyed. And most gamblers just like the adrenaline rush, right?

- Payment would be easier and faster if it's all going on one card (good for diners and the restaurant).

- Losing diners would be encouraged to return, thinking they wouldn't have to pay again because of the general misunderstanding of probability (see: the lottery). That's good for the restaurant.


Nobody really loses.

Tuesday, May 28, 2013

2013 Monaco Grand Prix Finishing Position vs. 2013 Driver Salary

I've only watched a few Formula One races, but I did manage to catch this weekend's Grand Prix de Monaco. Beautiful and exciting race, and only about 100 minutes long for those with things to do.

I did a quick regression on finishing position vs. 2013 driver salary. It's only one race, so there's bound to be some discrepancies (there were a couple accidents and one engine fire). However, the correlation is still quite moderate (R=0.613) andit makes the case for a more egalitarian approach to driver salaries. Alas: there's more to racing than just one race, and more to racing than, well: racing.

To note: one would expect a fairly strong downward-sloping curve if drivers were paid based on the results of just this one race.

I'll do another regression on 2013 World Series final results vs. salary.

Salary data (via Reddit) courtesy of Crash.net.



And when you log the salaries:




The best fit, as one may expect, is exponential:




Sunday, February 17, 2013

Video: A Case for Open Data in Transit

As a user of open data from the MBTA (I'm a big fan of NextBus), I can't agree more with the movement towards opening as much data as possible to developers for urban transit initiatives. If done right, it encourages expanded ridership and can serve to make the services more efficient.

I think O'Reilly's point in the video of government serving as a platform for private development is startlingly obvious and insightful.


Saturday, February 9, 2013

The Restaurant Service Auction Market

A recent story about a cheap pastor in St. Louis who got an Applebee’s server fired for uploading a copy of the pastor’s receipt (the pastor didn’t like the mandatory 18% tip for a group of 6, so she stiffed the server citing religious reasons) got me thinking about the Principal-Agent Problem again. I won’t dwell on it too much, but two quick things: 

First off, the obvious: doesn’t 10% seem a little low for a pastor? Why bother giving that figure, anyway? 

Secondly, punishing the server for a (disclosed and commonly applied) mandatory tip is a textbook example of the perils of the Principal-Agent Problem in the food service industry. The pastor should have gone directly to the manager to complain about the policy, but decided to take it out on the lowly server.

The fact that a religious figure was involved is a large part of why this story got out, but the fact of the matter is: the pastor should have taken out her frustration in another manner. It’s not like people blame her for bad weather events (a.k.a. Acts of God), right? 

Anyway, this sort of customer behavior can be avoided through a market approach. That is why, if I were to open a restaurant, I’d try something like this: 

  • All tips are set at a minimum of 15% and are pooled among all restaurant staff and there is no cap (i.e. a customer could bid for a 100% tip if they so desired)
  • Additional preferred treatment can be arranged by bidding (in units of % of the bill as total tip), such as: 
    • Personal food/drink recommendations
    • Faster service (this would be automatically controlled by the kitchen and bar – servers may game the system by holding up orders to appear like service to other tables is faster; customers can bid on when their meals or drinks come out) 
      • The estimated time of arrival of the meal or drink must be known to the customer for transparency. 
    • Requests for special occasions (birthdays, engagements, etc.) 
  • Customers are held to their promised tip amount unless the promised service at the time of the bid is not met (e.g. the food arrives a few minutes late) 
  • The price for a “bid for preferential treatment” is based on supply and demand. For instance, if it’s a busy Friday night with lots of people bidding, a 1% increase in tip won’t get you to the top of the list. 
    • Customers would only know if there is someone ahead of them and will be told by the system the current price (in % of tip) of a bid for a given request 
    • Someone who does not bid must be guaranteed some reasonable level of service. You might think of this as the “floor.” 
  • Standard quality must be maintained no matter how "rushed" a customer wants their food. This gets tricky if people want their food to be cooked faster than good quality would allow. This would need to be factored into resource constraints when telling the customer how quickly the food can be delivered to their table.


I'm not sure how well it would work (i.e. how long I'd stay in business), but it would be a good dining experiment.

Friday, November 9, 2012

Data Crunching, Election Edition

I'm getting more into data analytics, and have been looking for some substantial examples beyond the usual "more and more companies are using analytics" blanket statements from Harvard Business Review. Much to my relief, Time came out with a nice piece about the Obama campaign's use of data in their recent electoral victory.

Full disclosure: I'm a libertarian and was in the 1% of the population that voted for Gary Johnson. Living in Massachusetts, my vote didn't matter anyway. I also voted for Scott Brown for Senate because he is a moderate, though his campaign was poorly managed.

Anyway, the article points out some interesting bits about how the Obama campaign pulled off the win. Through massive computer simulations, data crunching, targeted ads, and efficient door-knocking, they were able to raise $1 billion. That is impressive no matter how you spin it, folks. One can only imagine what upcoming local elections will have.

I have a friend who works in the (spinning down post-election) Elizabeth Warren campaign. He's more on the database-building-mechanics side of things, but he was able to talk about how the campaign used the data to target individual voters. My bet: the scale of analytics used in this year's national election (per voter) will be used at the local level in 2-4 years.

Monday, November 5, 2012

Delivery Time Improvement Auction Market

In high school and during college breaks, I worked at a local pizza place (as I've mentioned all too frequently here). As an overzealous engineering student, I was always looking for ways to do things more efficiently. One of our biggest problems: inefficient allocation of human capital (a.k.a. delivery boys).

Alas, I never had the time to come up with anything fancy, until now (years later). I decided to start a pet project to develop a web-based "Pizza Delivery Route Optimizer" using as much open source software as possible.

I'm still designing the system, and don't plan on selling it, but a thought occurred to me:

What if people could bid in a live auction on how quickly their orders could arrive? Let's say it's a busy Friday night, and you want your pizza there in 30 minutes or less. In a fair world, even with a fancy Pizza Delivery Route Optimizer running, this would be an unrealistic expectation.

But what if it weren't? What if you could see approximately when your order would arrive, how many orders were ahead of you, and could either bid against other orders to move up in the pecking order or pay a small fee to do the same? What would be a fair price? I'd imagine it would be tailored to current supply (number of drivers) and demand (number of orders). For example, if there are 20 orders ahead of you and only 15 drivers on staff, the fee would be higher than it would be if there were 10 orders and 12 drivers.

To do an auction, the customer would need to set up an account and would only be billed if their expected (not actual) delivery time were made earlier. It'd be unfair to the delivery guy (and probably unsafe) to guarantee a time of arrival. Remember Domino's "30 Minute Guarantee"? Domino's ended it when a delivery guy killed someone trying to make it in the 30 minute window.

I think there may be a market for the auction format, but the implementation would be tough. For one, you wouldn't likely have people stay on a website to actually bid in real time (they are, after all, not willing to spend the time picking the pizza up in the first place). Therefore, a simple fee would probably be easier to implement. This would, however, not necessarily do well for the delivery guy, since the customer would feel no reason to tip the delivery guy if they've already paid more for the delivery in the first place. Perhaps you could split the fee with the delivery guy? It's a tough one to do if you want things to be fair to all parties.

Monday, October 29, 2012

2012 MLB Dollars Per Win

With the San Francisco Giants' sweep of the Detroit Tigers last night, the 2012 MLB Season is officially in the books.

As I've done the past few years, I broke things down in simple terms: which teams were the most efficient in getting the most value per dollar in payroll? Yes, this isn't really a straightforward answer, since teams with higher payrolls tend to have higher non-baseball revenues (namely merchandising and TV deals), but from a pure baseball standpoint: who had the best year?

Below is a simple table of how teams ended up in terms of dollars in payroll vs. total wins (including post-season). The Oakland Athletics, a perennially efficient team, did extremely well this year, spending nearly one fifth as much per win as the lowly Red Sox. Not to be forgotten was the sub-par performance of the Phillies this season.




Source for payroll amounts: http://www.cbssports.com/mlb/salaries

The World Series Champion San Francisco Giants ended up in the middle of the pack due to their above average payroll, though one could argue winning it all is worth it. I would also credit the Washington Nationals for being the only other 100 win team while having a below average payroll; great year for the Natinals.

Just for your edification, here is the correlation between wins and payroll this year. Note how low R^2 is: 0.053. I'd have to look at other years, but this year turned out to be quite a year of parity.


Saturday, October 20, 2012

Nissan's New "Steer By Wire" Technology

Before watching the video, I wasn't sure what Nissan was trying to accomplish. I didn't know of any market demand for this technology. After viewing the video and learning of the "smooth ride" capabilities, I was hooked. I can't wait for this to arrive in all cars.

I'm assuming the clutch also serves as the mechanical back-up in the event of a loss of power to the control units; the video seems to state that. That would be a major concern from a safety standpoint.

This also plays right into my dream of driverless cars. With this new system, all you'd need to do to make the vehicle driverless (assuming you completely remove the human driver) is ditch the steering wheel and clutch and plug the control unit into a master vehicle controller, which would also control throttle and brake. No more goofy actuators on the steering wheel; full-authority steering!