Trying to pull random items from a nested dictionary - dictionary

I'm new, I've only been learning for a week now. I've been looking through online resources and now I'm trying to make a small cat generator, I can get it to randomize the breeds like I want but I can't get it to give me the color, coat length, pattern
import random
breeds = {
'bengal': {
'colors' : ['red', 'black', 'grey'],
'length': ['short', 'medium', 'long'],
'pattern': ['spotted', 'marbled']
},
'tabbycat': {
'colors' : ['grey', 'blue', 'silver'],
'length' : ['short', 'medium', 'long'],
'pattern' : ['mackeral', 'classic']
}
}
breed = random.choice(list(breeds))
color = random.choice(list(breeds['colors']))
print(breed.title())
print(color.title())
This code tells me KeyError: 'colors', I've also seen 'colors' not defined, so i'm having issues accessing the nested bits and I'm not sure why.

The list(breeds) function just gives you this: ['bengal', 'tabbycat'], which clearly does not have "colours" in it.
Think about how dictionaries are accessed - dictionaries have a key that holds a value. You would need to access the "colors" key which is inside a dictionary inside another dictionary, meaning you would need two keys - the first being the breed (chosen by the line breed = random.choice(list(breeds)), and the second key being "colors" (returns the list of possible colours).
A fixed version including coat length and pattern might look something like this:
import random
breeds = {
'bengal': {
'colors' : ['red', 'black', 'grey'],
'length': ['short', 'medium', 'long'],
'pattern': ['spotted', 'marbled']
},
'tabbycat': {
'colors' : ['grey', 'blue', 'silver'],
'length' : ['short', 'medium', 'long'],
'pattern' : ['mackeral', 'classic']
}
}
breed = random.choice(list(breeds))
color = random.choice(breeds[breed]["colors"])
coat_length = random.choice(breeds[breed]["length"])
pattern = random.choice(breeds[breed]["pattern"])
print(breed.title())
print(color.title())
print(coat_length.title())
print(pattern.title())

Related

associative arrays in openscad?

Does openscad have any language primitive for string-keyed associative arrays (a.k.a hash maps, a.k.a dictionaries)? Or is there any convention for how to emulate associative arrays?
So far all I can think of is using vectors and using variables to map indexes into the vector to human readable names. That means there's no nice, readable way to define the vector, you just have to comment it.
Imagine I want to write something akin to the Python data structure:
bobbin_metrics = {
'majacraft': {
'shaft_inner_diameter': 9.0,
'shaft_outer_diameter': 19.5,
'close_wheel_diameter': 60.1,
# ...
},
'majacraft_jumbo': {
'shaft_inner_diameter': 9.0,
'shaft_outer_diameter': 25.0,
'close_wheel_diameter': 100.0,
},
# ...
}
such that I can reference it in model definitions in some recognisably hash-map-like way, like passing bobbin_metrics['majacraft'] to something as metrics and referencing metrics['close_wheel_diameter'].
So far my best effort looks like
# Vector indexes into bobbin-metrics arrays
BM_SHAFT_INNER_DIAMETER = 0
BM_SHAFT_OUTER_DIAMETER = 1
BM_CLOSE_WHEEL_DIAMETER = 2
bobbin_metrics_majacraft = [
9.0, # shaft inner diameter
19.5, # shaft outer diameter
60.1, # close-side wheel diameter
# ....
];
bobbin_metrics_majacraft_jumbo = [
9.0, # shaft inner diameter
25.0, # shaft outer diameter
100.0, # close-side wheel diameter
# ....
];
bobbin_metrics = [
bobbin_metrics_majacraft,
bobbin_metrics_majacraft_jumbo,
# ...
];
# Usage when passed a bobbin metrics vector like
# bobbin_metrics_majacraft as 'metrics' to a function
metrics[BM_SHAFT_INNER_DIAMETER]
I think that'll work. But it's U.G.L.Y.. Not quite "I write applications in bash" ugly, but not far off.
Is there a better way?
I'm prepared to maintain the data set outside openscad and have a generator for an include file if I have to, but I'd rather not.
Also, in honour of April 1 I miss the blink tag and wonder if the scrolling marquee will work? Tried 'em :)
I played around with the OpenSCAD search() function which is documented in the manual here;
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
The following pattern allows a form of associative list, it may not be optimal but does provide a way to set up a dictionary structure and retrieve a value against a string key;
// associative searching
// dp 2019
// - define the dictionary
dict = [
["shaft_inner_diameter", 9.0],
["shaft_outer_diameter", 19.5],
["close_wheel_diameter", 60.1]
];
// specify the serach term
term = "close_wheel_diameter";
// execute the search
find = search(term, dict);
// process results
echo("1", find);
echo ("2",dict[find[0]]);
echo ("3",dict[find[0]][1]);
The above produces;
Compiling design (CSG Tree generation)...
WARNING: search term not found: "l"
...
WARNING: search term not found: "r"
ECHO: "1", [2, 0]
ECHO: "2", ["close_wheel_diameter", 60.1]
ECHO: "3", 60.1
Personally, I would do this sort of thing in Python then generate the OpenSCAD as an intermediate file or maybe use the SolidPython library.
An example of a function that uses search() and does not produce any warnings.
available_specs = [
["mgn7c", 1,2,3,4],
["mgn7h", 2,3,4,5],
];
function selector(item) = available_specs[search([item], available_specs)[0]];
chosen_spec = selector("mgn7c");
echo("Specification was returned from function", chosen_spec);
The above will produce the following output:
ECHO: "Specification was returned from function", ["mgn7c", 1, 2, 3, 4]
Another very similar approach is using list comprehensions with a condition statement, just like you would in Python for example. Does the same thing, looks a bit simpler.
function selector(item) = [
for (spec = available_specs)
if (spec[0] == item)
spec
];

Preallocating a dict of dicts

When I run #code_warntype on the following function (Shown in bold are the expressions that are likely raising the red flags.)
function cardata(df::DataFrame,Emission_val::Float64,search_cars::Dict{String,Tuple{Int64,Int64}}=Dict("Car1" => (1000,10000), "Car2" => (1000,50000), "Car3" => (1000,6000)),
all_cars::Array{String,1}=["Car1","Car2","Car3","Car4","Car5","Car6"])
**species = Dict()**
# The data file containing car information of interest
car_library = joinpath(path,"cars.csv")
df_car_data=CSV.read(car_library,header=["Model","Velocity","Emission_Value","Mass","Column6"],delim='\t')
#delete unused column
deletecols!(df_car_data, :Column6)
#create a new column with only the car Identifier name
df_car_data[:Identifier_car]=[split(i,r"[0-9]+")[1] for i in df_car_data[:Model]]
#get the properties of all_cars from the cars_data table
for search_models in all_cars
**cars[search_models] = Dict()**
for i in 1:1:length(df_cars_data[1])
num = split(df_cars_data[:Model][i],r"[0-9]+")[1]
alpha = split(df_cars_data[:Model][i],r"[a-zA-Z]+")[2]
if ( num == search_models )
species[num][alpha] = df_car_data[:Velocity][i]
end
end
end
end
I get the following warning highlighted in red:
Body::Tuple{Dict{Any,Any},Union{DataFrame,DataFrameRow{DataFrame,Index}},Any,Any}.
How to preallocate the types for dicts in such a case, assuming that I know the length of data that will populate the dict?
You have not provided a minimal working example.
Have a look at the code below. Note that for efficiency reasons
it is recommended to use Symbol a the key rather than String
species = Dict{Symbol,Dict{Symbol,Float64}}()
group = get!(()->Dict{Symbol,Float64}(),species,Symbol("audi"))
group[Symbol("a4")]=10.5
group[Symbol("a6")]=9.5
And now printing the output:
julia> println(species)
Dict(:audi=>Dict(:a6=>9.5,:a4=>10.5))

How to use S4 object programming in R

What's wrong with my R script? I'm trying to use a vector of user-defined objects (here a vector of "Page" objects) within another user-defined object (here a "Book" object)
setClass("Page",
slots = c(PageNo = "numeric", #scalar
Contents = "character") #vector of strings
)
setClass("Book",
slots = c(Pages = "vector", # Something wrong here? vector of pages ? "Page" or vector" or "list"
Title = "character") #vector of strings
)
setGeneric(name="AddPage", def=function(aBook, pageNo){standardGeneric("AddPage")})
setMethod(f="AddPage", signature="Book",
definition=function(aBook, pageNo)
{
page1 = new("Page")
page1#PageNo = pageNo
aBook#Pages = c(aBook#Pages, page1) # Something wrong here?
}
)
book1 = new("Book")
book1#Title = "Sample Book"
book1
book1#Pages
AddPage(book1, 1)
AddPage(book1, 2)
book1#Pages
Remember that R does not use reference semantics, so AddPage(book1, 1) creates a copy of book1, and updates that. In the method you don't return the updated object, and book1 remains unchanged.
Update the method so that it returns the modified object
setMethod(f="AddPage", signature="Book",
definition=function(aBook, pageNo)
{
page1 = new("Page")
page1#PageNo = pageNo
aBook#Pages = c(aBook#Pages, page1) # Something wrong here?
aBook
}
)
and assign the return value to the old variable
book1 = AddPage(book1, 1)
But this is a very inefficient approach -- the line aBook#Pages = c(aBook#Pages, page1) makes a copy of all existing pages (on the right-hand side, to create a longer vector; this will scale with the square of the number of Pages added to the book) and then copies the entire Book (for the assignment). In addition, creating individual objects is expensive and does not exploit R's 'vectorization'. A first step is to think of the object 'Page' as instead 'Pages', where the object models the columns rather than rows of a data frame. 'Book' then doesn't have vector of Page objects, but a single Pages object. This also implies a different approach to creating your 'book'.

Aggregate by match ObjectId in rmongodb

I have trouble with an aggregation involving an ObjectId. This are the pipelines:
{'$match' : {'likes.id' : ObjectId('50e99acfb35de75402002023')}}
{'$project' : {'likes.id' : 1, '_id' : 0}}
{'$unwind' : '$likes'}
{'$group' : {'_id' : '$likes.id', 'count' : {'$sum':1}}}
{'$sort' : {'_id' : 1}}
My attempt to write it in R using rmongodb is:
pipe_1 <- mongo.bson.from.JSON('{"$match" : {"likes.id" : { "$oid" : "50e99acfb35de75402002023" }}}')
pipe_2 <- mongo.bson.from.JSON('{"$project" : {"likes.id" : 1, "_id" : 0}}')
pipe_3 <- mongo.bson.from.JSON('{"$unwind" : "$likes"}')
pipe_4 <- mongo.bson.from.JSON('{"$group" : {"_id" : "$likes.id", "count" : {"$sum":1}}}')
pipe_5 <- mongo.bson.from.JSON('{"$sort" : {"count" : 1}}')
pipes <- list(pipe_1,pipe_2,pipe_3,pipe_4,pipe_5)
result <- mongo.aggregation(mongo, ns = "analytics.analytics_profiles", pipeline =pipes)
Which returns
mongoDB error: 10
that corresponds with the BSON invalid error code.
I think the problem is with the match by ObjectId: the first pipeline alone gives the same error.
How can I fix this?
Extra: how can this be done using mongolite instead?
You really should not "dot notation" for a an "array" key in the aggregation pipeline, but what you are doing is still perfectly valid. However you can reduce the array elements to just "id" values with $project though:
Also looks like you might need to contruct your BSON for matching the ObjectId seperately:
oid <- mongo.oid.from.string("50e99acfb35de75402002023")
pipe_1 <- mongo.bson.from.list(list('$match' = list('likes.id' = oid)))
pipe_2 <- mongo.bson.from.JSON('{"$project" : {"likes" : "$likes.id", "_id" : 0}}')
pipe_3 <- mongo.bson.from.JSON('{"$unwind" : "$likes"}')
pipe_4 <- mongo.bson.from.list(list('$match' = list('likes' = oid)))
pipe_5 <- mongo.bson.from.JSON('{"$group" : {"_id" : "$likes", "count" : {"$sum":1}}}')
pipe_6 <- mongo.bson.from.JSON('{"$sort" : {"count" : 1}}')
That now makes "likes" an array of just values and not a "key/value" pair. So you don't need "$likes.id" in later stages. Just reference by "$likes".
--
For the record, I went through this with a sample document is a collection like what you seem to have defined:
{
"_id" : ObjectId("50e99acfb35de75402002023"),
"likes" : [
{
"id" : ObjectId("50e99acfb35de75402002023")
},
{
"id" : ObjectId("50e99acfb35de75402002023")
},
{
"id" : ObjectId("50e99acfb35de75402002023")
},
{
"id" : ObjectId("50e99acfb35de75402002023")
}
]
}
Then I actually defined the pipeline in R using the bson.from.list` contructors like so:
pipeline <- list(
mongo.bson.from.list(list(
'$match' = list(
'likes.id' = mongo.oid.from.string("50e99acfb35de75402002023")
)
)),
mongo.bson.from.list(list(
'$project' = list(
'_id' = 0,
'likes' = '$likes.id'
)
)),
mongo.bson.from.list(list(
'$unwind' = '$likes'
)),
mongo.bson.from.list(list(
'$match' = list(
'likes' = mongo.oid.from.string("50e99acfb35de75402002023")
)
)),
mongo.bson.from.list(list(
'$group' = list(
'_id' = '$likes',
'count' = list( '$sum' = 1 )
)
)),
mongo.bson.from.list(list(
'$sort' = list( 'count' = 1 )
))
)
mongo.aggregation(mongo, "test.posts", pipeline)
And for me that correctly adds all matching entries within the array.
Also "note" the additional match stage here after $unwind. The first $match in aggregation matches the "document", but this does nothing to "filter" the array content, so items in the array still contain things that do not match the "id" value you asked for.
So after processing $unwind you need to "filter" with $match again once the array has been denormalized. There are actually more efficient ways of doing this and they are well documented on this site even: Retrieve only the queried element in an object array in MongoDB collection
But you should also really be using the bson.from.list and general list() contructors for the structure rather than converting from JSON.

Partial matching confusion when arguments passed through dots ('...')

I've been working on an R package that is just a REST API wrapper for a graph database. I have a function createNode that returns an object with class node and entity:
# Connect to the db.
graph = startGraph("http://localhost:7474/db/data/")
# Create two nodes in the db.
alice = createNode(graph, name = "Alice")
bob = createNode(graph, name = "Bob")
> class(alice)
[1] "node" "entity"
> class(bob)
[1] "node" "entity"
I have another function, createRel, that creates a relationship between two nodes in the database. It is specified as follows:
createRel = function(fromNode, type, toNode, ...) {
UseMethod("createRel")
}
createRel.default = function(fromNode, ...) {
stop("Invalid object. Must supply node object.")
}
createRel.node = function(fromNode, type, toNode, ...) {
params = list(...)
# Check if toNode is a node.
stopifnot("node" %in% class(toNode))
# Making REST API calls through RCurl and stuff.
}
The ... allows the user to add an arbitrary amount of properties to the relationship in the form key = value. For example,
rel = createRel(alice, "KNOWS", bob, since = 2000, through = "Work")
This creates an (Alice)-[KNOWS]->(Bob) relationship in the db, with the properties since and through and their respective values. However, if a user specifies properties with keys from or to in the ... argument, R gets confused about the classes of fromNode and toNode.
Specifying a property with key from creates confusion about the class of fromNode. It is using createRel.default:
> createRel(alice, "KNOWS", bob, from = "Work")
Error in createRel.default(alice, "KNOWS", bob, from = "Work") :
Invalid object. Must supply node object.
3 stop("Invalid object. Must supply node object.")
2 createRel.default(alice, "KNOWS", bob, from = "Work")
1 createRel(alice, "KNOWS", bob, from = "Work")
Similarly, if a user specifies a property with key to, there is confusion about the class of toNode, and stops at the stopifnot():
Error: "node" %in% class(toNode) is not TRUE
4 stop(sprintf(ngettext(length(r), "%s is not TRUE", "%s are not all TRUE"),
ch), call. = FALSE, domain = NA)
3 stopifnot("node" %in% class(toNode))
2 createRel.node(alice, "KNOWS", bob, to = "Something")
1 createRel(alice, "KNOWS", bob, to = "Something")
I've found that explicitly setting the parameters in createRel works fine:
rel = createRel(fromNode = alice,
type = "KNOWS",
toNode = bob,
from = "Work",
to = "Something")
# OK
But I am wondering how I need to edit my createRel function so that the following syntax will work without error:
rel = createRel(alice, "KNOWS", bob, from = "Work", to = "Something")
# Errors galore.
The GitHub user who opened the issue mentioned it is most likely a conflict with setAs on dispatch, which has arguments called from and to. One solution is to get rid of ... and change createRel to the following:
createRel = function(fromNode, type, toNode, params = list()) {
UseMethod("createRel")
}
createRel.default = function(fromNode, ...) {
stop("Invalid object. Must supply node object.")
}
createRel.node = function(fromNode, type, toNode, params = list()) {
# Check if toNode is a node.
stopifnot("node" %in% class(toNode))
# Making REST API calls through RCurl and stuff.
}
But, I wanted to see if I had any other options before making this change.
Not really an answer, but...
The problem is that the user-provided argument 'from' is being (partially) matched to the formal argument 'fromNode'.
f = function(fromNode, ...) fromNode
f(1, from=2)
## [1] 2
The rules are outlined in section 4.3.2 of RShowDoc('R-lang'), where named arguments are exact matched, then partial matched, and then unnamed arguments are assigned by position.
It's hard to know how to enforce exact matching, other than using single-letter argument names! Actually, for a generic this might not be as trite as it sounds -- x is a pretty generic variable name. If 'from' and 'to' were common arguments to ... you could change the argument list to "fromNode, , ..., from, to", check for missing(from) in the body of the function, and act accordingly; I don't think this would be pleasant, and the user would invariable provide an argument 'fro'.
While enforcing exact matching (and errors, via warn=2) by setting global options() might be helpful in debugging (though by then you'd probably know what you were looking for!) it doesn't help the package author who is trying to write code to work for users in general.
It might be reasonable to ask on the R-devel mailing list whether it might be time for this behavior to be changed (on the 'several releases' time scale); partial matching probably dates as a 'convenience' from the days before tab completion.

Resources