class: center, middle, inverse, title-slide # Sharps and Squares ## Sports Betting 101 ### Cory Waters ### 2019-03-21 --- class: center background-position: 50% 80% background-image: url(images/squares_sharps.svg) # What do Polygons have to do with sports betting? **Sharps**: Sophisticated professional sports bettors. Sharps are long term winners. **Squares**: Bettors that apply no systematic approach to wagering, relying on gut instincts, silly trends, and the opinions of others. Sharps and Squares exist on a spectrum, there's "sharp squares" and "square sharps." --- class: inverse, middle ### Who Am I? - Worked for an offshore sportsbook from 2004-2017 - Currently work for a political consultant --- ### What's this talk not about? - Casino - Horse Racing - Poker - DFS - E-sports -- ### What's this talk about? - How sportsbooks' earn money off squares. - How sharps beat books. --- class: center, inverse .pull-left[ ![](images/Richard_Feynman.jpg) > "You can know the name of that bird in all the languages of the world, but when you're finished, you'll know absolutely nothing whatever about the bird. You'll only know about humans in different places, and what they call the bird. … I learned very early the difference between knowing the name of something and knowing something." \- Richard Feynman ] .pull-right[ ![](images/David_Sklansky.jpg) > "..., it's my belief that many complex subjects invite semi-dummies. Obviously, "dummies" is an exaggeration. But many endeavors are so complex that just understanding all the "rules" makes a person look smarter than he is. Economics, derivatives trading, medical research, football coaching, and conducting wars are all good examples." \- David Sklansky ] ### Information is Valuable - Sharps don't share. - Once information is in the public domain it has little to no value. - Models and +EV plays come and go. Being sharp is how you think about problems. ![](images/books.png) --- ### What's a Sportsbook? - A place where you can place wagers on sporting events. Nevada, up till recently, was the only state with legal walk-in sportsbooks. - Most bettors in the US play at offshore sportsbooks that operate in the "gray" area. -- - Two types of sportsbooks 1. Credit books 1. Post-Up books -- + People bet a lot more on credit. An 25 dollar average post-up bettor usually bets between 100 to 150 on average on credit. - All legal sports betting operations are post-up. --- class: center, bottom, inverse ### What's it like Working at a Sportsbook? .pull-left[ When I tell people I worked at a sportsbook they usually think of this. ] .pull-right[ ![What people think](images/casino.jpg) ] --- ### This is the reality ![The reality](images/tps_reports.jpg) --- ### Sportsbooks are like Every Other Internet Business Sportsbooks deal with most of the same standard data science problems most e-commerce businesses face. -- - Dealing with customer churn. -- - Optimizing customer signups via A/B testing. -- - Forecasting the number of players for the following year for marketing and budgetary decisions. -- - Preventing fraud. Usually credit card, but preventing bonus abuse is important too. -- - Detecting web access anomalies, usually to prevent "bot" access. -- But that's not why you're here. --- ### Can You Make Money Betting Sports - Short Answer: Yes - How? Easy, just make +EV bets. - How do I find +EV bets? That's hard.  -- ### How Profitable is Sports Betting? #### A quick case study. We create four simulated "bettors'." The only difference between each simulation is the winning percentage - All bets at standard **-110** - The four win rates are `0.5, 0.53, 0.55, 0.6` - Starting bankroll: 10,000 - Number of bets: 1,000 - Number of simulations per win percent: 100 - Bet size: 2% of bankroll (should use Kelly but KISS) --- class: center ### Bettor Simulation  --- ### Results  --- ### Simulation Takeaways - The difference between being a loser and winning millions is picking one more game correct than chance out of ten selections. - Break-even at **-110** is 52.4% hitting 53% is still enough to earn a decent living. - The higher your winning percentage the less uncertainty about your long term earnings. - If you're hitting 55%+ of your bets your biggest problem is going to be finding places that will take your action. --- ### Isn't 1000 bets a lot? There can't be 1000 good bets in a year? Let's do some simple math. How many bets on US sports are possible per year? -- |League|Teams|Regular Season Games|Possible Bets | --- |--- | --- | --- |MLB | 30 | 162 | 29160 |NBA | 30 | 82 | 22140 |NFL | 32 | 16 | 4608 |NHL | 31 | 82 | 15252 |FBS | 130|12 | 9360 |NCAA BB | 351 |30| 63180 A total of **143,700** possible bets. -- - Odds provider Betradar advertises it offers 220K events per year. - Doesn't include, futures, props, live betting. Or, other sports like WNBA, European soccer, tennis, NASCAR, golf, etc. --- ### How do Sportsbooks Make Money? #### Sportsbooks Don't Take Sides - Despite what you might have read in "Freakonomics," books' don't have opinions. -- - Books make money by taking a commission 1 from losing players' when they place a bet. -- - For the standard point spread bet most people think of the commission is usually 10% of the amount of the bet. It's common to see the odds listed as **-110**. This means you need to risk for 110 dollars to win 100. -- - Two models for business. Take all the square action you can and ruthlessly kick out the sharps. Or, take sharp action and make money on the massive volume of bets. Over 95% of the books follow model one. .footnote[ [1] This commission goes by many names. Juice, vig, vigorish, cut, take, etc. ] --- class: top ### A Simplified Model - Dolphins are playing the Patriots. The Patriots are a 6.5 point favorite. - I want to bet the Dolphins +6.5 - You the Patriots -6.5. -- - We both place bets for 100 with the sportsbook. Juice is standard **-110** -- - Patriots win 28-7. - Patriots cover (28 - 6.5) > Dolphins score (7) . - Dolphins don't cover (7 + 6.5) < Patriots score (28) -- #### Bet Results |Period |Me | You |Book | - - - | - - - | - - - | - - - |Before Bet|110 | 110 |0 |During Bet|0|0|220 |After Bet |0 | 210 |10 --- ### A Simplified Model cont. - Standard model: sportsbook keeps 10 and payouts 210 for 220 put at risk. `10/210 =.0476`. On bets at **-110** odds the sportsbook expects to earn around 4.5% -- - Another way to calculate the book expectation: Calculate the break-even percentage 1for both sides of the wager. In this case both sides are **-110**, add the percentage for both and subtract 1. - `110/(100+100) = .5238` - `.5238 + .5238 = 1.0476` - `1.0476 - 1 = 0.0476` -- In theory if both sides of the bet have an equal chance of winning (what -110 on both sides implies) then the combined probability of either side winning *should* add up to 1. The amount over 1 is approx the book's expected earnings. If you find a bet that adds up to under 1, bet, it means the bet is +EV. .footnote[ [1] More on break-even percentages later. ] --- # Model vs Real-world - Books unlikely to get balanced action. - 4% return isn't bad money but the book has costs. Getting stiffed (credit), marketing, infrastructure, staff (post-up). ### How to earn more money? -- **Give players more to bet on!** The more bets a player makes the more likely he is to lose in the long run. - Offer more bet types. - Offer different types of bets with a higher edge for the book. - Offer more events to bet on. --- ### Most Common Forms of Betting (in US) For any game these three markets are the most common forms of betting. - Money line: Who is going to win the game. - Spread: What "handicap" (points, runs, goals, etc) makes the match equal when you adjust the score by the spread12. - Total: Over or under the combined score of the two teams playing. For each of these it's possible to split into periods, first half, second half, first quarter, etc. ![NCAA Lines](images/ncaa_lines.png) .footnote[ [1] Invented by Charles K. McNeil, a math teacher and bookmaker from Chicago, in the 1940s. [2] In the UK spread betting is a wager that pays out based on the difference from the line. In the the US this form of wagering is called 'Action points.' ] --- ### Different Bet Types .pull-left[ ### Parlays - Parlays combine multiple "straight" bets into one for an increased payout. players need to win all bets to win. - Spreads/Totals parlay payouts are fixed. Vary between books. ML's payout at 'true' odds. - Probability of 3-team parlay 12.5%, that's +700 odds. Most books', pay +600 on 3-teamer earning a theoretical 12.5% on each 3-teamer. - Parlays can be manipulated by sharps i.e. correlated parlays, bonus abuse ] .pull-right[ ### Teasers - Teasers, like parlays, combine multiple plays but let the bettor move the point spread or total by a set number of points. - Teasers are basketball and football only. - Teaser payouts vary book to book. - Standard NFL 2-team teaser is **-120**. EV = **-14.52%**. - Played correctly teasers are +EV and a common sharp play. Books' are constantly defending against them. ] --- # Less Popular Bets These bets are usually much more lucrative for the book. .pull-left[ ### Futures (Long Bets) - Dolphins win over/under 8.5 regular season games (NFL) - team to win Eastern Conference Finals (Best of 7) (NBA) - team wins Western Conference Finals in `n` games. (NBA) - team to win 3 game series (MLB) - player to win first round (PGA) ] .pull-right[ ### Props - Brady over/under 23.5 receptions (NFL) - First score over/under 7.5 minutes (NFL) - First team to `X` points (NFL) - Score / No Score first inning (MLB) - Max Scherzer O/U 7 strikeouts (MLB) ] --- # Live Betting (the future) .pull-left[ Every possible bet type at any point in the game. - Spreads, ML's, totals - Juice is usually higher - Lines change fast - Examples of common bets offered beyond the main three types - Race to *n* points - Either team scores *n* points - Margin of victory - Alternate spreads / totals - Player props ] .pull-right[ ### Live Betting Exploits - For sharp bettors' live betting is a lucrative new area. - Low limits is common defense against sharps. - Lot's of opportunity for fraud: TV feeds faster than sportsbooks, watching game in-person, AM radio. - Models are much simpler than you'd think. - Baseball in-game is probably the sharpest lines. - More options and weaker leagues still offer new opportunities. ] --- class: center ### Managing Risk - Methods the Books Use ![](images/risk_mngmt.svg) --- # What moves the line - **Sharp Books** For US facing books' Pinnacle and Bookmaker.eu are the most important books. -- - **Sharp Bettors'** A 500 bet from a pro can move the line more than a 20k bet from a square. -- - **Money** Eventually if the money is skewed enough you need to move the line, even if the action is 100% square. --- class: center ### Betting Limits It's about trade-offs. -- Small markets are easy to beat but you can't bet a lot on them. -- Opening lines are easier to beat than closing lines but limits are low. -- Derivatives are much more beatable compared to game lines but lower limits. --  --- class: center, middle, inverse ### What's an overround? > The over-round (Vigorish, or simply "vig", or "juice") is what gives the bookmaker his profit. If the over-round is 1.2 (sometimes written as 120%) the bookmaker will expect to pay out 100 dollars for every 120 dollars he takes in, yielding him an expected profit of 20/120 = 16.7%.1 .footnote[ This is an altered example from http://www.bettingmarket.com/overround.htm ] --- ### How books select overround/juice - Common conventions that most book follow. - Prices vary in smaller markets and with exotic bet types. - Recreational books charge as much juice as they can get away with. - Squares can't do the math to figure out the juice they're paying but much like high rake, they notice they're losing faster. --- ### *x* cent lines - Charging 110 on both sides of point spread bet is referred to as a **20 cent line**. - **30 cent line** charges 115 on both sides of a spread bet. - Some derivatives like team totals charge 30 cent line. - **40 cent line** charges 120 on both sides. - Baseball is commonly a **10 cent line** up to -190 - NBA and NFL money-lines are usually **20 cents** - **x cent lines** aren't stationary around 100. A common boxing money-line might be **-200/160** - Cent lines used to be rounded to increments 5, i.e. 105,110,115,120 -- 50% win probability on both sides of an event translate to the following overrounds. |Cent Line | Format | Overround | --- | --- | --- | 10 | -105 | 1.024 | 20 | -110 | 1.047 | 30 | -115 | 1.069 | 40 | -120 | 1.090 --- ### The problem with fixed cent lines Most squares don't realize that they have a big edge with fixed cent lines. An example, - Game A: **-105/-105** - Game B: **-180/170** Both are **ten cent lines**. But even if both lines are solid the player is paying much less juice. **Decimal lines don't have this problem.** This is bad for the book but good for the player. Most old-time US bookmakers can't wrap their head around this fact. |Game|Odds | BreakevenPct | Overround|JuicePaid1 | --- |--- |--- | --- |A |-105/-105|51.21%/51.21%|1.0242|2.7% |B |-180/170| 64.26%/37.03%|1.0129|1.2% .footnote[ [1] To calculate the true juice paid 1-(1/overround) ] --- class: center ### Juice on 'Dime' line  --- ### Tools - Don Best **Dirty Secret** 95% of sportsbooks' don't set their own lines. Until XML/Json technology popular many books paid "odds makers" to follow other books' (usually Pinnacle or Cris) line movements. Eventually DonBest started offering odds feeds as XML from other books' and books pegged their line to the book of choice. ![](images/donbest.png) --- ### Tools - Betradar .pull-left[ - Started as a soccer/European sport solution but much more sophisticated than DonBest (eventually bought Don Best). - Official betting partners of the NBA. Lobbying for "official" data only sportsbooks. - Add your juice/margin into their models or copy odds from over 300 books. ] .pull-right[ ![](images/br.png) ] --- class: inverse, center, middle ### Let's Talk About Betting --- class: inverse ### Types of Winning Players 1. The Sharps - Handicappers - Modelers - Movers -- 1. The Advantage Players (Quants) - Probability players - Promo exploits - Bonus Hunting -- 1. Line Hunters - Scalpers (arbitrage) - 'Hunt and peck' off lines - Steam Chasers --- class: inverse, center, middle ### Betting Math --- ### The Foundation .pull-left[ #### Probability to Money Line ```r p_to_ml <- function(.p) { ifelse(.p > .5, .p / (1 - .p) * -1, (1 - .p) / .p) } p_to_ml(.75) ``` ``` ## [1] -3 ``` #### US Odds to Decimal ```r us_dec <- function(.ml) { ifelse(.ml < 100, 1 - (100 / .ml), 1 + (.ml / 100)) } us_dec(-110) ``` ``` ## [1] 1.909091 ``` ] .pull-right[ #### Money-line to Probability ```r ml_to_p <- function(.ml) { ifelse(.ml < 0, .ml / (.ml - 1), 1 / (.ml + 1)) } ml_to_p(1.8) ``` ``` ## [1] 0.3571429 ``` #### Decimal Odds to US ```r dec_us <- function(.d) { ifelse(.d >= 2, 100 * (.d - 1), -100 / (.d - 1)) } dec_us(2.7) ``` ``` ## [1] 170 ``` ] --- ### Calculating EV - Multiple the probability of an event happening by the amount won/lost associated with the result. Sum the outcomes. #### Simple 2-Way EV We flip a fair coin, heads I pay you 1.05, tails you pay me one dollar. ```r p_heads <- .5 p_tails <- 1 - p_heads amt_heads <- 1.05 amt_tails <- -1 ev <- (p_heads * amt_heads) + (p_tails * amt_tails) paste("Your EV is ", ev, "per flip." ) ``` ``` ## [1] "Your EV is 0.025 per flip." ``` --- #### Simple (but more complicated) Multi-way EV - Dolphins are playing the Bucs. Game opens Miami -4. You bet the Bucs +4 **110/100** - An injury is reported and the line moves to Miami -2. You bet Miami -2 **110/100** Is this a bad play? What's the EV? We need to know the push frequencies of 2, 3, and 4 points. ```r push <- c("2" = .02, "3" = .10, "4" = .03) # conquering risk one_side <- (1 - sum(push)) rest <- (100 - 110) # win one, lose one fins_2 <- 100 # Bucs +4 win , # Fins -2 push fins_3 <- 100 + 100 # Bucs +4 win, # Fins -2 win fins_4 <- 100 # Bucs +4 push, # Fins -2 win ev <- (one_side * rest) + (fins_2 * push['2']) + (fins_3 * push['3']) + (fins_4 * push['4']) a <- paste('We make', round(ev), 'on our 220 put at risk.') b <- paste("That's a",scales::percent(round(ev)/220), "ROR") paste(a,b) ``` ``` ## [1] "We make 16 on our 220 put at risk. That's a 7% ROR" ``` --- ### Break-even Rate ```r breakeven_rate_ml <- function(.ml) { ifelse(.ml < 0, abs(.ml) / (abs(.ml) + 100), 100 / (.ml + 100)) } common_lines <- c(seq(-110,-160,-10),seq(100,150,10)) breakeven_rate_ml(common_lines) %>% round(3) ``` ``` ## [1] 0.524 0.545 0.565 0.583 0.600 0.615 0.500 0.476 0.455 0.435 0.417 0.400 ``` --- ### Break-even Rate with Multi Event Plays Why are two team parlays at **+260** a bad bet? ```r breakeven_wgr_amt <- function(.amt_risk, .amt_won, .event=1){ # if event > 1 the return value represents the # percentage of time each leg must win (.amt_risk / (.amt_risk + .amt_won))^(1/.event) } paste( "In a 2-team parlay each leg must hit approx", scales::percent(breakeven_wgr_amt(100, 260, 2)), " of the time." ) ``` ``` ## [1] "In a 2-team parlay each leg must hit approx 53% of the time." ``` + The break-even rate for a straight bet at **-110** is 52%. --- ### Decoding Juice #### Calculate Overround ```r overround <- function(juiced_line) { sum(ml_to_p(juiced_line)) } fair_line_conversion <- function(juiced_line) { p_to_ml(ml_to_p(juiced_line) / sum(ml_to_p(juiced_line))) } paste('Overround on -110 odds= ', overround(c(-1.1, -1.1) %>% round(.,2))) ``` ``` ## [1] "Overround on -110 odds= 1.04761904761905" ``` ```r fair_line_conversion(c(-1.55,1.45)) ``` ``` ## [1] -1.489216 1.489216 ``` --- # Political Example ![](images/pres_odds.png) - What's the book making on this wager? - What's the true odds? --- ### Political Example cont. ```r pres_odds <- c(1.23, 7.07, 8.75, 3.90, 5.08, 42.00, 65.00, 44.48, 100.00, 200.00, 45.00, 58.64) cands <- c('Trump','ORouke','Harris', 'Biden','Sanders','Warren', 'Pence','Booker','Kasich', 'Schultz','Klobuchar','Gabbard') df <- tibble( cand = cands, odds = pres_odds, fair_odds = round(fair_line_conversion(pres_odds),3), pct = ml_to_p(fair_odds) %>% scales::percent() ) paste('The overround is:', overround(df$odds)) ``` ``` ## [1] "The overround is: 1.15724318120051" ``` --- ### Political Example cont.

  cand 	  odds 	  fair_odds 	  pct

  Trump 	  1.23 	  1.581 	  38.745%

  Biden 	  3.90 	  4.670 	  17.637%

  Sanders 	  5.08 	  6.036 	  14.213%

  ORouke 	  7.07 	  8.339 	  10.708%

  Harris 	  8.75 	  10.283 	  8.863%

  Warren 	  42.00 	  48.761 	  2.010%

  Booker 	  44.48 	  51.631 	  1.900%

  Klobuchar 	  45.00 	  52.233 	  1.879%

  Gabbard 	  58.64 	  68.018 	  1.449%

  Pence 	  65.00 	  75.378 	  1.309%

  Kasich 	  100.00 	  115.882 	  0.856%

  Schultz 	  200.00 	  231.606 	  0.430%

 --- ### Add Juice to Fair lines #### Pricing the MLB AL East Division Winner. Estimate each team's probability of winning the division. The probabilities need to add up to 1. - Yankees 46% - Redsox 35% - Rays 9% - Blue Jays 6% - Orioles 4% -- There's a lot of uncertainty in the estimates. Because of this, our overround should be much higher than an standard game. --- ### Add Juice to Fair Lines cont. ```r add_juice <- function(fair_ml, juice) { p_to_ml(ml_to_p(fair_ml) * juice) } teams <- c('NYY', 'BOS', 'TBB', 'TOR', 'BAL') p_est <- c(.46, .35, .09, .06, .04) fair_line <- p_to_ml(p_est) juiced <- add_juice(fair_line, 1.2) %>% round(., 2) knitr::kable(tibble(teams, p_est, fair=round(fair_line,2), juiced), format = 'html') ```

  teams 	  p_est 	  fair 	  juiced

  NYY 	  0.46 	  1.17 	  -1.23

  BOS 	  0.35 	  1.86 	  1.38

  TBB 	  0.09 	  10.11 	  8.26

  TOR 	  0.06 	  15.67 	  12.89

  BAL 	  0.04 	  24.00 	  19.83

 --- ### What's Your Winning Percentage? Commonly you'll hear bettors say: > I'm hitting 58% of my bets But without complete information, that number is meaningless. -- #### Example What's your winning percentage? |Risk |ToWin |Result |---- |---- |----- |-150 |100 |W (100) |100 |160 |W (160) |-250 |100 |L (-250) Square says: > 66% --- ### Normalized Win Percentage ```r results <- c(100,160,-250) win_pct_normed <- function(results){ win <- results[results > 0] loss <- results[results < 0] sum(win) / (sum(win) + sum(loss)/-1) } # True Win Percentage win_pct_normed(results) %>% round(.,3) %>% print() ``` ``` ## [1] 0.51 ``` --- class: inverse, center, middle ### Using Models --- class: inverse, center, middle .pull-left[ ![](images/JohnvonNeumann.gif) ] .pull-right[ > With four parameters I can fit an elephant, and with five I can make him wiggle his trunk. \- John von Neumann ] --- ### The Modeling Process **Remember sports analytics != sports betting.** 1. Get a database for your sport of choice. You'll need past lines and scores and stats. 1. Find meaningful variables (simple is always better) 1. Feature engineer if needed 1. Compare your results to past lines an analyze error. Finding the right metric to test your model against is tricky. In the end you're looking for your estimated spread to be closer to the mov than the closing spread. *note*: Chucking scores and stats into a machine learning model isn't enough to win. --- ### What Are We Predicting - Normally we're predicting game outcomes. This can either be margin of victory or the likelihood of team A beating team B. - You can use either regression or classification algorithms. If you use a classification model it's possible to turn the percentage into a point spread using a conversion table. - Less common but still valulable is predicting the direction a line will move. There's +EV plays if you can bet both sides if the line crosses key numbers. --- ### Example Model  --- ### Example Model cont.  --- class: inverse, center, middle ### Case Study: NCAA Basketball Example --- ### Gather Data Lot's of solid options for NCAA data. Don't reinvent the wheel. Find someone whose done the work. - [Luke Benz's Repos on github contain a wealth of information](https://github.com/lbenz730/NCAA_Hoops) - [The prediction tracker gives you historical closing lines for the past 14 seasons](http://www.thepredictiontracker.com/basketball.php) ```r ncaa_results_1819 <- 'https://raw.githubusercontent.com/lbenz730/NCAA_Hoops/master/3.0_Files/Results/2018-19/2019_Final.csv' results <- read_csv(ncaa_results_1819,guess_max = 5000) ``` --- ### Rating System Rating systems are a deep subject themselves. The most famous is probably the models behind the old BCS selection system and RPI used for NCAA basketball. - The Massey, Colley, and ELO (538) models are the best known ranking systems. Public rating/ranking systems won't beat the spread long term. Check out the [comperank](https://github.com/echasnovski/comperank) package for implementations of these and other models. ```r # simplfy our dataframe df <- results %>% select(gid = game_id, team, opp = opponent, location, team_score, opp_score) %>% filter(!is.na(team_score)) ``` --- ### Rating System cont. Some utility functions. ```r # initialize power ratings clear_ratings <- function(){ df %>% group_by(team) %>% summarise(power = 0) %>% ungroup() } add_hfa <- function(df){ df %>% mutate(opp_score = case_when( location == 'H' ~ opp_score + 4, location == 'V' ~ opp_score - 4, TRUE ~ opp_score )) } ``` --- ### Rating System cont. Calculate the team's rating by summing the margin of victory, plus the opponent's power rating, and dividing by the number of games played. ```r get_power_ratings <- function(.df, .ratings) { .df %>% left_join(.ratings, by = c('opp' = 'team')) %>% {{ mutate(margin = team_score - opp_score + power)}} %>% group_by(team) %>% {{summarise(power = sum(margin) / n())}} %>% ungroup() } ``` This model is iterative, each pass through the data uses an updated power rating. This approach does away with the need for strength of schedule. --- ### Rating System cont. #### Run Model ```r tm_ratings <- clear_ratings() # init each team rating to 0 df <- add_hfa(df) # add HFA n_trials <- 50 # number of iterations to go through ratings_list <- vector("list", length = n_trials) # good R practice # For loops aren't great in R but this is small data # Easy to understand for (i in 1:n_trials){ tm_ratings <- get_power_ratings(df, tm_ratings) ratings_list[[i]] <- tm_ratings } ``` --- class: center ### View Results `arrange(tm_ratings, desc(power)) %>% head(10)` |team | power| |:--------------|--------:| |Gonzaga | 26.71077| |Duke | 25.74658| |Virginia | 24.37975| |Michigan St. | 23.36485| |North Carolina | 23.13977| |Tennessee | 20.44052| |Michigan | 20.43373| |Purdue | 19.42287| |Kentucky | 19.22065| |Texas Tech | 18.99942| --- ### Ratings Converge Quickly  --- ### Compare with Popular Rating Systems Using the `comperank` package #### Prepare the Data Results must be in a specific format. ```r library(comperank) ncaa_results_1819 <- 'https://raw.githubusercontent.com/lbenz730/NCAA_Hoops/master/3.0_Files/Results/2018-19/2019_Final.csv' results <- read_csv(ncaa_results_1819,guess_max = 5000) ncaa19 <- dplyr::filter(results, location == 'H') %>% select( player1 = team, score1 = team_score, player2 = opponent, score2 = opp_score ) %>% na.omit() %>% comperes::as_widecr() ``` --- ### Colley Method ```r colley <- rank_colley(ncaa19) arrange(colley, ranking_colley) %>% head(10) %>% knitr::kable(format = 'html') ```

  player 	  ranking_colley

  Virginia 	  1

  North Carolina 	  2

  Kentucky 	  3

  LSU 	  4

  Houston 	  5

  Tennessee 	  6

  Duke 	  7

  Gonzaga 	  8

  Michigan 	  9

  Michigan St. 	  10

 --- ### Massey ratings ```r massey <- rank_massey(ncaa19) arrange(massey, ranking_massey) %>% head(10) %>% knitr::kable(format = 'html') ```

  player 	  ranking_massey

  Gonzaga 	  1

  Virginia 	  2

  Duke 	  3

  North Carolina 	  4

  Michigan St. 	  5

  Tennessee 	  6

  Kentucky 	  7

  Purdue 	  8

  Michigan 	  9

  Texas Tech 	  10

 --- ### Markov ratings ```r markov <- rank_markov(ncaa19, !!!h2h_funs[c("num_wins","mean_score_diff_pos")], weights = c(0.4, 0.6)) arrange(markov, ranking_markov) %>% head(10) %>% knitr::kable(format = 'html') ```

  player 	  ranking_markov

  North Carolina 	  1

  Duke 	  2

  Michigan 	  3

  Michigan St. 	  4

  Virginia 	  5

  Louisville 	  6

  Iowa 	  7

  Houston 	  8

  LSU 	  9

  Kentucky 	  10

 --- class: inverse, center, middle # Turn Ratings into Spreads --- # Historical Point Spread Data Using our ratings we can predict the average amount a home team will win a game by the simple formula `home edge + home team rating—away team rating`. In [Mathletics](https://www.amazon.com/Mathletics-Gamblers-Enthusiasts-Mathematics-Basketball/dp/0691154589), Wayne Winston cites a paper by Hal Stern. > Stern showed that the probability that the final margin of victory for a home NFL team can be well approximated by a normal random variable margin with mean = home edge + home team rating—away team rating and a standard deviation of 13.86 First let's find the standard deviation ```r pred_tracker_data <- 'http://www.thepredictiontracker.com/ncaabb17.csv' ncaa_ps_17 <- read_csv(pred_tracker_data, guess_max = 5000) %>% transmute(mov = hscore - rscore, s_diff = mov - line) sd(ncaa_ps_17$s_diff,na.rm=TRUE) ``` ``` ## [1] 11.01557 ``` --- # Difference Between Spreads and Outcomes Next let's confirm the normal assumption. ```r ggplot(ncaa_ps_17, aes(s_diff)) + geom_histogram(binwidth = 2, fill='#ddc5a2', color='white') ``` ![](index_files/figure-html/unnamed-chunk-31-1.svg) --- ### Simulate Game We now have the knowledge needed to simulate a game. We'll use two teams from the iterative ratings table. 1. Virginia - 24.37975 (1 seed) 2. Texas Tech - 18.99942 (3 seed) A rough estimate of the spread is `24.37975 - 18.99942`, 5.4 which we'll round up to **5.5**. There's no home field in this match-up. We can use this spread to estimate the percentage of time Virginia wins the match-up. ```r ncaa_sd <- 11 mu <- 5.5 sims <- qnorm(runif(1000), mu, ncaa_sd) # simulate 1000 games paste('Pct of games home team wins', scales::percent(mean(sims > 0))) ``` ``` ## [1] "Pct of games home team wins 68%" ``` ```r paste('Median mov', median(sims) %>% round()) # should be spread ``` ``` ## [1] "Median mov 6" ``` --- ### Scale Up Predictions ```r matchups <- expand.grid(team=tm_ratings$team, opp=tm_ratings$team) %>% as_tibble() %>% filter(team != opp) %>% left_join(tm_ratings) %>% rename(tm_power = power) %>% left_join(tm_ratings, c('opp' = 'team')) %>% rename(opp_power = power) simulate_game <- function(tm_pwr, opp_pwr, sd = 11, trials = 1) { mu <- tm_pwr - opp_pwr qnorm(p = runif(trials), mean = mu, sd = sd) } ``` --- ### Scale Up Predictions cont. ```r sim_matchups <- map2(matchups$tm_power, matchups$opp_power, ~simulate_game(.x,.y,trials = 10000)) p_preds <- sim_matchups %>% map_dbl(~mean(.x > 0)) matchups$pct_preds <- p_preds ``` --- class: inverse, center ### Case Study: NFL Calculating the Value of Points --- ### How Much is a ½ Point Worth? The answer... it depends. Buying off what number? The question you need to answer is how often does my push turn into a win. ![](index_files/figure-html/unnamed-chunk-35-1.svg) --- ### Push Charts In betting the value of a half point (or run, goal, etc) is derived from push percentages.In [Weighing the Odds in Sportsbetting](https://www.amazon.com/Weighing-Odds-Sports-Betting-King/dp/0935926305) Yao explains the process of converting a push percentage into money line. 1. Subtract the push percentage from 100 1. Divide the result by two 1. Convert the result into a money-line 1. Subtract 100 from the result of step 3, this is value of the half point --- ### An example Let's assume that the push percentage for a spread of seven in the NFL is `6.5%`. The value of buying a half point on or off a point spread of 7 points is calculated as follows. 1. `100 - 6.5 = 93.5` 1. `93.5/2 = 46.75` 1. Convert 46.75% to a money-line = `114` 1. `114 - 100 = 14` So according to the example the fair price for buying on or off the seven in NFL is 14 cents. If you can buy a half point or more for less than the value of the fair price it's +EV. --- ### Creating a Push Chart In most sports the problem is not enough games for a decent sample size, how to get around this? One way is to widen the "window" of games looked at. It might not be the most efficient but it gets the job done. ```r file_path <- file.path('~','Dropbox','ArmchairAnalysis','nfl_00-18') nfl <- read_csv(paste0(file_path,'/','GAME.csv')) grade_ats_home <- function(h_pts, v_pts, h_sprd) { case_when(h_pts + h_sprd > v_pts ~ 'ats_w', h_pts + h_sprd == v_pts ~ 'ats_p', TRUE ~ 'ats_l') } ``` --- ### Creating Push Chart cont The push chart function ```r push_chart <- function(df = nfl, ps, # point spread wnd = 2, # window seas_cut = 2008) { # ps = point spread # wnd = window df %>% filter(seas >= seas_cut) %>% mutate( sprh = sprv * -1, # view home team fav as -7 instead of 7 mov = ptsh - ptsv, ats_result = grade_ats_home(ptsh, ptsv, sprh) ) %>% filter(sprh >= ps - wnd & sprh <= ps + wnd) %>% mutate(push = (mov == (ps * -1)) * 1) %>% summarise(games = n(), push = sum(push)) } ``` --- ### Creating Push Chart cont. ```r spreads <- seq(-21, 14, 1) pc_df <- bind_cols(tibble(spreads), map_df(spreads, ~push_chart(ps=.x, wnd=2))) pc_df %>% mutate(pct = round(push/games,3)) %>% arrange(desc(games)) %>% head(5) %>% knitr::kable(format = 'html') ```

  spreads 	  games 	  push 	  pct

  -5 	  1093 	  23 	  0.021

  -4 	  964 	  28 	  0.029

  -3 	  963 	  85 	  0.088

  -2 	  861 	  23 	  0.027

  -6 	  749 	  32 	  0.043

 --- ### Creating Push Chart cont. Removing H/A perspective ```r get_fair_price <- function(.pct){ ml <- p_to_ml((1 - .pct) / 2) (ml - 1) * 100 } prices <- pc_df %>% group_by(spread=abs(spreads)) %>% summarise(games = sum(games), push = sum(push), pct = round(push/games,3)) %>% arrange(desc(pct)) %>% filter(games >= 100) %>% mutate(fair_price = get_fair_price(pct) %>% round()) ``` --- ### Creating Push Chart cont. Fair prices ```r prices %>% head(10) %>% knitr::kable(format='html') ```

  spread 	  games 	  push 	  pct 	  fair_price

  3 	  1630 	  133 	  0.082 	  18

  7 	  952 	  59 	  0.062 	  13

  6 	  1052 	  40 	  0.038 	  8

  10 	  413 	  14 	  0.034 	  7

  4 	  1583 	  52 	  0.033 	  7

  14 	  154 	  5 	  0.032 	  7

  2 	  1488 	  40 	  0.027 	  6

  8 	  930 	  25 	  0.027 	  6

  5 	  1700 	  38 	  0.022 	  4

  1 	  1312 	  28 	  0.021 	  4

 --- class: center, inverse, middle ### Case Study: EPL Correlated Wagers --- ## EPL Counts ```r epl_17 <- read_csv('data/epl_1617.csv') epl_18 <- read_csv('data/epl_1718.csv') epl_17 %>% count(draw = FTHG == FTAG) %>% knitr::kable(format = 'html') ```

  draw 	  n

  FALSE 	  296

  TRUE 	  84

 - Approx 22% of games in the EPL end in a draw (at least in 2017). - Of the 84 games that ended in draw how many had a combined total goals of 2 or less ```r epl_17 %>% filter(FTHG == FTAG) %>% count(FTHG, FTAG) %>% filter(FTHG < 2) %>% summarise(sum(n)) %>% pull() ``` ``` ## [1] 65 ``` --- ## EPL Counts cont. - 65 out of the 84 draws had 2 goals or less scored (77%) - If we parlayed the game being a draw to the game going under 2.5 goals we win our bet 77% of the time. Calculate the median payouts for draw and under and the median parlay payout. ```r calc_parlay_payout <- function(odds_1, odds_2){ p_to_ml((1/odds_1) * (1/odds_2)) } epl_17 %>% select(PSCD,ou_odds=`BbAv<2.5`) %>% mutate(payout = calc_parlay_payout(PSCD, ou_odds)) %>% summarise(med_draw=median(PSCD), med_ou = median(ou_odds), med_odds = median(payout)) %>% knitr::kable(format = 'html') ```

  med_draw 	  med_ou 	  med_odds

  3.765 	  1.97 	  6.2809

 --- ## EPL Counts cont. Simple math to estimate our edge. Assume 1,000 matches. ```r games <- 1000 pct_draw <- .22 pct_u_draw <- .77 draw_games <- games * pct_draw winners <- floor(draw_games * pct_u_draw) losers <- 1 - winners est_win <- (((winners/games) * 600) - ((losers/games) * 100)) /100 paste('For every 100 wagered we expect a return of', est_win*100) ``` ``` ## [1] "For every 100 wagered we expect a return of 118.2" ``` --- ### Testing Theory - We'll blindly bet draw+under on every match - We'll use a more conservative parlay payout of +600 - These matches are from the 2017/2018 season. We haven't calculated any stats from them yet. ```r # 2018 results count(epl_18, draw=FTHG == FTAG, under=FTHG + FTAG <= 2) %>% knitr::kable(format = 'html') ```

  draw 	  under 	  n

  FALSE 	  FALSE 	  172

  FALSE 	  TRUE 	  109

  TRUE 	  FALSE 	  22

  TRUE 	  TRUE 	  77

 --- ### How Theory Performed ```r winners <- 77 losers <- nrow(epl_18) - winners # 303 total volume_bet <- nrow(epl_18) * 100 # the amount we risked paste('We risk a total of ', scales::comma(volume_bet)) ``` ``` ## [1] "We risk a total of 38,000" ``` ```r won <- ((77 * 600) - (losers * 100)) paste('We won ', scales::dollar(won)) ``` ``` ## [1] "We won $15,900" ``` ```r paste('An ROR of', scales::percent(won/volume_bet)) ``` ``` ## [1] "An ROR of 42%" ``` --- class: center, inverse, middle ### Case Study: NBA Opening vs. Closing Lines --- ### Gather Data ```r library(lubridate) nba <- read_csv('data/KostatsNBA.csv',guess_max = 50000) betting_cols <- c('openspr', 'openfav', 'opentot', 'openhml', 'spread', 'favored', 'total', 'homeml') nba$date <- mdy_hms(nba$date,tz = Sys.timezone()) %>% as_date() nba <- filter(nba, date >= '2012-08-01') summary(select(nba,one_of(betting_cols))) ``` ``` ## openspr openfav opentot openhml ## Min. : 0.000 Length:7820 Min. :174.0 Min. :-90.000 ## 1st Qu.: 3.000 Class :character 1st Qu.:196.0 1st Qu.: -3.300 ## Median : 5.500 Mode :character Median :204.0 Median : -1.650 ## Mean : 5.882 Mean :204.0 Mean : -2.301 ## 3rd Qu.: 8.000 3rd Qu.:211.5 3rd Qu.: 1.150 ## Max. :22.000 Max. :241.0 Max. : 20.000 ## spread favored total homeml ## Min. : 0.000 Length:7820 Min. : 19.5 Min. :-115.000 ## 1st Qu.: 3.000 Class :character 1st Qu.:196.0 1st Qu.: -3.450 ## Median : 5.500 Mode :character Median :204.0 Median : -1.700 ## Mean : 6.164 Mean :203.9 Mean : -2.454 ## 3rd Qu.: 8.500 3rd Qu.:211.5 3rd Qu.: 1.200 ## Max. :22.000 Max. :242.0 Max. : 17.000 ``` --- ### Reshape Data ```r nba_odds <- nba %>% filter(playoff == FALSE, total > 100) %>% mutate(mov = homescore - awayscore, tot_score = homescore + awayscore) %>% select(mov, tot_score, one_of(betting_cols)) %>% mutate(openspr = ifelse(openfav == 'H', openspr, openspr * -1), spread = ifelse(favored == 'H', spread, spread * -1)) ``` --- ### New Data Set ```r knitr::kable(head(nba_odds),format = 'html') ```

  mov 	  tot_score 	  openspr 	  openfav 	  opentot 	  openhml 	  spread 	  favored 	  total 	  homeml

  10 	  178 	  5.5 	  H 	  191.0 	  -2.20 	  6.0 	  H 	  191.0 	  -2.50

  13 	  227 	  5.5 	  H 	  191.0 	  -2.20 	  6.0 	  H 	  187.5 	  -2.70

  -8 	  190 	  10.0 	  H 	  187.0 	  -4.40 	  8.5 	  H 	  186.0 	  -4.75

  9 	  159 	  2.0 	  H 	  201.0 	  -1.25 	  -1.5 	  A 	  197.0 	  1.10

  -2 	  178 	  -3.5 	  A 	  188.5 	  1.05 	  -1.5 	  A 	  190.0 	  1.00

  -9 	  201 	  3.0 	  H 	  188.0 	  -1.50 	  2.5 	  H 	  194.0 	  -1.35

 --- ### Plot Opening Spread vs Closing Spread ```r nba_odds %>% count(openspr, spread) %>% ggplot(aes(openspr, spread, fill = n)) + geom_tile() + scale_fill_gradient(low = '#ddc5a2', high = '#523634') + labs(fill = 'games', x = 'opening', y = 'closing') ``` ![](index_files/figure-html/unnamed-chunk-50-1.svg) --- ### Plotting Opening Total vs Closing Total ```r nba_odds %>% count(opentot, total) %>% ggplot(aes(opentot, total, fill = n)) + geom_tile() + scale_fill_gradient(low = '#ddc5a2', high = '#523634') + coord_cartesian(xlim = c(170, 245), ylim = c(170, 245)) ``` ![](index_files/figure-html/unnamed-chunk-51-1.svg) --- ### Common Spread Line Movement ```r common_spreads <- count(nba_odds, openspr, sort = TRUE) %>% pull(openspr) %>% head(12) filter(nba_odds, openspr %in% common_spreads) %>% ggplot(aes(spread)) + geom_density(col='#301b28') + facet_wrap(vars(openspr)) ``` ![](index_files/figure-html/unnamed-chunk-52-1.svg) --- ### Common Total Line Movement ```r common_totals <- count(nba_odds, opentot, sort = TRUE) %>% pull(opentot) %>% head(12) filter(nba_odds, opentot %in% common_totals) %>% ggplot(aes(total)) + geom_density(col='#301b28') + facet_wrap(vars(opentot)) ``` ![](index_files/figure-html/unnamed-chunk-53-1.svg) --- ### Opening Spreads Most Variance ```r nba_odds %>% transmute(diff = openspr - spread, openspr, spread) %>% group_by(openspr) %>% summarise(games=n(), mean = mean(diff), median = median(diff), sd = sd(diff) ) %>% filter(games >= 50) %>% arrange(desc(sd)) %>% head() %>% knitr::kable(format = 'html', digits = 3) ```

  openspr 	  games 	  mean 	  median 	  sd

  1.5 	  180 	  0.494 	  0.00 	  2.343

  0.0 	  152 	  0.329 	  1.00 	  2.286

  -2.5 	  145 	  -0.359 	  0.00 	  2.261

  2.0 	  222 	  0.097 	  -0.25 	  2.083

  1.0 	  243 	  0.440 	  0.00 	  2.043

  -1.5 	  152 	  -0.039 	  0.50 	  1.977

 --- ### Opening Totals Most Variance ```r nba_odds %>% transmute(diff = opentot - total, opentot, total) %>% group_by(opentot) %>% summarise(games = n(), mean = mean(diff), median = median(diff), sd = sd(diff) ) %>% filter(games >= 50) %>% arrange(desc(sd)) %>% head() %>% knitr::kable(format = 'html', digits = 3) ```

  opentot 	  games 	  mean 	  median 	  sd

  220.0 	  70 	  -0.657 	  -0.75 	  3.062

  205.0 	  165 	  0.112 	  0.50 	  2.998

  218.0 	  68 	  -0.110 	  -0.50 	  2.994

  211.0 	  130 	  0.085 	  0.00 	  2.823

  204.5 	  111 	  0.288 	  0.00 	  2.801

  217.0 	  71 	  0.338 	  0.00 	  2.781

 --- class: center, middle, inverse ### In Game Lines In 2007 [Pinnacle Sports posted](https://www.sportsinsights.com/sports-betting-articles/pinnacle-pulse/pinnacle-pulse-11807/) an explanation of a simple in-game betting model. 1. Calculate the estimated final score of a game using the posted spread and total. 1. Estimate the number of total possessions per team (12 is good default) 1. Get the average points per score (5.4 default) 1. Calculate the number of estimated scores per team ('est score / avg points per score') 1. Divide the the number of estimated scores by the number of estimated possessions. ### In Game Line Example In our example, the Dolphins (@home) playing the Giants are a 6 point favorite and the total is 44. ```r expected_scores <- function(h_sprd, tot){ require(tibble) a_pts <- (tot + h_sprd) / 2 h_pts <- a_pts - h_sprd tibble( spread = h_sprd, total = tot, home_scr = h_pts, away_scr = a_pts ) } expected_scores(-6,44) %>% knitr::kable(format = 'html') ```

  spread 	  total 	  home_scr 	  away_scr

  -6 	  44 	  25 	  19

 --- ### In Game Line Example cont. ```r dolphins_final_est <- 25 giants_final_est <- 19 est_pts_scr <- 5.4 dolphins_n_score <- dolphins_final_est/est_pts_scr giants_n_score <- giants_final_est/est_pts_scr dolphins_scr_pct <- dolphins_n_score / 12 giants_scr_pct <- giants_n_score / 12 ``` With this simple information we have enough to compute the fair line at any point in the game. --- ### In Game Line Example cont. ```r get_est_final_score <- function(poss, pts, scr_pct, est_poss = 12) { pos_remain <- est_poss - poss est_pts <- (scr_pct * pos_remain) * 5.4 est_final_scr <- pts + est_pts ceiling(est_final_scr) } ``` **Example Current State** - Dolphins 17 points, 9 possessions - Giants 23 points, 10 possessions ```r fins_final <- get_est_final_score(9, 17, dolphins_scr_pct) giants_final <- get_est_final_score(10, 23, giants_scr_pct) tibble(dolphins_est_scr = fins_final, giants_est_scr = giants_final, in_game_sprd = fins_final - giants_final, in_game_total = fins_final + giants_final) ``` ``` ## # A tibble: 1 x 4 ## dolphins_est_scr giants_est_scr in_game_sprd in_game_total ## <dbl> <dbl> <dbl> <dbl> ## 1 24 27 -3 51 ``` --- ### Problems with Model - Too simplistic doesn't scoring distributions into account. - Teams score more often towards the end of each half and more in the second half. -- - As the game progresses the standard deviation of scoring margins decrease. -- - The model can be improved by taking smaller slices of time into account. - If each team has 12 possessions and there's 60 minutes in a game. Each possession is modeling 2.5 minutes at a time. On average each team has 63 plays per game. That's 2.1 plays per minute. So our model is looking a chunks of 5 to 6 plays at a time. We'd improve by getting that number smaller. --- class: center, middle, inverse ### Pricing Props --- ### An Example from Sharp Sports Betting We find a prop bet that looks interesting. |Prop|Value|Over|Under |---|---|---|--- |Total QB Sacks (both teams) | 5 | -100 | -120| We use a three step process to decide if the prop is worth betting. 1. Analyzing past performance1 1. Estimate the likely value (in this case total QB sacks) 1. Use the proper distribution to price the prop Let's breakdown this bet. If we bet over we need to be right 50% of the time to breakeven. If we bet under we need to be right 54.5%. The book is earning roughly 4.5% on this bet (if it's priced correctly). .footnote[ [1] Getting your hands on past prop bet data is very hard for US sports. [propbets.football](https://propbets.football/nfl-prop-betting-odds-results-database/) posts some. With some scrapping it's possible to create a small database to compare predictions vs past results. ] --- ### Simple Poisson Model Quickly analyzing the two teams playing we determine the average number of sacks per game for the two to be 4.2. To use the Poisson distribution to price props we need to answer two questions. 1. Is the variable something counted one at a time? - NFL points scored come in bunches of 3's,6's, and 7's and don't satisfy this condition. Sacks come one at a time. 1. The probability of the event happening is small. - There's lots of plays in an NFL game but sacks occur on only a few. ```r trials <- rpois(10000, 4.2) push <- mean(trials == 5) #approx 16.5% over <- mean(trials > 5) #approx 24.5% we need 50% to BE under <- mean(trials < 5) #approx 58.9% we need 54.5% to BE ev <- (100 * under) + (0 * push) + (-120 * over) paste('Our EV is', ev, 'and our ROR is' ,scales::percent(ev / 120)) ``` ``` ## [1] "Our EV is 28.978 and our ROR is 24%" ``` ### Not So Easy That's the model most odds makers use to properly price props. This means our model need to produce more accurate variable estimates. Taking averages won't cut it. We can try to improve our variable accuracy using more complicated models. #### Using the `ebbr` Package ```r library(ebbr) file_dir <- '~/Dropbox/ArmchairAnalysis/nfl_00-18/' nfl_files <- dir(file_dir,full.names = TRUE) nfl_pass <- paste0(file_dir, 'PASS.csv') %>% read_csv() nfl_qbs <- paste0(file_dir, 'PLAYER.csv') %>% read_csv() %>% filter(pos1 == 'QB') nfl_play <- paste0(file_dir, 'PLAY.csv') %>% read_csv() %>% select(gid,pid) nfl_schd <- paste0(file_dir, 'SCHEDULE.csv') %>% read_csv() %>% select(gid, seas) player_map <- transmute(nfl_qbs,player, full_name = paste(fname, lname)) ``` --- ### `ebbr` Package ```r pass_summary <- nfl_pass %>% group_by(psr) %>% summarise( passes = n() - sum(spk), comp = sum(comp), tot_yds = sum(yds), avg_yds = mean(yds) ) %>% filter(passes > 200) %>% left_join( player_map, by=c('psr'='player') ) %>% select(-psr) ``` --- ### Plot Confidence Intervals ![](index_files/figure-html/unnamed-chunk-63-1.svg) --- class: inverse, center ### Case Study: MLB Small Sample Sizes --- ### Resampling MLB Of all the sports MLB has the best data and large samples. One issue is that teams' change. Is 2016's Mets the same as 2019's? An effective method for dealing with small sample sizes in sports data is bootstrapping. ```r five38_mlb <- 'https://projects.fivethirtyeight.com/mlb-api/mlb_elo.csv' mlb <- read_csv(five38_mlb) ``` ``` ## Parsed with column specification: ## cols( ## .default = col_double(), ## date = col_date(format = ""), ## playoff = col_character(), ## team1 = col_character(), ## team2 = col_character(), ## pitcher1 = col_character(), ## pitcher2 = col_character() ## ) ``` ``` ## See spec(...) for full column specifications. ``` ```r mlb_17 <- filter(mlb, season == 2017) %>% filter(is.na(playoff)) %>% select(date, team1, team2, score1, score2) %>% arrange(date) ``` --- ### Resampling MLB cont. ```r get_team_runs <- function(df, team, location){ if (location == 'H') { filter(df, team1 == team) %>% pull(score1) } else { filter(df, team2 == team) %>% pull(score2) } } early_ball <- mlb_17 %>% filter(date < '2017-05-10') hou_away_scores <- get_team_runs(early_ball, 'HOU', 'A') mia_home_scores <- get_team_runs(early_ball, 'FLA', 'H') hou_samples <- sample(hou_away_scores, 10000, replace = TRUE) mia_samples <- sample(mia_home_scores, 10000, replace = TRUE) mean(mia_samples > hou_samples) %>% p_to_ml() ``` ``` ## [1] 1.810568 ``` --- ### Reading List *updated 2/11/2020 #### Books on Sports Betting + [Sharp Sports Betting - Stanford Wong](https://www.amazon.com/Sharp-Sports-Betting-Stanford-Wong/dp/0935926240) + [Weighing the Odds in Sports Betting - King Yao](https://www.amazon.com/Weighing-Odds-Sports-Betting-King/dp/0935926305) + [Conquering Risk: Attacking Vegas and Wall Street - Elihu Feustel](https://www.amazon.com/Conquering-Risk-Attacking-Vegas-Street/dp/1450723004) + [Sharper: A Guide to Modern Sports Betting - True Pokerjoe](https://www.amazon.com/Sharper-Guide-Modern-Sports-Betting/dp/1520109326) + [Fixed Odds Sports Betting: Statistical Forecasting and Risk Management - Joseph Buchdahl](https://www.amazon.com/Fixed-Odds-Sports-Betting-Statistical/dp/1843440199) + [Logic Sports Betting - Ed Miller](https://www.amazon.com/Logic-Sports-Betting-Ed-Miller-ebook/dp/B07RG6H8C2) #### Books About Gambling/Gamblers + [Gambling Wizards: Conversations with the World's Greatest Gamblers - Richard Munchkin](https://www.amazon.com/Gambling-Wizards-Conversations-Greatest-Gamblers/dp/0929712056) + [A Man for All Markets: From Las Vegas to Wall Street, How I Beat the Dealer and the Market - Edward Thorpe](https://www.amazon.com/Man-All-Markets-Street-Dealer/dp/1400067960) + [Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street - William Poundstone](https://www.amazon.com/Fortunes-Formula-Scientific-Betting-Casinos/dp/0809045990) + [The Perfect Bet: How Science and Math Are Taking the Luck Out of Gambling - Adam Kucharski](https://www.amazon.com/gp/product/1541697235/ref=dbs_a_def_rwt_bibl_vppi_i0) + [Calculated Bets - Steven Skiena](https://www.amazon.com/Calculated-Bets-Computers-Gambling-Mathematical/dp/0521009626/ref=sr_1_2?crid=178BI5W4GZ2IW&keywords=steve+skiena&qid=1553222084&s=books&sprefix=Steve+ski%2Cstripbooks%2C161&sr=1-2-spell) + [Trading Bases - Joe Peta](https://www.amazon.com/Trading-Bases-Fortune-Betting-Baseball/dp/0451415175/ref=pd_sim_14_2/143-4648007-9270519?_encoding=UTF8&pd_rd_i=0451415175&pd_rd_r=173aabed-4c4b-11e9-84b8-e71b985bd75f&pd_rd_w=70Npr&pd_rd_wg=78oV7&pf_rd_p=90485860-83e9-4fd9-b838-b28a9b7fda30&pf_rd_r=4QKTJCDBJ8H61JJBBN4Q&psc=1&refRID=4QKTJCDBJ8H61JJBBN4Q)


British Columbians unprepared for increasingly severe weather: BC Hydro
VICTORIA - BC Hydro says its customers are largely unprepared for power outages despite increasingly severe winter storms.
The Crown utility says in a report that it's seeing increasing damage to its infrastructure because of storms in recent years.
A powerful windstorm last December left 750,000 customers without power and tens of thousands waited more than three days before it was reconnected in the largest outage in the utility's history.
BC Hydro says a survey of 800 customers found more than 60 per cent have not taken steps to prepare for storm-related power outages and only half have an emergency kit.
The survey found that most people affected by the December 2018 storm felt they could have been more prepared, yet only half have taken any steps toward that since then.
BC Hydro is encouraging customers to stock emergency kits with enough supplies to sustain each member of a household for at least three days.
A kit should include a flashlight with extra batteries, a first-aid kit, any required medication, non-perishable food and bottled water.
The utility is also reminding people to call 911 if they come across a downed or damaged power line, which should be considered live and dangerous.
In December 2017, an ice storm that hit the Fraser Valley caused challenges for crews when freezing rain and below-zero temperatures caused ice to form on trees, which broke and fell onto power lines or knocked down power poles.
BC Hydro says its equipment became encased in ice and the poor weather conditions made it extremely difficult for crews to respond.


Socket.io disconnected
PH3AR Terminal
Connection Options
SSH
Telnet
Host
Port
User
Connect
Save as...
Save
Saved Connections
Back


This style of portable BBQ smokers, is the most popular style used today. The reason for this popularity is our proven design, which has been around for many, many, years. We at old country BBQ pits make our portable BBQ smokers with 1/4" thick steel plate, 3500# axle and brand new wheels and trailer parts. Everything that goes into our pits is new, never used in any capacity.
These pits have great features some of them are: Slide out grills, Framed grills, 1 1/4" threaded pipe drain, Stainless steel spring handles, adjustable air intake and smoke stacks, polished welds for a cleaner looking pit, framed wood basket with plenty of storage capacity, 2" bulldog ball coupler, adjustable grills on firebox, stay on the center wood rack protects the firebox and the wood burns better, 1.5" door flange, water thermos/ lpg bottle holder, 3/16" thick vertical section, light kit & wiring installed, spare tire ready, ready to go, firebox has a lifetime warranty against burnout.
These BBQ smokers are our regular production smokers, custom jobs are always welcome. Any diameter any size!
Lids: 2
Diameter: 36"
Length of:
Firebox: 36"
Grill section: 96"
Vertical section: 36"
Vertical section height: 59"
Weight: 3980 Lbs
Tongue weight: 300lbs
Lids: 2
Diameter: 30"
Length of:
Firebox: 30"
Grill section: 96"
Vertical section: 56"
Vertical section height: 59"
Base unit weight: 3200 Lbs
Base smoker includes: Tuning plates & counter weights.
Portable smoker shown above has the following accessories: Chrome wheels & fenders, red trailer, double burners, temp. gages, insulated firebox and a log startup kit.
Click on "More Photos" below to see additional options and accessories.
This smoker has the following accessories:
Chromed wheels
Aluminum fenders
Crank up BBQ grill
A low pressure double burner
Sink system ( hot & cold water, tanks, pump, etc.)
Wood starter kit that can also cook
Gas line
Temperature gages
Lid on storage basket
Electric brakes
This smoker has the following accessories:
Chromed wheels
Aluminum fenders
A low pressure double burner
Gas line
Wood starter kit that can also cook
Insulated firebox
Drain ball valve
Temperature gages
Custom paint job
The low pressure burners work just like your gas stove at home. They can be set very low and cook a couple of eggs or very high and fry a turkey.
The gas line connects both the firebox and the double burner to the same propane bottle.
The wood starter is a low pressure burner that is removable form the firebox and does not produce soot when you set it low to cook
Both the firebox and the door have two inches of insulation.
Drain ball valve
The standard unit with a custom paint job.
Lids: 3
Diameter: 24"
Length of:
Firebox: 24"
Grill section: 84"
Vertical section: 24"
Vertical section height: 52"
Lids: 2
Diameter: 24"
Length of:
Firebox: 24"
Grill section: 56"
Vertical section: 24"
Vertical section height: 48"
Lids: 2
Diameter: 20"
Length of:
Firebox: 24"
Grill section: 56"
Vertical section: 20"
Vertical section height: 48"
Lids: 2
Diameter: 20"
Length of:
Firebox: 24"
Grill section: 56"
Lids: 1
Diameter: 20"
Length of:
Firebox: 20"
Grill section: 40"
This sink has the capacity to give you 45 gallons of clean water and then to store the water you use so that you may dispose of it properly. It will also give you hot or cold water on demand.
The sink kit contains: one 45 gallon clean water tank, one 45 gallon gray water tank, one propane powered water heater, one pressure sensitive water pump, two sinks, drain valve and plumbing.
These photos are of a 30" portable BBQ smoker that has the following: sink kit, slide out BBQ grill, double burner, log starter kit, elec. brakes, chrome wheels and aluminum fenders.
Here you see the inside of the sink kit, the gray water tank the heater the pump and plumbing. The last photo shows the drain valve.
The cooking grate of this slide out BBQ grill cranks up and down. It can be placed in any position and allow for the lid to be closed. This is a terrific feature if you are grilling some thick cuts or chicken.
While you can control the air intake from the front of the grill it is very easy to tend to the fire from the back.
The log starter kit is a very useful tool to start your fire. It works with a low pressure burner that can start the fire or aid you to cook.
The double burner shown here is not part of the log starter kit but it will show you how we divide the gas line so that with one gas bottle you can run the stove and the log lighter. The bottle is connected to the gas line on the front of the trailer.
The low pressure burner is placed inside the firebox to start the fire. Afterwards you can turn off the burner or use it to aid you to cook
The tuning plates in the BBQ smokers help distribute the hot air from the fire box, and allow the smoker to have an even cooking temperature.
The hot air exits the firebox towards the cooking chambers and travels below the baffle and tuning plates. It is therefore forced to move towards the vertical section and the smokestacks. Now that we have the hot air traveling on a "chamber" we can release it, by sliding open the tuning plates, where it is cooler and heat up that spot. This way we can tune the smoker for an even temperature.
The photo on the left shows the lid next to the firebox. It shows the baffle (the inclined steel plate) followed by the tuning plates. The center photo shows several spaced tuning plates. The photo on the right shows the angle iron rail where the tuning plates rest upon and the bolts that are used to tighten the plates into place.
These tuning plates are on a rectangular smoker. The last photo shows the plates going into the vertical section.
20" smokers: $350.00
24" smokers: $450.00
30" smokers: $680.00
36" smokers: $1,000.00
20" smokers: $ 450.00
24" smokers: $ 500.00
30" smokers: $ 850.00
36" smokers: $1,220.00
20" smokers: contact us
24" smokers: contact us
30" smokers: contact us
36" smokers: contact us
20" smokers: contact us
24" smokers: contact us
30" smokers: contact us
36" smokers: contact us
Any BBQ Smokers you like? contact us!


With very few exceptions (the products modules for example are an exception, they have 2 "Individual Product - Small" layouts) most modules had one of each, the but the general rule is one module - a single set of layouts.
Custom templates are basically an extension to regular layouts. Until they were introduced you could only use a limited number of layouts.
The custom templates are actually plain files however we usually advise to change their extension to .tpl - that is to avoid having them indexed by search engine bots.
The simplest way to get started is to create a new empty file, let's call it productfeaturelist.tpl and create it under the /layouts/onlineshop/custom/ folder. Next, we need to populate it with some tags. The easiest way to get started is to copy the HTML code of an existing layout and paste it into the productfeaturelist.tpl file.
If you insert this on a webpage and preview that webpage you will notice it is using the custom layout instead of the default Individual Product - Small.
The general rule is the template="/layouts/onlineshop/custom/productfeaturelist.tpl" string is added at the end of the module declaration just before the ending curly bracket.
Make sure you use double brackets instead of single brackets. This is incorrect: {module_productfeaturelist,special offer,3,price,_blank,true template='/layouts/onlineshop/custom/productfeaturelist.tpl'}
The custom template declaration has only one parameter, that is the path to the file itself. If the file does not exist or it cannot be found for any reason the module will render with its default layout instead. So for example if the custom template file "/layouts/onlineshop/custom/productfeaturelist.tpl" does not exist or we have a typo in its path the {module_productfeaturelist} module will render using the Individual Product - Small layout instead.
{module_productfeaturelist,special offer,3,price,_blank,true template="/layouts/onlineshop/custom/productfeaturelist.tpl"}
- this rendes a list of products using this custom template{module_booking,i,63522 template="/layouts/custom/bookings.tpl"}
- this rendes an individual event a custom template{module_product,63882,5450055,_top template="/layouts/custom/productfeaturelist.tpl"}
- this rendes an individual product a custom template{module_productfeaturelist,special offer,3,price,_blank,true template="/layouts/custom/productfeaturelist.tpl"}
- lists the products that are tagged with the "special offer" tag using a custom template (the "true" parameter that states the backup template should be used is ignored). The products are sorted by price (cheapest first) and when clicking the product's name the detail view opens in a new window{module_productresults,2,_self,2,,,true template="/layouts/custom/productfeaturelist.tpl"}
- displays the products that match the search criteria 2 per page and renders No products found matching your query. if no products are found.
The structure is no longer an unordered list as the true parameter would suggest, the custom template is used instead.Here is the list of modules where you can apply your own templates as attributes:
{module_literature}
{module_announcement}
{module_faq}
{module_forum}
{module_blog}
{module_case}
{module_order}
{module_webappscustomer}
{module_affiliateprogram}
{module_industrycontentresults}
,{module_webappsresults}
{module_booking}
{module_catalogue}
{module_product}
{module_productresults}
, {module_productresultsold}
{module_ratingfeedback}
{module_caseresults}
{module_webapp}
{module_blogsitepost}
{module_blogpostlist}


{% load static %}
Hello, world!
FlipKart Price Tracker
Offcanvas
About
Sign into your account
{% csrf_token %}
username
Password
Don't have an account?
Register here


The AADL files in this directory are produced by the Cheddar's XML to AADL tranlator.
The XML files, from which the AADL files are generated, are the followings :
- test_rate_monotonic.xml : export of a preemptive Rate Monotonic scheduler and periodic tasks
- test_edf.xml : expot of a preemptive EDF scheduler with periodic tasks
- test_non_preemptive_edf.xml : export of non preemptive EDF scheduler and periodic tasks
- test_non_preemptive_rate_monotonic.xml : export of non preemptive Rate Monotonic scheduler and periodic tasks
- test_edf_aperiodic.xml : export of preemptive EDF with aperiodic tasks
- test_posix_scheduler.xml : export of preemptive POSIX scheduler with periodic and aperiodic tasks
- test_PCP_shared_resources.xml : export periodic tasks sharing PCP resources
- test_PIP_shared_resources.xml : export periodic tasks sharing PIP resources
- test_task_dependencies.xml : export task with scheduling task dependencies
- test_parametric_scheduler.xml : export user's defined scheduler with user's defined task parameters
- test_message.xml : export a system with task sending/receiving messages
- test_parametric_task.xml : export user's defined task activation rule
- test_buffers.xml : export tasks sharing a buffer


Hello World! I am Elena, a math teacher in Skopje (Macedonia) starting her career as a web developer. I am looking for a new exciting experience, either a freelance project or a full time job. I started learning web development last summer, and I have just finished a full - stack development course at Code Academy Macedonia. If this text got your attention, you can check some of my work on my Github , see my first personal project at Github , or contact me through my LinkedIn profile
Started teaching in september 2014, and still doing it. I've acquired excellent organizational skills. Being a teacher makes you evolve constantly by trying new and creative ways in teaching and learning
Started the program in October 2018 and finished in May 2019. It was a new and an exciting way to learn coding, JavaScript, HTML, CSS, DOM manipulations, AJAX, jQuery, a basic React, Angular and node.js. Here I got familiar with github.
I am looking for a job experience related to either freelance project or full time job. Currently, my focus is on Vue.js, you can check my first vue project in the My Work section on this web page


Object
Returns the active Twig::User in the Twig::Users object.
If active hasn’t been called before, or if action is :update, :sync, or :refresh, the Twig::User object with the numerically smallest id # in the Twig::Users.fetched Hash will be returned.
If there are no Twig::User objects in the fetched Hash, nil will be returned.
# File lib/twig/user.rb, line 51 def active(action=nil) if @active case action when :update, :sync, :refresh @active = first when nil @active else raise ArgumentError, "#{action} is not a valid argument for @active." end else @active = first end end
Returns a Hash of all Twig::User objects that have been fetched from the Twitter server in the format _id _ => Twig::User.
If fetched has not been called before, or if action is :clear, :empty or :destroy, it will be reset to an empty Hash object.
# File lib/twig/user.rb, line 26 def fetched(action=nil) if @fetched case action when :clear, :empty, :destroy @fetched = {} when nil @fetched else raise ArgumentError, "#{action} is not a valid argument for @fetched." end else @fetched = {} end end
Searches for a Twig::User object by id_sn (either Fixnum id # or String screen_name), and returns the Twig::User object if found.
find first searches through the fetched hash for the Twig::User, to check whether it’s already been fetched from the Twitter server.
If found in fetched the Twig::User object will be returned. If not found in fetched, find will query the Twitter server to check whether a user exists with the provided id_sn. If no user is found on the server, nil will be returned and an error will be raised.
# File lib/twig/user.rb, line 77 def find(id_sn, action=nil) unless [String, Fixnum, Twitter::User].member?(id_sn.class) raise "User must be inputted as either a screen_name(String), a user id#(Fixnum)," \ + " or a Twitter::User object." else case id_sn when Fixnum if @fetched[id_sn] @active = fetched[id_sn] output = @active else user_check = @client.user(id_sn) if user_check id = user_check.id fetched[id] = Twig::User.new(@client, user_check) @active = fetched[id] output = @active else output = nil raise "Twitter user ##{id} could not be found." end end when String match = nil #sort through fetched users fetched.each do |user_id, user_obj| if user_obj.info.screen_name == id_sn match = fetched[user_obj.info.id] break end end if match @active = match output = @active else user_check = @client.user(id_sn) if user_check id = user_check.id fetched[id] = Twig::User.new(@client, user_check) @active = fetched[id] output = @active else output = nil raise "Twitter user '#{user}' could not be found." end end when Twitter::User if fetched[id_sn.id] @active = fetched[id_sn.id] output = @active else user_check = @client.user(id_sn.id) if user_check id = user_check.id fetched[id] = Twig::User.new(@client, user_check) @active = fetched[id] output = @active else output = nil raise "Twitter user ##{user} could not be found." end end end end return output end
Returns the first Twig::User object in the Twig::Users.fetched Hash based on the Twig::User.info.id #.
# File lib/twig/user.rb, line 11 def first if fetched.size > 0 # Find the first object sorted by id fetched[fetched.sort {|a,b| a[1] <=> b[1]}[0][0]] else nil end end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.


Angular
HOME
BLOG
CONTACT
ABOUT
LOGIN PORTAL
LOGIN
LATEST NEWS
Random news running with a speed of light.
Random news running like the above one
Random news running like the above one
Random news running like just like being itself
JQuery App Demo
1st No.
2nd No.
Result
Subtract
Divide
Multiply
Add


QC for {{ searchname }}
Protein/peptide level QC
Summary
{{ tm.table(sumtable, tablefieldtitles) }}
Overlap
{% for feat in overlap %}
{{ featnames[feat] }}
{{ tm.table(overlap[feat], tablefieldtitles) }}
{% endfor %}
{% for graphtype in ["featyield", "isobaric", "pca"] %}
{% if graphtype in features['peptides'] or ('proteins' in features and graphtype in features['proteins']) %}
{{ titles[graphtype] }}
{% for feat in features %}
{{ featnames[feat] }}
{{ features[feat][graphtype] }}
{% endfor %}
{% if graphtype == "isobaric" and 'normfac' in features['proteins'] %}
Median centering
{{ features['proteins']['normfac']}}
{% endif %}
{% endif %}
{% endfor %}
{% if ptms|length %}
{% include 'ptmqc.j2' %}
{% endif %}


Get your brand respected: distribute vector logos
When we built Ellen’s website (www.physioellen.co.uk), she wanted to show her work accreditations as part of her professional details.
I wanted to avoid too much text to keep the whole page relatively light, so for this section, we decided to only display the logos of the physiotherapy societies she is a member of.
However, in order for those to render correctly on most screens, including on high density displays, I needed to find better versions than the logos those organisations use on their own websites.
Fortunately, they all had a brand management department, so I placed a request with each of them for either high resolution images, or better: vector files.
It turned out that for most of them, I could only get rasterised versions of their logos through the standard request process, and I had to insist quite a few times to obtain proper vector versions, in SVG or EPS format. But one of them simply refused entirely to give its logo as a vector file.
I have a feeling that it’s for fear of seeing vector images being modified that the process of obtaining higher quality images was hard in the first place, but for me, this is doing it the wrong way around.
While I can understand some caution is needed to ensure your brand is used appropriately, if you make it so difficult for anyone to obtain the correct version of your logo, you’re basically sending them back to their graphics design tools to try and recreate your logo themselves.
As a developer, I want the websites I create to look good on as many devices as I can optimise them for, and for this I need to have good quality images. Vector images ensures that I don’t have to worry about either shape or color, since they are infinitely scalable. Raster images, on the other hand, degrade quite quickly when resizing them, and need to be quite large to render correctly on high density screens.
If I was an organisation really caring about my brand and its visual elements, I’d make every attempt to have web developers or any other user of my logo obtain the right files as easily as possible! And I believe distributing the vector graphics for those logos is the best way to go about it!
What’s your take on it?


--- layout: home ---
Spock is a lightweight Haskell web framework inspired by Ruby's Sinatra. It provides a full toolbox including everything to get a quick start into web programming using Haskell.
Spock provides fast route dispatching in a type safe environment
Spock and all ecosystem packages are open sourced on Github. Feel free to review code or contribute!


Ognomy MVP Backend API Challenge
CHALLENGE OBJECTIVES
- Build the backend API for the Ognomy MVP project.
PROJECT BACKGROUND
- We are building an MVP of a mobile app that allows patients to schedule and participate in teleconsultations on their mobile device, and of a web app that allows doctors to view teleconsult schedules and participate in teleconsults from a desktop device.
- For the purpose of this challenge, we need to implement the backend that will support the MVP of this mobile app and web app.
- This MVP is the 1st...
Platforms: AWS
Technologies: MongoDB / Node.js / REST
Prize: 2100 (1400)
Registration Period: 2020-01-17 14:04 +00:00 - 2020-01-24 14:07 +00:00
Open for registration: No
Submissions Due: 2020-01-24 14:16 +00:00
Type: Code / develop
LINK


Playground should help during development to experiment and debug with the current sources without having to build everything.
Please disable the browser cache. F12 (developer Tools): disable cache


Joseph Karasek
Home
Portfolio
Experience
Interests
Contact
Contact
There are a lot of ways to keep in touch with me...
Oh no, still under construction...


Contact Your Congressperson
This example uses all the defaults.
Find
Contact Your Congressperson
This example explicitly lists the fields to be used.
Find


@()(implicit request: RequestHeader)
@import controllers.admin.routes.{UncachedAssets, UncachedWebAssets}
@admin_main("Programmatic advert performance", "Prod", isAuthed = true, loadJquery = false) {
Programmatic advert performance
This is the time taken for an individual advert slot to obtain a response from the server. There are multiple slots on a page, and many page views, so an average is calculated. For programmatic systems, such as 'prebid' or 'sonobi', the load time includes the additional network latency introduced by header-bidding.
The expectation is that prebid's latency increases with additional bidders, whilst sonobi's latency should not be affected by additional bidders. Both systems are expected to be slower than the standard 'waterfall' system; however they promise better CPM revenue.


List of all members.
Public Member Functions |
|
def | setupUi |
|
def | retranslateUi |
Public Attributes |
|
| centralwidget |
|
| explorersTab |
|
| projectExplorerTab |
|
| projectExplorerTreeWidget |
|
| layoutWidget |
|
| horizontalLayout |
|
| addFileButton |
|
| executionsTab |
|
| executionsExplorerTreeWidget |
|
| compileProgramButton |
|
| executeProgramButton |
|
| generateEquivalentProgramButton |
|
| unitTestRadioButton |
|
| label_2 |
|
| codeTextEdit |
|
| outputWindowTextEdit |
|
| label |
|
| refreshProjectButton |
|
| clearOutputButton |
|
| openProjectButton |
|
| newProjectButton |
|
| closeProjectButton |
|
| editProjectButton |
|
| menubar |
|
| menuFile |
|
| menuHelp |
|
| menuHerramientas |
|
| statusbar |
|
| actionNewProject |
|
| actionOpenProject |
|
| actionCloseProject |
|
| actionExit |
|
| actionBraHelp |
|
| actionAbout |
|
| actionOptions |
|
| actionEditProject |
The documentation for this class was generated from the following file:


Dev Bootcamp, Chicago, 2015 – Ruby, Javascript, Ruby on Rails, SQL, HTML, CSS
Queen’s University, Ontario, 2010-2011- Master of Public Administration
University of Manitoba, Winnipeg, 2004-2007 – Bachelor of Arts (advanced)
St. Cloud State University, St. Cloud, 2003, Java programming
Mound Westonka High School, Minnesota, 2001, Basic programming
Ontario Ministry of Infrastructure, Toronto, 2012-2013, Research Analyst
Stearns-Benton Employment & Training Council, 2003-2007, VISTA Researcher


"SWINE "(Hebrews hazir), regarded as the most unclean and the most abhorred of all animals (Leviticus 11:7; Isaiah 65:4; 66:3, 17; Luke 15:15, 16). A herd of swine were drowned in the Sea of Galilee (Luke 8:32, 33). Spoken of figuratively in Matthew 7:6 (see Proverbs 11:22). It is frequently mentioned as a wild animal, and is evidently the wild boar(Arab. khanzir), which is common among the marshes of the Jordan valley(Psalm 80:13).
"SWINE "(Hebrews hazir), regarded as the most unclean and the most abhorred of all animals (Leviticus 11:7; Isaiah 65:4; 66:3, 17; Luke 15:15, 16). A herd of swine were drowned in the Sea of Galilee (Luke 8:32, 33).
Spoken of figuratively in Matthew 7:6 (see Proverbs 11:22). It is frequently mentioned as a wild animal, and is evidently the wild boar(Arab. khanzir), which is common among the marshes of the Jordan valley(Psalm 80:13).


Advantages of antenatal care
To assess the state of health of the mother and the fetus early in pregnancy.
Get access to quality healthcare services using Healthic. Seasoned with renowned health practitioners.
To assess the state of health of the mother and the fetus early in pregnancy.
Eating a variety of foods from all food groups helps to meet up with the high nutrients needed during pregnancy.
Being able to care for the children born adequately in terms of time, attention and material resources.


‘Getting Started’ - Sam Fender
Getting Started” by Sam Fender perfectly encapsulates the spirit of starting the year on campus. An upbeat rock song about facing hardships and coming out on top, all while trying to prove yourself, it feels like an anthem for a hardworking college student eager to prove themselves and show people what they’re really made of.
‘Campus’ - Vampire Weekend
The name of the song is literally "Campus.” I just couldn’t resist.
‘New Kid in Town’ - Eagles
When you reach campus, it can feel like everyone already knows each other. You are quite literally the new kid in town, but you’ve got something to prove. You’re excited to meet new people, and you aren’t quite sure what lies ahead in all of your relationships. But, that’s the fun of it, as this Eagles song highlights.
‘Junior Varsity’ - Dayglow
In the excitement of leaving for college, sometimes you forget the things you’re leaving behind. Your childhood, your friends from home and all of your memories from high school. That’s why Dayglow’s soulful “Junior Varsity” might touch a nerve for incoming freshmen about to embark on their college journey, as it highlights the feeling of being “in the real world now” and “that change coming around.”
‘Youth’ - Glass Animals
“Youth” acts as a letter from a parent to a growing child, encouraging them to embrace their young life and the new friends they will make, which often hits close to home for college students who have to leave home and move far away from their parents to get to school.
‘Vienna’ - Billy Joel
It’s easy to get caught up in the excitement of college life. But, with college comes inevitable stress. Academics, friendship and oftentimes work, all catch up with students very quickly, leaving them overwhelmed by the stresses of life. “Vienna” serves as a reminder that you’re doing fine, and taking it easy every once in a while isn’t necessarily a bad thing.
‘Young Dumb & Broke’ - Khalid
Okay, you might not be “young dumb broke high school kids” anymore, but I’m willing to bet that you’re young and you don’t have a ton of money either (hey, tuition is expensive). But, that’s beside the point. The point of the song is about embracing youth and having fun while you’re young, which is one of the best parts of college.
‘Doses & Mimosas’ - Cherub
There’s not really much of a sentimental meaning for this song, it’s really just become associated with college parties as an anthem for good times.


Blind date: ‘I’d like to see her glasses fog up again’
by Guardian StaffJen, 26, technologist, meets Anna, 23, graduate trainee
Jen on Anna
What were you hoping for?
Either a nice time or a complete disaster, so I had an anecdote.
First impressions?
Super pretty. She has very beautiful eyes.  I was glad that she also felt out of place in the posh restaurant.
What did you talk about?
Being queer and not having role models when we were teenagers, drunken antics and our love of Keira Knightley. Three hours felt like five minutes.
Any awkward moments?
When I showed how much of a Londoner I am and thought Bristol was in the Midlands. We were “politely asked to move to the bar” because it was an hour after we should have left the table.
Good table manners?
No idea what makes good or bad table manners.
Best thing about Anna?
We have similar outlooks. A lot of what she was saying felt super-familiar. Also, her eyes.
Would you introduce her to your friends?
Yep! They are already begging to meet her.

What do you think she made of you?
I find it hard to make judgments about myself because I tend to be too harsh. She seemed to like me, though.
Did you go on somewhere?
We stayed in another bar until last orders at just before midnight.
And... did you kiss?
Yesssss! Outside the station. I am so bad at kissing, though, so I hope I didn’t embarrass myself. Her glasses fogged up, which was super cute. I’d like to see her glasses fog up again.
If you could change one thing about the evening, what would it be?
More time.
Marks out of 10?
10.
Would you meet again?
Yes!
Anna on Jen
What were you hoping for?
Someone fun and kind, and no long silences.
First impressions?
She seemed nervous, but so was I. I liked her colourful hair.
What did you talk about?

Any awkward moments?
We asked if we could get extra wine free and they said no, but we ordered it anyway. It cost much, much more than we expected.
Good table manners?
She handled the bewildering array of cutlery with grace.
Best thing about Jen?
The way she lights up when she talks about her volunteering.
Would you introduce her to your friends?
She’s lovely, so they’d like her.
Describe Jen in three words
Sweet, open, animated.
What do you think she made of you?
Either she thought I was funny, or she has a great fake laugh.
Did you go on somewhere?
To a pub down the road.
And… did you kiss?
A snog outside the station.
If you could change one thing about the evening, what would it be?
I’d shoot down the dessert wine idea.
Marks out of 10?
8.
Would you meet again?
We’re texting to make plans.
• Jen and Anna ate at Margot, London WC2
Fancy a blind date? Email<EMAIL_ADDRESS>If you’re looking to meet someone like‑minded, visit soulmates.theguardian.com


#assign docker="active">
Ebean provides docker test containers for Postgres, MySql, SqlServer, Oracle, Hana, Clickhouse, Redis
and ElasticSearch.
ebean-test-docker automatically manages the docker containers and sets them up ready for
running tests. As developers we just need to specify the platform (e.g. postgres) and Ebean
will do the rest:
- Start a docker container(s)
- Wait for the container to be ready
- Create the database and user setting any permissions as necessary
- When ready allow the tests to run
We can see/review what is occurring by increasing the logging for io.ebean.docker
to TRACE
. When we do that we can see log messages like:
```text
... Docker test container start and setup
15:15:02.537 INFO io.ebean.docker.commands.Commands - Container ut_postgres running with port:6432 db:test_ex user:test_ex mode:Create shutdown:
15:15:02.538 DEBUG io.ebean.docker.commands.Commands - docker exec -i ut_postgres pg_isready -h localhost -p 5432
15:15:02.645 DEBUG io.ebean.docker.commands.Commands - docker exec -i ut_postgres psql -U postgres -c select datname from pg_database
15:15:02.753 DEBUG io.ebean.docker.commands.Commands - docker exec -i ut_postgres psql -U postgres -c select rolname from pg_roles where rolname = 'test_ex'
15:15:02.871 DEBUG io.ebean.docker.commands.Commands - docker exec -i ut_postgres psql -U postgres -c select 1 from pg_database where datname = 'test_ex'
15:15:02.960 DEBUG io.ebean.docker.commands.Commands - create database extensions hstore,pgcrypto
15:15:02.960 DEBUG io.ebean.docker.commands.Commands - docker exec -i ut_postgres psql -U postgres -d test_ex -c create extension if not exists hstore
15:15:03.058 DEBUG io.ebean.docker.commands.Commands - docker exec -i ut_postgres psql -U postgres -d test_ex -c create extension if not exists pgcrypto
15:15:03.143 DEBUG io.ebean.docker.commands.Commands - waitForConnectivity ut_postgres ...
15:15:03.143 DEBUG io.ebean.docker.commands.Commands - checkConnectivity on ut_postgres ...
15:15:03.190 DEBUG io.ebean.docker.commands.Commands - connectivity confirmed for ut_postgres
15:15:03.190 DEBUG io.ebean.docker.commands.Commands - Container ut_postgres ready with port 6432
...
15:15:03.239 [main] INFO o.a.datasource.pool.ConnectionPool - DataSourcePool [db] autoCommit[false] transIsolation[READ_COMMITTED] min[2] max[200]
15:15:03.277 [main] INFO io.ebean.internal.DefaultContainer - DatabasePlatform name:db platform:postgres
... DDL Execution
15:15:03.618 [main] INFO io.ebean.DDL - Executing extra-dll - 0 statements
15:15:03.618 [main] INFO io.ebean.DDL - Executing db-drop-all.sql - 26 statements
15:15:03.649 [main] DEBUG io.ebean.DDL - executing 1 of 26 alter table if exists address drop constraint if exists fk_address_country_code
15:15:03.651 [main] DEBUG io.ebean.DDL - executing 2 of 26 drop index if exists ix_address_country_code
...
15:15:03.701 [main] INFO io.ebean.DDL - Executing db-create-all.sql - 28 statements
15:15:03.701 [main] DEBUG io.ebean.DDL - executing 1 of 28 create table address ( id bigserial not null, line1...
15:15:03.709 [main] DEBUG io.ebean.DDL - executing 2 of 28 create table contact ( id bigserial not null, first_n...
...
15:15:03.841 [main] INFO io.ebean.DDL - Executing extra-dll - 1 statements
15:15:03.842 [main] DEBUG io.ebean.DDL - executing 1 of 1 create or replace view order_agg_vw as select d.order_id as id, d.order_id as or...
```
To startup the docker containers we hook into the Ebean lifecycle. This means that it will "just work" whether
the tests are run from the IDE, maven, gradle or any build tool. In prior iterations the startup of docker
containers was hooked specifically into the maven lifecycle but this proved to be less than ideal.
In general this is providing a similar experience to the one we have when using H2 database. That is, we bring up
the database (if needed), setup the database, user, roles etc (if needed) get it ready for testing by running DDL
... and then run all tests.
On developer machines we generally keep the docker container running. We can do this by having a marker file
~/.ebean/ignore-docker-shutdown
.
On CI servers we want the docker containers to be stopped and removed at completion of tests. To do this we
set shutdown
to either remove
(to stop and remove the container) or stop
(to just stop the container).
```yml
ebean:
test:
shutdown: remove # stop | remove
platform: postgres # h2, postgres, mysql, oracle, sqlserver
ddlMode: dropCreate # none | dropCreate | create | migration | createOnly | migrationDropCreate
dbName: my_app
```
If we don't want to start and run a Docker container but instead test against some other existing database we can
do that via setting useDocker: false
.
The below configuration runs against an existing Postgres database. Typically when not using docker we need to
set the username, password and url to appropriate values.
When we are using useDocker: false
the database and user are expected to already exist.
```yml
ebean:
test:
useDocker: false ## DO NOT USE DOCKER
platform: postgres # h2, postgres, mysql, oracle, sqlserver
ddlMode: dropCreate # none | dropCreate | create | migration | createOnly | migrationDropCreate
dbName: test
postgres:
username: test
password: test
url: jdbc:postgresql://localhost:5432/test
```
<@next_edit "Platform" "platform" "/docs/testing/docker.html"/>


"""Theil Inequality metrics
"""
__author__ = "Sergio J. Rey<EMAIL_ADDRESS>"
# from pysal.lib.common import *
import numpy as np
__all__ = ['Theil', 'TheilD', 'TheilDSim']
SMALL = np.finfo('float').tiny
[docs]class Theil:
"""
Classic Theil measure of inequality
.. math::
T = \sum_{i=1}^n \left( \\frac{y_i}{\sum_{i=1}^n y_i} \ln \left[ N \\frac{y_i}{\sum_{i=1}^n y_i}\\right] \\right)
Parameters
----------
y : array (n,t) or (n,)
with n taken as the observations across which inequality is
calculated. If y is (n,) then a scalar inequality value is
determined. If y is (n,t) then an array of inequality values are
determined, one value for each column in y.
Attributes
----------
T : array (t,) or (1,)
Theil's T for each column of y
Notes
-----
This computation involves natural logs. To prevent ln[0] from occurring, a
small value is added to each element of y before beginning the computation.
Examples
--------
>>> import pysal.lib
>>> import numpy as np
>>> from pysal.explore.inequality.theil import Theil
>>> f=pysal.lib.io.open(pysal.lib.examples.get_path("mexico.csv"))
>>> vnames=["pcgdp%d"%dec for dec in range(1940,2010,10)]
>>> y=np.array([f.by_col[v] for v in vnames]).T
>>> theil_y=Theil(y)
>>> theil_y.T
array([0.20894344, 0.15222451, 0.10472941, 0.10194725, 0.09560113,
0.10511256, 0.10660832])
"""
[docs] def __init__(self, y):
n = len(y)
y = y + SMALL * (y == 0) # can't have 0 values
yt = y.sum(axis=0)
s = y / (yt * 1.0)
lns = np.log(n * s)
slns = s * lns
t = sum(slns)
self.T = t
[docs]class TheilD:
"""Decomposition of Theil's T based on partitioning of
observations into exhaustive and mutually exclusive groups
Parameters
----------
y : array (n,t) or (n, )
with n taken as the observations across which inequality is
calculated If y is (n,) then a scalar inequality value is
determined. If y is (n,t) then an array of inequality values are
determined, one value for each column in y.
partition : array (n, )
elements indicating which partition each observation belongs
to. These are assumed to be exhaustive.
Attributes
----------
T : array (n,t) or (n,)
global inequality T
bg : array (n,t) or (n,)
between group inequality
wg : array (n,t) or (n,)
within group inequality
Examples
--------
>>> import pysal.lib
>>> from pysal.explore.inequality.theil import TheilD
>>> import numpy as np
>>> f=pysal.lib.io.open(pysal.lib.examples.get_path("mexico.csv"))
>>> vnames=["pcgdp%d"%dec for dec in range(1940,2010,10)]
>>> y = np.array([f.by_col[v] for v in vnames]).T
>>> regimes=np.array(f.by_col('hanson98'))
>>> theil_d=TheilD(y,regimes)
>>> theil_d.bg
array([0.0345889 , 0.02816853, 0.05260921, 0.05931219, 0.03205257,
0.02963731, 0.03635872])
>>> theil_d.wg
array([0.17435454, 0.12405598, 0.0521202 , 0.04263506, 0.06354856,
0.07547525, 0.0702496 ])
"""
[docs] def __init__(self, y, partition):
groups = np.unique(partition)
T = Theil(y).T
ytot = y.sum(axis=0)
#group totals
gtot = np.array([y[partition == gid].sum(axis=0) for gid in groups])
mm = np.dot
if ytot.size == 1: # y is 1-d
sg = gtot / (ytot * 1.)
sg.shape = (sg.size, 1)
else:
sg = mm(gtot, np.diag(1. / ytot))
ng = np.array([sum(partition == gid) for gid in groups])
ng.shape = (ng.size,) # ensure ng is 1-d
n = y.shape[0]
# between group inequality
sg = sg + (sg==0) # handle case when a partition has 0 for sum
bg = np.multiply(sg, np.log(mm(np.diag(n * 1. / ng), sg))).sum(axis=0)
self.T = T
self.bg = bg
self.wg = T - bg
[docs]class TheilDSim:
"""Random permutation based inference on Theil's inequality decomposition.
Provides for computationally based inference regarding the inequality
decomposition using random spatial permutations. See :cite:`rey_interregional_2010`.
Parameters
----------
y : array (n,t) or (n, )
with n taken as the observations across which inequality is
calculated If y is (n,) then a scalar inequality value is
determined. If y is (n,t) then an array of inequality values are
determined, one value for each column in y.
partition : array (n, )
elements indicating which partition each observation belongs
to. These are assumed to be exhaustive.
permutations : int
Number of random spatial permutations for computationally
based inference on the decomposition.
Attributes
----------
observed : array (n,t) or (n,)
TheilD instance for the observed data.
bg : array (permutations+1,t)
between group inequality
bg_pvalue : array (t,1)
p-value for the between group measure. Measures the
percentage of the realized values that were greater than
or equal to the observed bg value. Includes the observed
value.
wg : array (size=permutations+1)
within group inequality Depending on the shape of y, 1 or 2-dimensional
Examples
--------
>>> import pysal.lib
>>> from pysal.explore.inequality.theil import TheilDSim
>>> import numpy as np
>>> f=pysal.lib.io.open(pysal.lib.examples.get_path("mexico.csv"))
>>> vnames=["pcgdp%d"%dec for dec in range(1940,2010,10)]
>>> y=np.array([f.by_col[v] for v in vnames]).T
>>> regimes=np.array(f.by_col('hanson98'))
>>> np.random.seed(10)
>>> theil_ds=TheilDSim(y,regimes,999)
>>> theil_ds.bg_pvalue
array([0.4 , 0.344, 0.001, 0.001, 0.034, 0.072, 0.032])
"""
[docs] def __init__(self, y, partition, permutations=99):
observed = TheilD(y, partition)
bg_ct = observed.bg == observed.bg # already have one extreme value
bg_ct = bg_ct * 1.0
results = [observed]
for perm in range(permutations):
yp = np.random.permutation(y)
t = TheilD(yp, partition)
bg_ct += (1.0 * t.bg >= observed.bg)
results.append(t)
self.results = results
self.T = observed.T
self.bg_pvalue = bg_ct / (permutations * 1.0 + 1)
self.bg = np.array([r.bg for r in results])
self.wg = np.array([r.wg for r in results])