Nested Json in Pandas Column - python

I have a dataframe with nested json as column.
df.depth
0 {'buy': [{'quantity': 51, 'price': 2275.85, 'o...
1 {'buy': [{'quantity': 1, 'price': 2275.85, 'or...
2 {'buy': [{'quantity': 1, 'price': 2275.85, 'or..
inside each row have 5 depths of buy sell
df.depth[0]
{'buy': [{...}, {...}, {...}, {...}, {...}], 'sell': [{...}, {...}, {...}, {...}, {...}]}
real json structure is as below
{'buy': [{'quantity': 51, 'price': 2275.85, 'orders': 2}, {'quantity': 38, 'price': 2275.8, 'orders': 2}, {'quantity': 108, 'price': 2275.75, 'orders': 3}, {'quantity': 120, 'price': 2275.7, 'orders': 2}, {'quantity': 6, 'price': 2275.6, 'orders': 1}], 'sell': [{'quantity': 353, 'price': 2276.95, 'orders': 1}, {'quantity': 29, 'price': 2277.0, 'orders': 2}, {'quantity': 54, 'price': 2277.1, 'orders': 2}, {'quantity': 200, 'price': 2277.2, 'orders': 1}, {'quantity': 4, 'price': 2277.25, 'orders': 1}]}
i want to explode this in to something like this
Required Output:
depth.buy.quantity1 df.depth.buy.price1 ... depth.sell.quantity1 depth.sell.price1....
0 51 2275.85.... 353 2276
1 1 2275.85.... 352 2276
how to do it ?
Edit:
for help i have added demo dataframe:
a={'buy': [{'quantity': 51, 'price': 2275.85, 'orders': 2}, {'quantity': 38, 'price': 2275.8, 'orders': 2}, {'quantity': 108, 'price': 2275.75, 'orders': 3}, {'quantity': 120, 'price': 2275.7, 'orders': 2}, {'quantity': 6, 'price': 2275.6, 'orders': 1}], 'sell': [{'quantity': 353, 'price': 2276.95, 'orders': 1}, {'quantity': 29, 'price': 2277.0, 'orders': 2}, {'quantity': 54, 'price': 2277.1, 'orders': 2}, {'quantity': 200, 'price': 2277.2, 'orders': 1}, {'quantity': 4, 'price': 2277.25, 'orders': 1}]}
c=dict()
c['depth'] = a
df = pd.DataFrame([c,c])

You could try concat:
df = pd.concat([pd.concat([pd.DataFrame(x, index=[0]) for x in i], axis=1) for i in pd.json_normalize(df['depth'])['buy'].tolist()], ignore_index=True)
print(df)
Output:
quantity price orders quantity price orders ... quantity price orders quantity price orders
0 51 2275.85 2 38 2275.8 2 ... 120 2275.7 2 6 2275.6 1
1 51 2275.85 2 38 2275.8 2 ... 120 2275.7 2 6 2275.6 1
[2 rows x 15 columns]

Related

Not able to parse JSON data from text file using python script

I have a '.txt' file that contains JSON data like below.
[{'tradable': True, 'mode': 'full', 'instrument_token': 4708097, 'last_price': 178.65, 'last_traded_quantity': 5, 'average_traded_price': 180.1, 'volume_traded': 4581928, 'total_buy_quantity': 1282853, 'total_sell_quantity': 1673842, 'ohlc': {'open': 181.95, 'high': 181.95, 'low': 177.8, 'close': 181.0}, 'change': -1.2983425414364609, 'last_trade_time': datetime.datetime(2023, 1, 12, 13, 4, 58), 'oi': 0, 'oi_day_high': 0, 'oi_day_low': 0, 'exchange_timestamp': datetime.datetime(2023, 1, 12, 13, 5, 1), 'depth': {'buy': [{'quantity': 653, 'price': 178.6, 'orders': 8}, {'quantity': 2408, 'price': 178.55, 'orders': 15}, {'quantity': 6329, 'price': 178.5, 'orders': 22}, {'quantity': 9161, 'price': 178.45, 'orders': 24}, {'quantity': 7775, 'price': 178.4, 'orders': 17}], 'sell': [{'quantity': 5726, 'price': 178.7, 'orders': 8}, {'quantity': 4099, 'price': 178.75, 'orders': 11}, {'quantity': 23951, 'price': 178.8, 'orders': 25}, {'quantity': 7446, 'price': 178.85, 'orders': 21}, {'quantity': 11379, 'price': 178.9, 'orders': 21}]}}, {'tradable': True, 'mode': 'full', 'instrument_token': 871681, 'last_price': 972.55, 'last_traded_quantity': 1, 'average_traded_price': 973.85, 'volume_traded': 411290, 'total_buy_quantity': 152925, 'total_sell_quantity': 214765, 'ohlc': {'open': 971.75, 'high': 978.6, 'low': 969.0, 'close': 967.75}, 'change': 0.4959958667011061, 'last_trade_time': datetime.datetime(2023, 1, 12, 13, 4, 53), 'oi': 0, 'oi_day_high': 0, 'oi_day_low': 0, 'exchange_timestamp': datetime.datetime(2023, 1, 12, 13, 5, 4), 'depth': {'buy': [{'quantity': 6, 'price': 972.15, 'orders': 2}, {'quantity': 3, 'price': 972.1, 'orders': 2}, {'quantity': 15, 'price': 972.05, 'orders': 3}, {'quantity': 455, 'price': 972.0, 'orders': 16}, {'quantity': 14, 'price': 971.95, 'orders': 2}], 'sell': [{'quantity': 6, 'price': 972.5, 'orders': 3}, {'quantity': 49, 'price': 972.55, 'orders': 2}, {'quantity': 10, 'price': 972.6, 'orders': 1}, {'quantity': 27, 'price': 972.65, 'orders': 2}, {'quantity': 10, 'price': 972.7, 'orders': 1}]}}]
This data was written to a .txt file after it was recieved from zerodha websocket. Now, I want to read the data from the .txt file using my python script and want to load it as a json. But the json.loads() method in python throws the below error.
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
I have tried eval and ast.literal_eval methods in python as well but it didn't solve my problem. All I want is to be able to read the above data as a JSON to my python script. Any leads would be of great help.
Let me start off with THIS IS A BAD IDEA.
The comments on the question call out that this is not a JSON object in a file, rather the result of some other Python process printing the result and putting that in a file. The correct solution would be to modify the producer to use json.dumps() instead.
That aside, here's a DANGEROUS way to read that source file into a Python object.
import datetime # needed for `eval()`
with open('textfile.txt', 'r') as f:
data = eval(f.read())
from pprint import pprint
pprint(data)
This will produce the following output from that input:
[{'average_traded_price': 180.1,
'change': -1.2983425414364609,
'depth': {'buy': [{'orders': 8, 'price': 178.6, 'quantity': 653},
{'orders': 15, 'price': 178.55, 'quantity': 2408},
{'orders': 22, 'price': 178.5, 'quantity': 6329},
{'orders': 24, 'price': 178.45, 'quantity': 9161},
{'orders': 17, 'price': 178.4, 'quantity': 7775}],
'sell': [{'orders': 8, 'price': 178.7, 'quantity': 5726},
{'orders': 11, 'price': 178.75, 'quantity': 4099},
{'orders': 25, 'price': 178.8, 'quantity': 23951},
{'orders': 21, 'price': 178.85, 'quantity': 7446},
{'orders': 21, 'price': 178.9, 'quantity': 11379}]},
'exchange_timestamp': datetime.datetime(2023, 1, 12, 13, 5, 1),
'instrument_token': 4708097,
'last_price': 178.65,
'last_trade_time': datetime.datetime(2023, 1, 12, 13, 4, 58),
'last_traded_quantity': 5,
'mode': 'full',
'ohlc': {'close': 181.0, 'high': 181.95, 'low': 177.8, 'open': 181.95},
'oi': 0,
'oi_day_high': 0,
'oi_day_low': 0,
'total_buy_quantity': 1282853,
'total_sell_quantity': 1673842,
'tradable': True,
'volume_traded': 4581928},
{'average_traded_price': 973.85,
'change': 0.4959958667011061,
'depth': {'buy': [{'orders': 2, 'price': 972.15, 'quantity': 6},
{'orders': 2, 'price': 972.1, 'quantity': 3},
{'orders': 3, 'price': 972.05, 'quantity': 15},
{'orders': 16, 'price': 972.0, 'quantity': 455},
{'orders': 2, 'price': 971.95, 'quantity': 14}],
'sell': [{'orders': 3, 'price': 972.5, 'quantity': 6},
{'orders': 2, 'price': 972.55, 'quantity': 49},
{'orders': 1, 'price': 972.6, 'quantity': 10},
{'orders': 2, 'price': 972.65, 'quantity': 27},
{'orders': 1, 'price': 972.7, 'quantity': 10}]},
'exchange_timestamp': datetime.datetime(2023, 1, 12, 13, 5, 4),
'instrument_token': 871681,
'last_price': 972.55,
'last_trade_time': datetime.datetime(2023, 1, 12, 13, 4, 53),
'last_traded_quantity': 1,
'mode': 'full',
'ohlc': {'close': 967.75, 'high': 978.6, 'low': 969.0, 'open': 971.75},
'oi': 0,
'oi_day_high': 0,
'oi_day_low': 0,
'total_buy_quantity': 152925,
'total_sell_quantity': 214765,
'tradable': True,
'volume_traded': 411290}]
Again, I will restate, THIS IS A BAD IDEA.
Read more on the specifics of eval here: https://realpython.com/python-eval-function/
Your JSON is not valid. You can check that by using an online JSON validator like this one: https://jsonformatter.org/
After entering the “JSON”, you can see it is not in a valid format.
You could either export it right, which I would highly recommend, or you could replace the wrong chars.
Currently, you have three issues:
You are using single quotes instead of double quotes
You are not parsing your datetime object, it looks like you just insert the object, you have to serialize it
You are writing true values as True, but that is the python way and not the JSON way. You either have to write it as true, or you have to pass it as a string. I would recommend the first one.
It could look like this (but I didnt parse datetime right, I just stringified it):
[{"tradable": true, "mode": "full", "instrument_token": 4708097, "last_price": 178.65, "last_traded_quantity": 5, "average_traded_price": 180.1, "volume_traded": 4581928, "total_buy_quantity": 1282853, "total_sell_quantity": 1673842, "ohlc": {"open": 181.95, "high": 181.95, "low": 177.8, "close": 181.0}, "change": -1.2983425414364609, "last_trade_time": "datetime.datetime(2023, 1, 12, 13, 4, 58)", "oi": 0, "oi_day_high": 0, "oi_day_low": 0, "exchange_timestamp": "datetime.datetime(2023, 1, 12, 13, 5, 1)", "depth": {"buy": [{"quantity": 653, "price": 178.6, "orders": 8}, {"quantity": 2408, "price": 178.55, "orders": 15}, {"quantity": 6329, "price": 178.5, "orders": 22}, {"quantity": 9161, "price": 178.45, "orders": 24}, {"quantity": 7775, "price": 178.4, "orders": 17}], "sell": [{"quantity": 5726, "price": 178.7, "orders": 8}, {"quantity": 4099, "price": 178.75, "orders": 11}, {"quantity": 23951, "price": 178.8, "orders": 25}, {"quantity": 7446, "price": 178.85, "orders": 21}, {"quantity": 11379, "price": 178.9, "orders": 21}]}}, {"tradable": true, "mode": "full", "instrument_token": 871681, "last_price": 972.55, "last_traded_quantity": 1, "average_traded_price": 973.85, "volume_traded": 411290, "total_buy_quantity": 152925, "total_sell_quantity": 214765, "ohlc": {"open": 971.75, "high": 978.6, "low": 969.0, "close": 967.75}, "change": 0.4959958667011061, "last_trade_time": "datetime.datetime(2023, 1, 12, 13, 4, 53)", "oi": 0, "oi_day_high": 0, "oi_day_low": 0, "exchange_timestamp": "datetime.datetime(2023, 1, 12, 13, 5, 4)", "depth": {"buy": [{"quantity": 6, "price": 972.15, "orders": 2}, {"quantity": 3, "price": 972.1, "orders": 2}, {"quantity": 15, "price": 972.05, "orders": 3}, {"quantity": 455, "price": 972.0, "orders": 16}, {"quantity": 14, "price": 971.95, "orders": 2}], "sell": [{"quantity": 6, "price": 972.5, "orders": 3}, {"quantity": 49, "price": 972.55, "orders": 2}, {"quantity": 10, "price": 972.6, "orders": 1}, {"quantity": 27, "price": 972.65, "orders": 2}, {"quantity": 10, "price": 972.7, "orders": 1}]}}]

Extract values from Dictionary inside a pandas dataframe (Python)

trying to extract the dictionary in a dataframe. but unable to. none of the solution mentioned matches my requirement hence seeking help for the same.
instrument_token last_price change depth
0 17600770 180.75 20.500000 {'buy': [{'quantity': 1, 'price': 1, 'orders': 1},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 1, 'price': 1, 'orders': 1},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
1 12615426 0.05 -50.000000 {'buy': [{'quantity': 2, 'price': 2, 'orders': 2},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 2, 'price': 2, 'orders': 2},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
2 17543682 0.35 -89.062500 {'buy': [{'quantity': 3, 'price': 3, 'orders': 3},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 3, 'price': 3, 'orders': 3},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
3 17565954 6.75 -10.000000 {'buy': [{'quantity': 4, 'price': 4, 'orders': 4},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 4, 'price': 4, 'orders': 4},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
4 26077954 3.95 -14.130435 {'buy': [{'quantity': 5, 'price': 5, 'orders': 5},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 5, 'price': 5, 'orders': 5},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
5 17599490 141.75 -2.241379 {'buy': [{'quantity': 6, 'price': 6, 'orders': 6},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 6, 'price': 6, 'orders': 6},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
6 17566978 17.65 -1.671309 {'buy': [{'quantity': 7, 'price': 7, 'orders': 7},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 7, 'price': 7, 'orders': 7},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
7 26075906 24.70 -16.554054 {'buy': [{'quantity': 8, 'price': 8, 'orders': 8},{'quantity': 0, 'price': 0.0, 'orders': 0}], 'sell': [{'quantity': 8, 'price': 8, 'orders': 8},{'quantity': 0, 'price': 0.0, 'orders': 0}]}
looking to convert to the following:
instrument_token last_price change buy_price sell_price
0 17600770 180.75 20.500000 1 1
1 12615426 0.05 -50.000000 2 2
2 17543682 0.35 -89.062500 3 3
3 17565954 6.75 -10.000000 4 4
4 26077954 3.95 -14.130435 5 5
5 17599490 141.75 -2.241379 6 6
6 17566978 17.65 -1.671309 7 7
...
able to access the individual elements using a for loop by unable to convert the dictionary to the desired df.col as shown in the above desired df.
You want to get price only from the first element of the list, and not a sum, then do:
df["buy_price"]=df["depth"].str["buy"].str[0].str["price"]
df["sell_price"]=df["depth"].str["sell"].str[0].str["price"]
In case you wish to get a sum of all nested elements:
df["buy_price"]=df["depth"].str["buy"].apply(lambda x: sum(el["price"] for el in x))
df["sell_price"]=df["depth"].str["sell"].apply(lambda x: sum(el["price"] for el in x))
Is this what you're looking for?
def get_prices(depth, tag):
def sum(items):
total = 0
for item in items:
total += item['price']
return total
return int(sum(depth[tag]))
df['buy_price'] = df['depth'].apply(lambda depth: get_prices(depth, 'buy'))
df['sell_price'] = df['depth'].apply(lambda depth: get_prices(depth, 'sell'))
df.drop(columns='depth', inplace=True)
print(df)
Output:
instrument_token last_price change buy_price sell_price
0 17600770 180.75 20.500000 1 1
1 12615426 0.05 -50.000000 2 2
2 17543682 0.35 -89.062500 3 3
3 17565954 6.75 -10.000000 4 4
4 26077954 3.95 -14.130435 5 5
5 17599490 141.75 -2.241379 6 6
6 17566978 17.65 -1.671309 7 7
7 26075906 24.70 -16.554054 8 8
I use ast here to get it into Python data structure from string. For actual dictionaries, as is your case, you can remove the ast.literal_eval part out of the script.
Get the dictionary and merge back to original dataframe. Assumption, based on your output is that you are only interested in the first dict in each sublist for buy and sell respectively.
import ast
res = [{f"{x}_price" : ast.literal_eval(ent)[x][0]['price']
for x in ("buy","sell")}
for ent in df.pop('depth') ]
df.join(pd.DataFrame(res))
instrument_token last_price change buy_price sell_price
0 17600770 180.75 20.500000 1 1
1 12615426 0.05 -50.000000 2 2
2 17543682 0.35 -89.062500 3 3
3 17565954 6.75 -10.000000 4 4
4 26077954 3.95 -14.130435 5 5
5 17599490 141.75 -2.241379 6 6
6 17566978 17.65 -1.671309 7 7
7 26075906 24.70 -16.554054 8 8
For actual dictionaries:
res = [{f"{x}_price" : ent[x][0]['price']
for x in ("buy","sell")}
for ent in df.pop('depth') ]
#merge back to df
result = df.join(pd.DataFrame(res))

Iterate over two lists of dicts and create list of tuples without loop

I have two lists of dicts: list1 and list2.
print(list1)
[{'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
{'name': 'bard', 'desc': 'besd', 'city': 2, 'ID': 1},
{'name': 'baer', 'desc': 'bees', 'city': 2, 'ID': 1},
{'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
{'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2}]
print(list2)
[{'name': 'foo', 'desc': 'baz', 'city': 1, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 1, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1},
{'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2},
{'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2}]
I need a list of tuples that will hold two paired dicts (one dict from each list) with the same city and ID.
I did it with double loop:
list_of_tuples = []
for i in list1:
for j in list2:
if i['ID'] == j['ID'] and i['city'] == j['city']:
list_of_tuples.append((i, j))
print(list_of_tuples)
[({'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
{'name': 'foo', 'desc': 'baz', 'city': 1, 'ID': 1}),
({'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 1, 'ID': 1}),
({'name': 'bard', 'desc': 'besd', 'city': 2, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1}),
({'name': 'baer', 'desc': 'bees', 'city': 2, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1}),
({'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
{'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2}),
({'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
{'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2}),
({'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2},
{'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2}),
({'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2},
{'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2})]
Question: How to do this in a more pythonic way (without loops)?
You can use itertools.product and filter:
from itertools import product
list1 = [{'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
{'name': 'bard', 'desc': 'besd', 'city': 2, 'ID': 1},
{'name': 'baer', 'desc': 'bees', 'city': 2, 'ID': 1},
{'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
{'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2}]
list2 = [{'name': 'foo', 'desc': 'baz', 'city': 1, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 1, 'ID': 1},
{'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1},
{'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2},
{'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2}]
def condition(x):
return x[0]['ID'] == x[1]['ID'] and x[0]['city'] == x[1]['city']
list_of_tuples = list(filter(condition, product(list1, list2)))
This is a problem well suited for pandas. If you convert the lists to DataFrames, matching the records on ID and city is the same as an inner join of the two DataFrames.
import pandas as pd
# convert lists to DataFrames
df1 = pd.DataFrame(list1)
df2 = pd.DataFrame(list2)
# merge the two DataFrames
print(df1.merge(df2, on=["ID", "city"]))
# ID city desc_x name_x desc_y name_y
#0 1 1 bazv fooa baz foo
#1 1 1 bazv fooa bes bar
#2 1 2 besd bard bes bar
#3 1 2 bees baer bes bar
#4 2 1 bnbb aaaa bbb aaa
#5 2 1 bnbb aaaa ddd ccc
#6 2 1 dgdd cgcc bbb aaa
#7 2 1 dgdd cgcc ddd ccc
Now you have the matched records in each row. Since the desc and name columns were present in both (and not used for the merge), they get subscripted with _x and _y to differentiate between the two souce DataFrames.
You just need to reformat it to be in your desired output. You can achieve this using to_dict and a list comprehension:
list_of_tuples = [
(
{"name": r["name_x"], "desc": r["desc_x"], "city": r["city"], "ID": r["ID"]},
{"name": r["name_y"], "desc": r["desc_y"], "city": r["city"], "ID": r["ID"]}
) for r in df1.merge(df2, on=["ID", "city"]).to_dict(orient="records")
]
print(list_of_tuples)
#[({'ID': 1, 'city': 1, 'desc': 'bazv', 'name': 'fooa'},
# {'ID': 1, 'city': 1, 'desc': 'baz', 'name': 'foo'}),
# ({'ID': 1, 'city': 1, 'desc': 'bazv', 'name': 'fooa'},
# {'ID': 1, 'city': 1, 'desc': 'bes', 'name': 'bar'}),
# ({'ID': 1, 'city': 2, 'desc': 'besd', 'name': 'bard'},
# {'ID': 1, 'city': 2, 'desc': 'bes', 'name': 'bar'}),
# ({'ID': 1, 'city': 2, 'desc': 'bees', 'name': 'baer'},
# {'ID': 1, 'city': 2, 'desc': 'bes', 'name': 'bar'}),
# ({'ID': 2, 'city': 1, 'desc': 'bnbb', 'name': 'aaaa'},
# {'ID': 2, 'city': 1, 'desc': 'bbb', 'name': 'aaa'}),
# ({'ID': 2, 'city': 1, 'desc': 'bnbb', 'name': 'aaaa'},
# {'ID': 2, 'city': 1, 'desc': 'ddd', 'name': 'ccc'}),
# ({'ID': 2, 'city': 1, 'desc': 'dgdd', 'name': 'cgcc'},
# {'ID': 2, 'city': 1, 'desc': 'bbb', 'name': 'aaa'}),
# ({'ID': 2, 'city': 1, 'desc': 'dgdd', 'name': 'cgcc'},
# {'ID': 2, 'city': 1, 'desc': 'ddd', 'name': 'ccc'})]
Having nested loops is not "not pythonic". However, you can achieve the same result with a list comprehension. I don't think it's more readable though:
[(i, j) for j in list2 for i in list1 if i['ID'] == j['ID'] and i['city'] == j['city']]

Find keys occurence in list of objects in python

I'm trying to transform this list:
list = [
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
]
into the sum of occurence on the key 'product_name' like this:
list_final = [
{'product_name': '4x6', 'quantity': 4, 'price': 1.16},
{'product_name': '4x4', 'quantity': 4, 'price': 1.16},
]
I can't figure how to search the occurence of the key 'product_name' without doing loops in loops
what I did :
for item in list:
if item.product_name in data.keys():
data[item.product_name]['qty'] += 1
data[item.product_name]['price'] *= 2
else:
data.update({item.product_name: [{'qty': item['quantity'], 'price': item['price']}]})
but I cant find a solution to get my list as I want
how can I do this right ?
Here's a solution with OrderedDict that handles multiple products.
from collections import OrderedDict
o = OrderedDict()
for x in data:
p = x['product_name']
if p not in o:
o[p] = x
else:
o[p].update({k : o[p][k] + x[k] for k in x.keys() - {'product_name'}})
list_final = list(o.values())
A product is added to the inventory if it doesn't exist, or else is summed with the existing inventory. This should work on python3.x and above.
print(list_final)
[{'price': 1.16, 'product_name': '4x6', 'quantity': 4}]
For python2.x, change this
o[p].update({k : o[p][k] + x[k] for k in x.keys() - {'product_name'}})
To
o[p].update({k : o[p][k] + x[k] for k in set(x.keys()) - {'product_name'}})
Probably not the most readable, but here's a for loop-free implementation:
def transform(array):
def inner(cumulator, row):
product_name = row['product_name']
bucket = cumulator.get(product_name, {'quantity': 0, 'price': 0})
cumulator[product_name] = {
'quantity': bucket['quantity'] + row['quantity'],
'price': bucket['price'] + row['price'],
}
return cumulator
return reduce(inner, array, {})
And then you just
transform(list)
// {'4x6': {'price': 1.16, 'quantity': 4}}
this might help:
l = [
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x6', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
{'product_name': '4x4', 'quantity': 1, 'price': 0.29},
]
from collections import defaultdict
count = defaultdict(lambda: {'quantity':0, 'price':0.0})
for d in l:
count[d['product_name']]['quantity'] += 1
count[d['product_name']]['price'] = d['price']
for prod_name, prod_info in count.items():
print("product_name:", prod_name, "quantity: {quantity} price: {price}".format(**prod_info))
Output for your input:
product_name: 4x6 quantity: 4 price: 0.29
product_name: 4x4 quantity: 4 price: 0.29
Note: This also works with python2

Couldn't rename field in python list (key, value structure)

I have below list with nested lists (sort of key,values)
inp1=[{'id': 0, 'name': 98, 'value': 9}, {'id': 1, 'name': 66, 'value': 8}, {'id': 2, 'name': 29, 'value': 5}, {'id': 3, 'name': 99, 'value': 3}, {'id': 4, 'name': 15, 'value': 9}]
Am trying to replace 'name' with 'wid' and 'value' with 'wrt', how can I do it on same list?
My output should be like
inp1=[{'id': 0, 'wid': 98, 'wrt': 9}, {'id': 1, 'wid': 66, 'wrt': 8}, {'id': 2, 'wid': 29, 'wrt': 5}, {'id': 3, 'wid': 99, 'wrt': 3}, {'id': 4, 'wid': 15, 'wrt': 9}]
I tried below, but it doesn't work as list cannot be indexed with string but integer
inp1['name'] = inp1['wid']
inp1['value'] = inp1['wrt']
I tried if I can find any examples, but mostly I found only this for dictionary and not list.
You need to iterate each item, and remove the old entry (dict.pop is handy for this - it removes an entry and return the value) and assign to new keyes:
>>> inp1 = [
... {'id': 0, 'name': 98, 'value': 9},
... {'id': 1, 'name': 66, 'value': 8},
... {'id': 2, 'name': 29, 'value': 5},
... {'id': 3, 'name': 99, 'value': 3},
... {'id': 4, 'name': 15, 'value': 9}
... ]
>>>
>>> for d in inp1:
... d['wid'] = d.pop('name')
... d['wrt'] = d.pop('value')
...
>>> inp1
[{'wid': 98, 'id': 0, 'wrt': 9},
{'wid': 66, 'id': 1, 'wrt': 8},
{'wid': 29, 'id': 2, 'wrt': 5},
{'wid': 99, 'id': 3, 'wrt': 3},
{'wid': 15, 'id': 4, 'wrt': 9}]
def f(item):
if(item.has_key('name') and not item.has_key('wid')):
item['wid']=item.pop('name')
if(item.has_key('value') and not item.has_key('wrt')):
item['wrt']=item.pop('value')
map(f,inp1)
Output:
[{'wrt': 9, 'wid': 98, 'id': 0}, {'wrt': 8, 'wid': 66, 'id': 1}, {'wrt': 5, 'wid': 29, 'id': 2}, {'wrt': 3, 'wid': 99, 'id': 3}, {'wrt': 9, 'wid': 15, 'id': 4}]

Categories

Resources