Are you a die-hard NBA fan and want to take a closer look at your favorite team’s performance? With the NBA API used with various developing platforms, you can do that and more. The NBA API is a stream of useful and sensational data for game lovers. From seasons to leagues, and teams to players, it can spew out a lot of data for you to consume. So why not put this basketball sports API into action and show you how to utilize the information you actually want from this API. retailers
Specifically, this article focuses on the basics of using the nba_api module in python, js, ruby, and php. This is a highly vigorous module that allows direct access to the NBA.com API. This information is intended to facilitate API users and new developers getting connect with the API Technology and with the Rapid API marketplace.
Rest assured, this article intends to give you an elementary insight into how we can use the API and what is possible with it. Furthermore, by the time you reach the end of this article, you should be able to connect the nba_api module, get player stats, and get team stats. Most of the code is extracted from other sources, which are properly cited and can be referred for more clarity if you would like to learn more.
This article focuses on the basics of using the nba_api module in python, js, ruby and php. This is a highly vigorous module which allows direct access to the NBA.com API. This information is intended to facilitate API users and new developers getting connect with the API Technology and with the Rapid API marketplace.
Rest assured, this article intends to give you an elementary insight into how we can use the API and what is possible with it. Furthermore, by the time you reach the end of this article you should be able to connect the nba_api module, get player stats, and get team stats. Most of the code is extracted from other sources, which are properly cited and can be referred for more clarity if you would like to learn more.
Using NBA API with Python
Getting Started (Installing nba_api)
Firstly, we install the nba_api in order for it to reconcile the version of python we are using. It is recommended using Anaconda (platform) as it is a full fledge version covering all aspects.
In order to install the code, access the anaconda prompt, command prompt (windows), or terminal (mac), and type:
pip install nba_api
Please Enter:
The nba_api takes off with the static data on teams and individual players. Every player and team have a particular ID and this is the most common way to recognise them when creating our own API calls later. This also happens to reveal miscellaneous API endpoints that can be targeted. Generally, the features are passed from the static data to the API endpoints as limitations and all of the endpoints are revealed here.
Getting Player & Team Ids
As mentioned earlier, we must know what we are looking for in order to access NBA.com API. We must have the respective ids of players and teams that we are trying to retrieve data for. In order to get these, the following code can be used:
from nba_api.stats.static import players player_dict = players.get_players() #Use ternary operator or write function #Names are case sensitive bron =[player for player in player_dic if player['full_name']=='LeBron James'][0] born_id = bron['id'] #find team ids from nba_api.stats.static import teams teams=teams.get_teams() GSW = [x for x in teams if x['full_name'] =='Golden State Warriors'][0] GSW_id =GSW['id']
As seen above, this code is specifically developed for Lebron James and the Golden State Warriors. And with this code we can easily access data for the respective player and his team.
Getting Game Data
Now we attempt to use playergamelog API endpoint to the game logs for Lebron
#First we import the endpoints #We Will be using Pandas dataframes to manipulates the data from nba_api.stats.endpoints import playergamelog import pandas as pd #Call the API endpoints passing in lebron's ID & which season gamelog_bron =playergamelog.PlayerGameLog(player_id='2544' , season='2018') #Convert gamelog object into a pandas #Can Also Convert to JSON to dictionary df_bron_games_2018 = game_log_bron.get_data_frames() #if want all seasons, you must imort SeasonAll parameters from nba_api.stats.library.parameters import SeasonAll game_log_bron_all = playergamelog.PlayerGameLog(player_id='2544' ,season=seasonAll.all) df__bron_games_all = game_log_bron_all.get_data_frames()
Lebron’s career game stats will now be accessible in a basic data frame.
Similarly, we now access Golden State’s game data through the following code by using the leaguegamefinder endpoint.
from nba_api.stats.endpoints import leaguegamefinder #this time we convert it to a dataframes in the same line of code GWS_games = leaguegamefinder.LeagueGameFinder(team_id_nullable=GWS_id).get_data_frames()[0]
Accessing Other Data
The examples given above should be enough to learn the basics of how to use NBA_API with python module. You can access an incredible amount of basketball data in a similar manner. Upon retrieving the list of endpoints, you will encounter a list which looks like this:
Using NBA API with PHP
NBA provides a long and detailed list of API endpoints which are very useful. This includes stats, scores and shot charts. For the purpose of this article, we will be focusing on the NBA findPlayer(str) API which is an endpoint revealing the game schedules and details for a particular game, both current and past games.
Getting Started
NBA.findPlayer(str) API reveals an object with the name of the player, their ID, and information on their team. All arrays in the NBA.stats namespace require that a single object passes through a parameter.
const NBA = require("nba"); const curry = NBa.findPlayer('Stephen Curry'); console.log(curry) /* logs the following: { firstName:'Stephen', lastName : 'Curry', playerId : 201939, teamId : 1610612744, fullName : 'Stephen Curry', } */ NBA.stats.playerInfo({PlayerID:curry.playerID}).then(console.log);
Stability Warning
In case of unstable API which are neither documented, the fundamental HTTP API can be or has already been altered without warning. Specifically, NBA has again and again denounced endpoints, or required headers have been added without which the requested data will fail to reveal. Further, this comes hustled with a (moderately) current list of present NBA players which is again subject to change — the exact contents of it should not be included in this library’s API contract.
Usability
Furthermore, the NBA’s API endpoints are a little heavy to use. This library tries to maintain a balance in being operational but not telling us how the data will be used. NBA sends the data in a tabular form, which reveals the column headers first then every result is displayed as a range of values that need to be synchronized with the proper header. This library makes a basic alteration to zip the header and values range into an object. It is fundamental to note since the various “result sets” that come back on a single endpoint seem kind of illogical. The fundamental HTTP API doesn’t seem to follow the best practices; rather it ties the endpoints directly to the facts required by particular tables and charts displayed on stats.nba.com. It can be complicated to collect the data you need for a explicit analysis from the various endpoints available.
Transport Layer
In some cases, a different transport layer can be used to handle HTTP requests. Otherwise, you have an HTTP client library which can be better than the one used here. If you want to get stats for the WNBA or the G League, the following code shows how to use the Transport method to generate a new user with your own transport function.
//here we are getting stats for the WNBA const nba = require("nba"); const getJSON = require("nba/src/get-json"); // for the G league, try "stats.gleague.nba.com" const newHost = "stats.wnba.com"; const transport = (url,params,options)=>{ // simply swap the host and then defer the rest to the built in getJSOn function const fixedURL =url.replace("stats.nba.com","stats.wnba.com"); return getJSON(fixedURL,params,options); }; // create anew stats client here with our WNBA transport const wnbaStats=nba.stats.withTransport(transport); (async () =>{ const result = await wnbaStats.playerInfo({PlayerID:"1628886"}); console.log(result); })();
Using NBA API with JavaScript
For the purpose of this article, now we will implement the usage of JavaScript (React and Node) for the purpose of which we will use NBA API for building a simple NBA scoreboard, which will provide real-time match score updates. Following is the development flow of my application from its initial stage down to the final stage where the product is developed.
Scoreboard Architecture
For the development to start, it is important to come up with a graphic view of the project. This image entails the flow of the scoreboard application.
Flow diagram of the scoreboard application
Evident from the diagram above, the architectural design looks very simple. Firstly, a user makes a request on the frontend (React.js) which transmits the user’s request to the backend (Node.js). Simultaneously a request call is also sent to NBA API from the backend which then process the user’s invitation and sends back a reply which is seen by the user.
Development Stage (Backend)
For this stage of development, we will focus more on the backend. We assume to already have Node.js and Express.js installed on our machine. The backend is designed in a manner to include controllers and routes folder. These folders individually have apiController.js and apiRouter.js files. The reason behind the backend is written in the controller file (apiController.js). It is probable to make a HTTP get request to the external api, which sends back the needed response.
const config = require('../config/config'); const unirest = require('unirest'); function getbballUpdates(req, res) { let unirest = require("unirest"); let req = unirest("GET",config.url); req.headers({ "x-rapidapi-host":config.host, "x-rapidapi-key":config.key }); req.end(function(result){ if(result.error) throw now Error(result.error); if(result.status==200){ let games =[ ]; let dataArray=result.body; let mainData=dataArray.data; mainData.forEach(data => { let date = data.date; let home_team=data.home_team.full_name; let home_team_score = data.home_team_score; let status = data.status; let season = data.season; let visitor_team = data.visitor_team.full_name; let visitor_team_score = data.visitor_team_score; let allGames={ date, home_team, home_team_score, status, season, visitor_team, visitor_team_score } games.push(allGames) }); return res.status(200).json(games); } }); } module.exports=getbballUpdates;
While the route file (apiRouter.js) processes all get requests from the frontend and receives the needed response from the controller.
const express = require('express'); const bballRouter = express.Router(); const bballController = require('../controllers/apiController'); bballRouter.route('/updates').get(bballController); module.exports = bballRouter;
Finally, the entry point into the backend is the index.js. It is sometimes referred to as the server.
const express = require('express'); require('dotenv').config(); const bodyParser=require('body-parser'); const app = express(); const cors=require('cors'); app.use(cors()) app.use(bodyParser.json()); const bballRouter = require('./routes/apiRouter'); app.get('/', (req, res) => { res.status(200).json('Basketball api updates') }); app.use('/api/vi', bballRouter); const PORT=process.env.PORT || 5000; app.listen(PORT, () => { console.log('server running on port $(PORT)') });
Using NBA API with Ruby on Rails
In order to reveal the process of using NBA_API with Ruby, let’s take into consideration how to rank every basketball player based on three attributes in order:
- The player’s individual rating (1–10)
- The player’s team rating (1–10)
- The player’s conference rating (1–10)
- The player’s ID (any number)
Example of a few players, ranked in this way:
Player 1: rating 10, team rating 10, conference rating 10 Player 2: rating 10, team rating 10, conference rating 5 Player 3: rating 10, team rating 5, conference rating 10 Player 4: rating 10, team rating 5, conference rating 5 Player 5: rating 5, team rating 10, conference rating 10 Player 6: rating 5, team rating 10, conference rating 5
All players are sorted out inexpensively through this approach as compared to taking onboard a naive approach. Let’s assume there are 1,000,000 players to evaluate. Using a sorting nested out properly, we could construct something like this:
Player = Struct.new(:id, :rating, :team_rating, :conf_rating) # setup id = 1.step @players = 1_000_000.times.map do Player.new(id.next, rand(0 . . 10), rand(0 . . 10), rand(0 . . 10)) end # I want to sort by player rating descending, # then team rating descending, # then conference rating descending, # then by id descending. Def sort_natively @players.group_by(&:rating).sort.reverse.map do |rb| rb[1].group_by(&:team_rating).sort.reverse.map do |tb| tb[1].group_by(&:conf_rating).sort.reverse.map do |cb| cb[1].group_by(&:id_rating).sort.reverse.map do |ib| ib[1] end end.flatten end.flatten end.flatten end
Average sorting of 1,000,000 records using this method takes ~2000 ms. It is not bad, considering the size of the sample. However, the array structure uses a lot of memory. This is the most common issue faced in using Ruby applications. All in all, it’s a practical procedure for smaller data sets, but that can be improved.
We could sort using Array.sort_by, which is better to look at, and passing in an array of values to sort on:
@players.sort_by do |p| [p.rating, p.team_rating, p.conf_rating, p.id] end.reverse
In low-memory environments this may come in handy, but let’s explore another sorting method, one that saves on both time and memory by utilizing ingenious operations.
If you’re not familiar with such operations, they are extensively used in lower-level languages like C to achieve operations on bits. In Ruby, the main operators are:
& -> AND | -> OR ^ -> XOR ~ -> NOT << -> LEFT SHIFT >> -> RIGHT SHIFT
For this article, we only need an explainer on | (OR) and << (LEFT SHIFT)
If you’ve understood so far, you perhaps have a pretty good knowledge of how computers work, and that bits are just 1s and 0s.
Bitwise | (OR) basically takes two arguments, a first and second bit, and returns a new bit with the 1s from the first bit, and adds in the 1s from the second bit where they are missing. For example:
integer | bit | Operator | other bit | new bit | result 0 | 0000 | | | 0001 | 0001 | 1 1 | 0001 | | | 0001 | 0000 | 1 2 | 0010 | | | 0001 | 0011 | 3 3 | 0011 | | | 0001 | 0011 | 3 4 | 0100 | | | 0001 | 0101 | 5 5 | 0101 | | | 0010 | 0111 | 7 10 | 1010 | | | 0101 | 1111 | 15
Bitwise << (LEFT SHIFT) , on the other hand, shifts a bit left by a certain number of places, adding 0s to the end of the bit. For example:
integer | bit | Operator | number | new bit | integer 1 | 1 | << | 1 | 10 | 2 2 | 10 | << | 1 | 100 | 4 3 | 11 | << | 1 | 110 | 6 10 | 1010 | << | 2 | 101000 | 40
We can use these two operations to make our sorting algorithm a little cleverer. Since the sort superiority is always rating > team rating > conf_rating > id, it follows that a player with rating 10 will always be ranked above a player with ranking 9, and so on, no matter what the other ratings are. Between players rated the same, the player with a better team rating will be overall rated higher, etc.
To achieve this ranking using bitwise operators, we should add a new parameter, bit_rank, to the Player struct. The new code looks like this:
Player = Struct.new( :id, :rating, :team_rating, :conf_rating, :bit_rank # <- new attribute ) # setup id = 1.step @players = 1_000_000.times.map do Player.new(id.next, rand(0 . . 2), rand(0 . . 2), rand(0 . . 2)) end #now, calculate the bit rank for each player @players.each do |p| p.bit_rank = p.rating << 30 | p.team_rating << 25 | p.conf_rating << 20 | p.id end
In short, this new bit_rank attribute is a number (a big one, at that) that represents the overall rating of the player. We’re shifting the rating over 30 places, the team rating 25 places, the conference rating 20 places, and then running a bitwise OR on all three plus the ID. For example, a player with ID 1 and rated 10 in all three categories would have a bit_rank of 11_083_448_321. This is intuitive when looking at the bit representation of this value, which is:
01010010100101000000000000000000001 01010 01010 01010 0000000000000000000001 ^ ^ ^ ^ | | | |___ player ID = 1 | | | | | |___ player conference rating (10 = 010110) | | | |___ player team rating (10 = 010110) | | ___ player rating (10 = 010110)
That same player with all 5s would have a bit_rank of 5_541_724_161, which, when seen in the bit lens:
00101001010010100000000000000000001 00101 00101 00101 00000000000000000001 ^ ^ ^ ^ | | | |___ player ID = 1 | | | | | |___ player conference rating (10 = 01010) | | | |___ player team rating (10 = 01010) | | ___ player rating (10 = 01010)
…makes sense.
The bit_rank embeds the sort precedence into itself, where the player’s respective ratings are shifted into appropriate areas of the bit rank, and they are all shifted far enough left to still sort by ID at the end.
Now that we understand what the bit_rank is doing, let’s see what code would be needed to run such a complex operation:
@players.sort_by(&:bit_rank)
Yep, that’s it. Since bit_rank is an attribute of the Struct, it can be called using the old Symbol.to_proc ruby magic. If we were in Rails land, this could be an attribute of a model, making sorting and ranking very easy.
Using this method, though, really shines when you look at the timing and memory usage.
On average, adding this attribute to each of the million Player objects adds 200ms to the setup stage, but sorting based on this attribute reduces the sort time to 500ms. In total, what was a 800ms build + 2000ms sort = 2800ms operation is now 1000ms build + 500ms sort = 1500ms! We cut 1300ms off the sort time, for a 46% improvement! This amount of efficiency is multiplied the more we add attributes to sort by, as well.
Memory usage, though, is unbelievable. To refresh, the original naive sort used up 219MB of memory, mostly because it was sorting into 10 + (10*10) + (10*10*10) + (10*10*10*10) = 11,110 separate, sorted arrays. When using the bit_rank sort method, our operation only uses 16MB, or 203MB less, a 92% decrease, since we don’t have any nested arrays. In reality, the whole of the memory footprint comes from building the array of 1,000,000 players.
Using this method is great for simple sort precedences on large data sets, provides an easy way to sort on multiple values, and can really be helpful in memory-deprived environments.
Leave a Reply