The project’s purpose is to develop an R package. This package provides an R wrapper for the Pokémon API. It is specifically for getting, filtering, and summarizing the following information for each of the generation 1 Pokémon.

  • pokedex index number (idx)
  • pokemon species (pokemon)
  • API URL for pokemon species (speciesURL)
  • habitat
  • type
  • top 5 moves (moves)

More information on the API documentation can be found at the official website: Poke API

API Call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Creating pokeAPI get requests
#'
#' The poke_api() function takes in variable part of the path required
#' for the GET request to the pokeAPI servers.
#'
#' @import jsonlite httr tidyverse stringr dplyr
#'
#' @param path The pokeAPI parameter to pull data for
#'
#' @return The json response from the pokeAPI
#' @export
#'
#' @examples
#' poke_api("/pokemon-species/1")

poke_api <- function(path){
  out <- tryCatch(
    {
      url <- modify_url("https://pokeapi.co", path=paste("/api/v2",path, sep=""))
      response <- GET(url, timeout(10))
      if (http_error(response)){
        if (round(status_code(response)/100,0)==5 | path==500){ # NOTE: all path==500 checks are for testing purposes only (allows simulation of inability to reach API)
          if (path == 500){
            # If 500 was passed in set the response to 500 so that the while loop can be tested
            response = 500
          }
          # If the error status code is in the 500 range attempt to call the API up to 5 more times, with a timed delay between calls
          delayTime <- 1
          while (round(status_code(response)/100,0)==5 & delayTime<=16){
            Sys.sleep(delayTime) # Delay time between calls
            response <- GET(url, timeout(10)) # Attempting to reach the API again
            # Exponential backoff of request time +/- randomly selected value between 0+5% of current delay time
            delayTime = delayTime*2+runif(1, -delayTime*0.05, delayTime*0.05)
            # For Testing when the API can't be reached
          }
          if (round(status_code(response)/100,0)==5 | path==500){
            stop("Unable to reach API", call. = FALSE)
          }
        } else if (round(status_code(response)/100,0)==4){
          stop("Invalid Input Path", call. = FALSE)
        }
      }
      return(response)
    },
  error=function(cond){
    print(gsub("\n","",gettext(cond)))
  })
}

Creating Data Frame to Store API Data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#' Create a Data Frame with Aggregated Data from pokeframe
#'
#' The poke.summary() function counts the number of Pokémon and mean of capture rate grouped
#' by either 'habitat' or type' from pokeframe data frame.
#'
#'
#' @param pokeframe The data frame of pokemon to summarise
#' @param infoType The type of information to summarise 'habitat' or 'type'
#'
#' @return A data frame
#' @export
#'
#' @examples
#' \dontrun{
#' poke.summary(pokeframe, 'habitat')
#' poke.summary(pokeframe, 'type')
#' }

poke.summary <- function(pokeframe, infoType){
  if (infoType == "habitat"){
    # Count of pokemon in each habitat
    group_by(pokeframe, habitat) %>% summarise(pokemonCount=n(), meanCaptureRate=round(mean(captureRate, na.rm = TRUE)))
  } else if (infoType == "type"){
    # Count of pokemon in each type
    group_by(pokeframe, type) %>% summarise(pokemonCount=n(), meanCaptureRate=round(mean(captureRate, na.rm = TRUE)))
  } else{
    print("Invalid Request")
  }
}

Creating Function to Filter Data Frame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#' Create a Data Frame Filtered by Pokémon
#'
#' The poke.filter() function filters the pokeframe data frame by pokemon names
#' selected in a vector,
#'
#'
#' @param pokeframe The data frame of pokemon to summarise
#' @param vec the name(s) of the pokemon being filtered, accepts a string for a single pokemon, or a list for multiple
#'
#' @return A data frame
#' @export
#'
#' @examples
#' \dontrun{
#' poke.filter(pokeframe, "jigglypuff")
#' poke.filter(pokeframe, c("pikachu", "bulbasaur", "charmander", "squirtle"))
#' }

poke.filter <- function(pokeframe, vec){
  if (is.data.frame(pokeframe)){
    # Checks if the passed in pokeframe is a data frame
    pokeFilter <- subset(pokeframe, pokemon %in% vec)
    if (nrow(pokeFilter) > 0){
      # Checks if any of the pokem  on to be filtered for were in the passed in pokeframe
      if (nrow(pokeFilter) == length(vec)){
        # Returns the filtered dataframe if all pokemon passed in were found
        pokeFilter
      } else{
        # Notifies the user if any pokemon were not found and returns the dataframe of the pokemon that were found
        print("Some of the requested pokemon were not generation 1, the generation 1 pokemon are listed below")
        pokeFilter
      }
    } else{
      # If none of the desired pokemon were found
      if (length(vec)>1){
        # If the filter was for more than 1 pokemon
        print("Requested pokemon are not from generation 1")
      } else{
        # If the filter was for 1 pokemon
        print("Requested pokemon is not from generation 1")
      }
    }
  } else{
    # If the passed in pokeframe isn't a data frame
    print("pokeframe must be a data frame")
  }
}

Installation

Developer GitHub Version

library(devtools)
devtools::install_github("nguyeneva/data534_project/pokeWrapper")

Attention The devtools package will need to be installed.

Loading pokeWrapper Library

library(pokeWrapper)

pokeframe Data Frame

Before anything can be done a data frame (referred to as a pokeframe) storing all of the generation 1 Pokémon needs to be created. A pokeframe can be created using the following commands.

As the pokeframe initializes the R Console will show a counter, once the counter reaches 151 (the number of generation 1 pokemon) the pokeframe initialization is complete. Expect this initialization to take approximately 4 minutes.

pokeframe <- initializeDataFrame()

Sample Output

head(pokeframe)
##   idx   pokemon                                     speciesURL     habitat
## 1 149 dragonite https://pokeapi.co/api/v2/pokemon-species/149/ waters-edge
## 2 148 dragonair https://pokeapi.co/api/v2/pokemon-species/148/ waters-edge
## 3 143   snorlax https://pokeapi.co/api/v2/pokemon-species/143/    mountain
## 4 141  kabutops https://pokeapi.co/api/v2/pokemon-species/141/         sea
## 5 139   omastar https://pokeapi.co/api/v2/pokemon-species/139/         sea
## 6 136   flareon https://pokeapi.co/api/v2/pokemon-species/136/       urban
##   captureRate           type
## 1          45 flying, dragon
## 2          45         dragon
## 3          25         normal
## 4          45    water, rock
## 5          45    water, rock
## 6          45           fire
##                                                   moves
## 1     fire-punch,ice-punch,thunder-punch,razor-wind,cut
## 2               bind,slam,headbutt,horn-drill,body-slam
## 3 mega-punch,pay-day,fire-punch,ice-punch,thunder-punch
## 4         scratch,razor-wind,swords-dance,cut,mega-kick
## 5        bind,headbutt,horn-attack,horn-drill,body-slam
## 6       sand-attack,headbutt,tackle,body-slam,take-down

Filtering Data Frame poke.filter

Once the pokeframe has been initialized the data can be filtered using the poke.filter() function for any generation 1 Pokémon. The first argument is the pokeframe and the second argument is either a single Pokémon or a list of Pokémon.

For example: Filtering individual Pokémon

poke.filter(pokeframe, "jigglypuff")
##    idx    pokemon                                    speciesURL   habitat
## 58  39 jigglypuff https://pokeapi.co/api/v2/pokemon-species/39/ grassland
##    captureRate          type                                             moves
## 58         170 fairy, normal pound,double-slap,mega-punch,fire-punch,ice-punch

Filtering multiple Pokémon

poke.filter(pokeframe, c("dragonite","snorlax"))
##   idx   pokemon                                     speciesURL     habitat
## 1 149 dragonite https://pokeapi.co/api/v2/pokemon-species/149/ waters-edge
## 3 143   snorlax https://pokeapi.co/api/v2/pokemon-species/143/    mountain
##   captureRate           type
## 1          45 flying, dragon
## 3          25         normal
##                                                   moves
## 1     fire-punch,ice-punch,thunder-punch,razor-wind,cut
## 3 mega-punch,pay-day,fire-punch,ice-punch,thunder-punch

Incorrect Inputs for the poke.filter Function

If an incorrectly spelled Pokémon name, or a non-generation 1 Pokémon is passed in amoung a list of generation 1 Pokémon then only the correctly spelled, or generation 1 Pokémon will be filtered for, and a message indicating that some of the Pokémon are not from generation 1. If none of the Pokémon passed in are from generation 1 a message indicating this will be printed to the R Console.

If the pokeframe passed into the function is not a data frame then a message will be printed on the R Console indicating that the pokeframe must be a data frame.

Using the poke.summary Function

The poke.summary() function can be used to provide a data frame summary for the following information:

  • Habitat:
    • Count of Pokémon per habitat
    • Mean capture rate per habitat
  • Pokemon Type:
    • Count of Pokémon per type
    • Mean capture rate per type

For example: Using habitat option

poke.summary(pokeframe, 'habitat')
## # A tibble: 9 x 3
##   habitat       pokemonCount meanCaptureRate
##   <chr>                <int>           <dbl>
## 1 cave                     8             128
## 2 forest                  21             126
## 3 grassland               35             111
## 4 mountain                18              94
## 5 rare                     5              11
## 6 rough-terrain            8             140
## 7 sea                     15             105
## 8 urban                   22              88
## 9 waters-edge             19             110

Using type option

poke.summary(pokeframe, 'type')
## # A tibble: 38 x 3
##    type            pokemonCount meanCaptureRate
##    <chr>                  <int>           <dbl>
##  1 bug                        3             140
##  2 dragon                     2              45
##  3 electric                   6             101
##  4 fairy                      2              88
##  5 fairy, normal              2             110
##  6 fairy, psychic             1              45
##  7 fighting                   7              96
##  8 fighting, water            1              45
##  9 fire                      10              96
## 10 flying, bug                2              45
## # … with 28 more rows

Incorrect Inputs for the poke.summary Function

If an incorrect parameter is passed into the poke.summary function a message will be printed in the R Console stating that the request was invalid.

Plotting with pokeWrapper

The summary data can be plotted through the following steps:

  1. Create a pokeframe
  2. Run the poke.summary() function on the desired parameters and store the resulting data frame
  3. Plot the resulting data frame

Code Example:

pokeframe <- initializeDataFrame()
habitatSummary <- poke.summary(pokeframe, 'habitat')
barplot(habitatSummary$pokemonCount, names.arg=habitatSummary$habitat, las=2,col="#FFCC33")
par(mar=c(7,3,3,0))
mtext(side=3, line=0.5, "Count of Pokemon by Habitat", col="red", font=3, cex=2)
mtext(side=1, line=4.5, "Habitat", col="blue", font=2,cex=1.2)
mtext(side=2, line=2, "Count of Pokemon", col="blue", font=2, cex=1.2)

png