Am trying to retrieve values from a namedtuple in a list. It takes one argument, a namedtuple or a list of namedtuples, and returns the value in the price field. Here is my code:
def price(rlist):
for item in rlist:
return item.price
RC = [
Restaurant("Thai Dishes", "Thai", "334-4433", "Mee Krob", 12.50),
Restaurant("Nobu", "Japanese", "335-4433", "Natto Temaki", 5.50)]
print(price(RC))
It should print 12.50 and 5.50..but it only prints 12.50. How can I improve or correct the iteration?
Book = namedtuple('Book', 'title author year price')
favorite = Book('Adventures of Sherlock Holmes', 'Arthur Conan Doyle', 1892, 21.50)
and then when I do:
price(favorite)
It gives me an error:
for item in rlist:
TypeError: 'type' object is not iterable
Perhaps use
def price(rlist):
return [item.price for item in rlist]
to return what you seem to want
You can use list comprehension with string.join method.
>>> from collections import namedtuple
>>> Restaurant = namedtuple('Restaurant', 'name cuisine phone dish price')
>>> RC = [Restaurant("Thai Dishes", "Thai", "334-4433", "Mee Krob", 12.50), Restaurant("Nobu", "Japanese", "335-4433", "Natto Temaki", 5.50)]
>>> def price(a_tuple):
... return ', '.join([str(rc.price) for rc in a_tuple])
...
>>> price(RC)
'12.5, 5.5'
The method join return a string with elements of an iterable.
To solve the problem of only one namedtuple (and not a list of they) you can check if the argument has an attribute _fields (namedtuples have this attribute)
>>> def price(a_tuple):
... if hasattr(a_tuple, '_fields'):
... return a_tuple.price
... return ', '.join([str(rc.price) for rc in a_tuple])
...
>>> price(RC)
'12.5, 5.5'
>>> price(RC[0])
12.5
This solution working with any iterable types (list, tuple, iterator, etc)
Related
I follow a python course on finance about portfolio theory. I have to create a function with a nested function in it.
My problem is I have a error message of "neg_sharpe_ratio() missing 2 required positional arguments: 'er' and 'cov'" whereas to my mind 'er' and 'cov' are already defined in my function msr below. So I understand how they are missing.
from scipy.optimize import minimize
def msr(riskfree_rate, er, cov):
n= er.shape[0]
init_guess= np.repeat(1/n, n)
bounds=((0.00, 1.0),)*n
weights_sum_to_1 = {
'type' :'eq' , #
'fun' : lambda weights: np.sum(weights) - 1 ##
}
def neg_sharpe_ratio(weights,riskfree_rate, er, cov):
r = erk.portfolio_return(weights, er)
vol = erk.portfolio_vol(weights,cov)
return -(r-riskfree_rate)/vol
results = minimize( neg_sharpe_ratio, init_guess,
args=(cov,), method="SLSQP",
options={'disp': False},
constraints=( weights_sum_to_1),
bounds=bounds
)
return results.x
TypeError: neg_sharpe_ratio() missing 2 required positional arguments: 'er' and 'cov'
The function neg_sharpe_ratio is able to reference any of the variables passed in and made by the function msr without needing those same variables passed into it itself. Therefore you should be able to remove the paramters riskfree_rate, er, and cov from the neq_sharpe_ratio function definition and have it work, as those variables are passed into its parent function, leaving you with:
def neg_sharpe_ratio(weights):
For those who might be interested, I find my mistake..
Indeed, I forgot to define correctly the arguments of my function neg_share_ratio in my function minimize.
Here is the code amended:
from scipy.optimize import minimize
def msr(riskfree_rate, er, cov):
n= er.shape[0]
init_guess= np.repeat(1/n, n)
bounds=((0.00, 1.0),)*n
weights_sum_to_1 = {
'type' :'eq' , #
'fun' : lambda weights: np.sum(weights) - 1 ##
}
def neg_sharpe_ratio(weights,riskfree_rate, er, cov):
r = erk.portfolio_return(weights, er)
vol = erk.portfolio_vol(weights,cov)
return -(r-riskfree_rate)/vol
results = minimize( neg_sharpe_ratio, init_guess,
args=(weights,riskfree_rate,er,cov), method="SLSQP",
options={'disp': False},
constraints=( weights_sum_to_1),
bounds=bounds
)
return results.x code here
This is my data:
>>> print(MyModel.objects.get(id=1).Fruits) #Fruits is JSONField
>>> print(favorites)
{"Title": ["Fruits"], "Name": ["Banana", "Cherry", "Apple", "Peach"], "Other":["Banana"]}
I define a query as follows:
>>> query = reduce(operator.or_, (Q(Fruits__Name__icontains=x) for x in favorites))
#or: query = reduce(operator.or_, (Q(Fruits__icontains={'Name':x}) for x in favorites))
>>> print(query)
(OR: ('Fruits__Name__icontains', 'Apple'), ('Fruits__Name__icontains', 'Banana'))
I want it return that item banana or an apple are in Name.
When I run this query (postgresql):
MyModel.objects.filter(query)
it doesn't match any item in the database.
With MyModel.Fruits as your JSONField, depending on the database used, it might be stored/accessed as string. You could try just accessing icontains directly on the JSON string value:
Fruits__icontains=x
So full query would be:
query = reduce(operator.or_, (Q(Fruits__icontains=x) for x in favorites))
I have a data structure that I want to convert to json and preserve the key order.
For example:
%{ x: 1, a: 5} should be converted to "{\"x\": 1, \"a\": 5}"
Poison does it without any problem. But when I upgrade to Jason, it changes to "{\"a\": 5, \"x\": 1}".
So I use JasonHelpers json_map to preserve the order like this:
Jason.Helpers.json_map([x: 1, a: 5])
It creates a fragment with correct order.
However, when I use a variable to do this:
list = [x: 1, a: 5]
Jason.Helpers.json_map(list)
I have an error:
** (Protocol.UndefinedError) protocol Enumerable not implemented for {:list, [line: 15], nil} of type Tuple.
....
QUESTION: How can I pass a pre-calculated list into Jason.Helpers.json_map ?
The calculation is complicated, so I don't want to repeat the code just to use json_map, but use the function that returns a list.
json_map/1 is a macro, from its docs:
Encodes a JSON map from a compile-time keyword.
It is designed for compiling JSON at compile-time, which is why it doesn't work with your runtime variable.
Support for encoding keyword lists was added to the Jason library a year ago, but it looks like it hasn't been pushed to hex yet. I managed to get it work by pulling the latest code from github:
defp deps do
[{:jason, git: "https://github.com/michalmuskala/jason.git"}]
end
Then by creating a struct that implements Jason.Encoder (adapted from this solution by the Jason author):
defmodule OrderedObject do
defstruct [:value]
def new(value), do: %__MODULE__{value: value}
defimpl Jason.Encoder do
def encode(%{value: value}, opts) do
Jason.Encode.keyword(value, opts)
end
end
end
Now we can encode objects with ordered keys:
iex(1)> Jason.encode!(OrderedObject.new([x: 1, a: 5]))
"{\"x\":1,\"a\":5}"
I don't know if this is part of the public API or just an implementation detail, but it appears you have some control of the order when implementing the Jason.Encoder protocol for a struct.
Let's say you've defined an Ordered struct:
defmodule Ordered do
#derive {Jason.Encoder, only: [:a, :x]}
defstruct [:a, :x]
end
If you encode the struct, the "a" key will be before the "x" key:
iex> Jason.encode!(%Ordered{a: 5, x: 1})
"{\"a\":5,\"x\":1}"
Let's reorder the keys we pass in to the :only option:
defmodule Ordered do
#derive {Jason.Encoder, only: [:x, :a]}
defstruct [:a, :x]
end
If we now encode the struct, the "x" key will be before the "a" key:
iex> Jason.encode!(%Ordered{a: 5, x: 1})
"{\"x\":1,\"a\":5}"
I have the following closure:
def get!(Item, id) do
Enum.find(
#items,
fn(item) -> item.id == id end
)
end
As I believe this looks ugly and difficult to read, I'd like to give this a name, like:
def get!(Item, id) do
defp has_target_id?(item), do: item.id = id
Enum.find(#items, has_target_id?/1)
end
Unfortunately, this results in:
== Compilation error in file lib/auction/fake_repo.ex ==
** (ArgumentError) cannot invoke defp/2 inside function/macro
(elixir) lib/kernel.ex:5238: Kernel.assert_no_function_scope/3
(elixir) lib/kernel.ex:4155: Kernel.define/4
(elixir) expanding macro: Kernel.defp/2
lib/auction/fake_repo.ex:28: Auction.FakeRepo.get!/2
Assuming it is possible, what is the correct way to do this?
The code you posted has an enormous amount of syntax errors/glitches. I would suggest you start with getting accustomed to the syntax, rather than trying to make Elixir better by inventing the things that nobody uses.
Here is the correct version that does what you wanted. The task might be accomplished with an anonymous function, although I hardly see a reason to make a perfectly looking idiomatic Elixir look ugly.
defmodule Foo do
#items [%{id: 1}, %{id: 2}, %{id: 3}]
def get!(id) do
has_target_id? = fn item -> item.id == id end
Enum.find(#items, has_target_id?)
end
end
Foo.get! 1
#⇒ %{id: 1}
Foo.get! 4
#⇒ nil
You can do this:
def get!(Item, id) do
Enum.find(
#items,
&compare_ids(&1, id)
)
end
defp compare_ids(%Item{}=item, id) do
item.id == id
end
But, that's equivalent to:
Enum.find(
#items,
fn item -> compare_ids(item, id) end
)
and may not pass your looks ugly and difficult to read test.
I was somehow under the impression Elixir supports nested functions?
Easy enough to test:
defmodule A do
def go do
def greet do
IO.puts "hello"
end
greet()
end
end
Same error:
$ iex a.ex
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
** (ArgumentError) cannot invoke def/2 inside function/macro
(elixir) lib/kernel.ex:5150: Kernel.assert_no_function_scope/3
(elixir) lib/kernel.ex:3906: Kernel.define/4
(elixir) expanding macro: Kernel.def/2
a.ex:3: A.go/0
wouldn't:
defp compare_ids(item, id), do: item.id == id
be enough? Is there any advantage to including %Item{} or making
separate functions for returning both true and false conditions?
What you gain by specifying the first parameter as:
func(%Item{} = item, target_id)
is that only an Item struct will match the first parameter. Here is an example:
defmodule Item do
defstruct [:id, :name, :description]
end
defmodule Dog do
defstruct [:id, :name, :owner]
end
defmodule A do
def go(%Item{} = item), do: IO.inspect(item.id, label: "id: ")
end
In iex:
iex(1)> item = %Item{id: 1, name: "book", description: "old"}
%Item{description: "old", id: 1, name: "book"}
iex(2)> dog = %Dog{id: 1, name: "fido", owner: "joe"}
%Dog{id: 1, name: "fido", owner: "joe"}
iex(3)> A.go item
id: : 1
1
iex(4)> A.go dog
** (FunctionClauseError) no function clause matching in A.go/1
The following arguments were given to A.go/1:
# 1
%Dog{id: 1, name: "fido", owner: "joe"}
a.ex:10: A.go/1
iex(4)>
You get a function clause error if you call the function with a non-Item, and the earlier an error occurs, the better, because it makes debugging easier.
Of course, by preventing the function from accepting other structs, you make the function less general--but because it's a private function, you can't call it from outside the module anyway. On the other hand, if you wanted to call the function on both Dog and Item structs, then you could simply specify the first parameter as:
|
V
func(%{}=thing, target_id)
then both an Item and a Dog would match--but not non-maps.
What you gain by specifying the first parameter as:
|
V
func(%Item{id: id}, target_id)
is that you let erlang's pattern matching engine extract the data you need, rather than calling item.id as you would need to do with this definition:
func(%Item{}=item, target_id)
In erlang, pattern matching in a parameter list is the most efficient/convenient/stylish way to write functions. You use pattern matching to extract the data that you want to use in the function body.
Going even further, if you write the function definition like this:
same variable name
| |
V V
func(%Item{id: target_id}, target_id)
then erlang's pattern matching engine not only extracts the value for the id field from the Item struct, but also checks that the value is equal to the value of the target_id variable in the 2nd argument.
Defining multiple function clauses is a common idiom in erlang, and it is considered good style because it takes advantage of pattern matching rather than logic inside the function body. Here's an erlang example:
get_evens(List) ->
get_evens(List, []).
get_evens([Head|Tail], Results) when Head rem 2 == 0 ->
get_evens(Tail, [Head|Results]);
get_evens([Head|Tail], Results) when Head rem 2 =/= 0 ->
get_evens(Tail, Results);
get_evens([], Results) ->
lists:reverse(Results).
I have a number of csv files. Two example files are shown below.
input1.csv
Actinocyclus actinochilus,7
Asterionella formosa,4
Aulacodiscus orientalis,1
Aulacoseira granulata,3
Chaetoceros radicans,1
Corethron hystrix,6
Coscinodiscaceae 1
Dactyliosolen fragilissimus,32
Diadesmis gallica,1
Diatoma hyemalis 1
Synedropsis hyperboreoides,4
Trigonium formosum,4
Urosolenia eriensis,2
input2.csv
Actinocyclus actinochilus,55
Asterionella formosa,3
Aulacoseira granulata,5
Chaetoceros radicans,7
Dactyliosolen fragilissimus,5
Diatoma hyemalis,1
Stephanopyxis turris,1
Striatella unipunctata,1
Synedropsis hyperboreoides,28
Trigonium formosum,3
Urosolenia eriensis,2
I want to merge these csv files by adding column two based on the same name in column one as in the example output below.
output.csv
Actinocyclus actinochilus,62
Asterionella formosa,7
Aulacodiscus orientalis,1
Aulacoseira granulata,8
Chaetoceros radicans,8
Corethron hystrix,6
Coscinodiscaceae, 1
Dactyliosolen fragilissimus,37
Diadesmis gallica,1
Diatoma hyemalis,2
Stephanopyxis turris,1
Striatella unipunctata,1
Synedropsis hyperboreoides,32
Trigonium formosum,7
Urosolenia eriensis,4
I tried join and cat but these stacked them together. Any idea how could add them together?
Solution for multiple files
This is a Python 3 solution. If you need it to work with Python 2, change this line names = inp.keys() | data.keys() into names = inp.viewkeys() | data.viewkeys().
# get this list of file names form somewhere like `glob`
file_names = ['input1.csv', 'input2.csv', 'input3.csv', 'input4.csv']
def file_to_dict(file_name):
"""Read a two-column csv file into a dict with first column as key
and an integer value from the second column.
"""
with open(file_name) as fobj:
pairs = (line.split(',') for line in fobj if line.strip())
return {k.strip(): int(v) for k, v in pairs}
def merge(data, file_name):
"""Merge input file with dict `data` adding the numerical values.
"""
inp = file_to_dict(file_name)
names = inp.keys() | data.keys()
for name in names:
data[name] = data.get(name, 0) + inp.get(name, 0)
return data
data = {}
for file_name in file_names:
merge(data, file_name)
with open('output.csv', 'w') as fobj:
for name, val in sorted(data.items()):
fobj.write('{},{}\n'.format(name, val))
Solution for two files
This produces the desired output:
def file_to_dict(file_name):
"""Read a two-column csv file into a dict with first column as key
and an integer value from the second column.
"""
with open(file_name) as fobj:
pairs = (line.split(',') for line in fobj if line.strip())
return {k.strip(): int(v) for k, v in pairs}
inp1 = file_to_dict('input1.csv')
inp2 = file_to_dict('input2.csv')
names = sorted(inp1.keys() | inp2.keys())
with open('output.csv', 'w') as fobj:
for name in names:
val = inp1.get(name, 0) + inp2.get(name, 0)
fobj.write('{},{}\n'.format(name, val))
Explanation
The function file_to_dict reads one input file and returns a dictionary like this:
{'Actinocyclus actinochilus': 7,
'Asterionella formosa': 4,
...
Next:
pairs = (line.split(',') for line in fobj if line.strip())
pairs holds a generator expression that represents all name-value pairs as strings. Then:
{k.strip(): int(v) for k, v in pairs}
creates a dictionary from this pairs, stripping of extra whits space from the name and converting the string in the second column into an integer.
After reading both input files with this function:
names = sorted(inp1.keys() | inp2.keys())
uses the union of the names from both inputs, i.e. all names that appear in input1 and input2 and sorts them alphabetically.
The output file needs to be open in write mode:
with open('output.csv', 'w') as fobj:
for each name:
for name in names:
we retrieve the value from the input dictionaries:
val = inp1.get(name, 0) + inp2.get(name, 0)
The method get returns the value if the name is in the dictionary. Otherwise, it returns the 0 given as second argument.
Finally, we write this result line by line:
fobj.write('{},{}\n'.format(name, val))