Atomic vector error in R for $ symbol. Please find reproducible example below

I am receiving an error when I run the last like of this code

# Load relevant packages
    library("httr")
    library("XML")
    library("stringr")
    library("ggplot2")

    # Define image source
    img.url     = 'https://www.whitehouse.gov/sites/whitehouse.gov/files/images/first-family/44_barack_obama[1].jpg'
    # Define Microsoft API URL to request data
    URL.emoface = 'https://westus.api.cognitive.microsoft.com/emotion/v1.0/recognize'

    # Define access key (access key is available via: https://www.microsoft.com/cognitive-services/en-us/emotion-api)
    emotionKEY = 'xxx'

    # Define image
    mybody = list(url = img.url)

    # Request data from Microsoft
    faceEMO = POST(
      url = URL.emoface,
      content_type('application/json'), add_headers(.headers = c('Ocp-Apim-Subscription-Key' = emotionKEY)),
      body = mybody,
      encode = 'json'
    )

    # Show request results (if Status=200, request is okay)
    faceEMO

    # Reuqest results from face analysis
    Obama = httr::content(faceEMO)[[1]]
    Obama
    # Define results in data frame
    o<-as.data.frame(as.matrix(Obama$scores))

The error I receive after running o is Error in Obama$scores : $ operator is invalid for atomic vectors

Have you examined Obama using str()? What do you get?

The code above seems to assume that it will be a list-like object with a scores element, but instead you apparently have an atomic vector. I’d start by taking a look at the line where you create Obama:

Obama = httr::content(faceEMO)[[1]]

Running str() (or even View(), in RStudio) on httr::content(faceEMO) might help you figure out the right subsetting syntax to get the thing you are intending to get. Keep in mind the difference between double bracket and single bracket subsetting: single brackets preserve the structure of the object they subset, while double brackets simplify. See: http://adv-r.had.co.nz/Subsetting.html

str(Obama)
int 404

Yup, it’s a vector containing 404 integers. So I think you have to go back to the step where you’re pulling that object out of content(faceEMO) — seems that you need to be pulling out some other part (beyond that, there’s the possibility that maybe the faceEMO request didn’t quite work as desired, but better to take it one step at a time).

Did you look at the structure of content(faceEMO)?

for content(FaceEMO)
$message
[1] "Resource not found"

I am using another API now, but a rookie issue I am facing is that I am unable to convert my output into a dataframe.

library(devtools)
devtools::install_github("IndicoDataSolutions/IndicoIo-R")
library(indicoio)
setwd('C:/Users/Joel/Desktop/SJU/Semester 3/R programming')
emotionKEY = "xxx"
library(base64enc)
img <- file("C:/Users/Joel/Desktop/SJU/Semester 3/R programming/happily-surprised.jpg", "rb", raw=TRUE)
data <- base64encode(img)

emotion <- face_emotion(data,api_key = 'xxx')
emotion

ggplot(data=emotion, aes(x=emotion, y=Level)) +
geom_bar(stat="identity")

Error: data must be a data frame, or other object coercible by fortify(), not a list
How can I convert emotion into a DF.

when I run emotion:
$Happy
[1] 0.9994215

$Neutral
[1] 0.0005757142

$Sad
[1] 0.00000002021321

$Surprise
[1] 0.000002606573

The above is my output and i want to plot them

Here's an example of how to go from your list to something more plottable:

# Let's make a list similar to yours
lst <- list(
  CategoryA = rnorm(1),
  CategoryB = rnorm(1),
  CategoryC = rnorm(1),
  CategoryD = rnorm(1)
)

lst
#> $CategoryA
#> [1] -1.467648
#> 
#> $CategoryB
#> [1] 1.427778
#> 
#> $CategoryC
#> [1] -0.7641655
#> 
#> $CategoryD
#> [1] -0.9828783

# Note the structure
str(lst)
#> List of 4
#>  $ CategoryA: num -1.47
#>  $ CategoryB: num 1.43
#>  $ CategoryC: num -0.764
#>  $ CategoryD: num -0.983

# Now we convert it to a data frame
dfr <- as.data.frame(lst)

# Again, note the structure! Turns out, a data frame is just a
# special kind of list. Each element becomes a column.
str(dfr)
#> 'data.frame':    1 obs. of  4 variables:
#>  $ CategoryA: num -1.47
#>  $ CategoryB: num 1.43
#>  $ CategoryC: num -0.764
#>  $ CategoryD: num -0.983
    
# This shape isn't very useful for the sort of plot I think
# you want to make
dfr
#>   CategoryA CategoryB  CategoryC  CategoryD
#> 1 -1.467648  1.427778 -0.7641655 -0.9828783

library(tidyverse)

# There are plenty of ways to reshape the data frame,
# but I think `tidyr::gather()` is the easiest
gather(dfr, key = "Category", value = "Amount")
#>    Category     Amount
#> 1 CategoryA -1.4676481
#> 2 CategoryB  1.4277784
#> 3 CategoryC -0.7641655
#> 4 CategoryD -0.9828783

Created on 2018-11-25 by the reprex package (v0.2.1)

2 Likes

When I run a plot (dfr) after running this in my example, I receive the image(attached), im not sure how to interpret it as it does not entirely correlate with my original list in terms of chronology:
lst <- emotion
dfr <- as.data.frame(lst)
gather(dfr, key = "Category", value = "Amount")
plot(dfr)

Emoplot

1 Like

@jcblum Thank you for all your help. I would truly appreciate your feedback on how to interpret the above plot.

plot() is part of R's base graphics system. It is designed to guess what type of plot you want based on the class of object you pass to it (you can override some of these guesses by using its many parameters, or by using some of the other base plotting functions to build up your own plots). When you pass plot() a data frame without any other instruction, you get the result of the plot.data.frame() method. Here's what its documentation says:

This is intended for data frames with numeric columns. For more than two columns it first calls data.matrix to convert the data frame to a numeric matrix and then calls pairs to produce a scatterplot matrix. This can fail and may well be inappropriate: for example numerical conversion of dates will lose their special meaning and a warning will be given.

For a two-column data frame it plots the second column against the first by the most appropriate method for the first column.

So the upshot is that what you see above was plot()'s best guess based on the data frame it received. It's meant to be a matrix of scatterplots, plotting every variable against every other variable. Of course, since your data frame only has one value for each variable, it doesn't make a lot of sense!

If you go back to the ggplot2 code you tried before and make sure you're using it with a "long" format data frame, it should give you something more sensible (a bar plot). (The way to predictably get that in the base graphics system is to use barplot()):

library(tidyverse)

# Let's call the data frame you got from converting your list
# the "wide" format:
dfr_wide <- as.data.frame(
  list(
    CategoryA = rnorm(1),
    CategoryB = rnorm(1),
    CategoryC = rnorm(1),
    CategoryD = rnorm(1)
  )
)

# And we'll call the `gather`-ed version the "long" format:
dfr_long <- gather(dfr_wide, key = "Category", value = "Amount")

dfr_wide
#>   CategoryA CategoryB  CategoryC CategoryD
#> 1  0.249787 0.8104117 -0.5028831 -1.221933
dfr_long
#>    Category     Amount
#> 1 CategoryA  0.2497870
#> 2 CategoryB  0.8104117
#> 3 CategoryC -0.5028831
#> 4 CategoryD -1.2219327

# ggplot likes long-format data! 
# See: https://tidyr.tidyverse.org/articles/tidy-data.html
ggplot(data = dfr_long, aes(x = Category, y = Amount)) +
  geom_bar(stat = "identity")


# So does base `barplot()`
barplot(dfr_long$Amount)


# To get something sensible from `plot(dfr_long)`, the Category variable
# needs to be defined as a factor
dfr_long$Category <- factor(dfr_long$Category)
plot(dfr_long)

Created on 2018-11-25 by the reprex package (v0.2.1)

One more tiny note: In case it's not clear, you can pass anything you want (or nothing at all!) to the key and value parameters of gather() — all those parameters are doing is setting the names of the resulting columns in the new data frame. See the documentation for gather() to learn more.

2 Likes

On a different note, I'm happy to hear that I've helped you! :grin: And I know how it feels when you're struggling through something and you just need answers :sweat: . But it's not a great idea to nudge people for more help — it tends to come across as demanding, no matter how it's meant.

Keep in mind that people like me answer questions here entirely on our own time and for free, so we may not be available as quickly as you wish. There's not much you can do about that, but you can set yourself up to get the best, fastest help possible by taking care to be respectful of your helpers' time. That might look like: working to improve your questions (see: FAQ: Tips for writing R-related questions), learning how to make solid self-contained reproducible examples so helpers can understand your problem quickly and efficiently, and being patient with threads :grin:

3 Likes

I understand what you are referring to. Thank you and I will keep this in mind henceforth :slight_smile:

1 Like

RplotHappy

@jcblum Just one last question on this. I notice you were able to change the scales so as to represent all the catgegory amounts. In my case as you notice in this image it only plot Happy because all the others were 1/1000 lesser than the value of happy(0.9999). How can I change this?

Please note df:

  Category           Amount
1    Angry 0.00000010230325
2     Fear 0.00000007393743
3    Happy 0.99942147731781
4  Neutral 0.00057571416255
5      Sad 0.00000002021321
6 Surprise 0.00000260657316

By the way, I think the 404 in the original response was a "Not Found" HTTP error message, not a vector of 4 hundred and 4 integers. Especially since the $message component gave "Resource not found".

3 Likes

Ha! Nice catch :grinning:

Also kind of an object lesson in the utility of reproducible examples, though. Parsing error messages when you’re basically trying to run the code in your head is mistake-prone (we have computers for that... :grin:) . The pattern that I think might have helped avoid that bit of confusion would have been to center a reprex around dput() output of the object that was throwing the error.

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.