I've got a list of lists containing integers sorted at the moment by the sum of the contents:
[[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 2, 0], [1, 0, 1], [1, 1, 0], [2, 0, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], [0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0], [4, 0, 0]]
I would like to sort them in ascending order by the common structure of its contents i.e like
[[1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 0, 1], [0, 0, 2], [0,0,3], [0,0,4]... ]
I have seen the docs but I can't figure out how I can do this.
Is this what you're after...
>>> l = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 2, 0], [1, 0, 1], [1, 1, 0], [2, 0, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], [0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0], [4, 0, 0]]
>>> l.sort(key=lambda x: (-x.count(0), x[::-1]))
>>> l
[[1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4], [1, 1, 0], [2, 1, 0], [3, 1, 0], [1, 2, 0], [2, 2, 0], [1, 3, 0], [1, 0, 1], [2, 0, 1], [3, 0, 1], [0, 1, 1], [0, 2, 1], [0, 3, 1], [1, 0, 2], [2, 0, 2], [0, 1, 2], [0, 2, 2], [1, 0, 3], [0, 1, 3], [1, 1, 1], [2, 1, 1], [1, 2, 1], [1, 1, 2]]
See http://docs.python.org/2/howto/sorting.html for an explanation of the Python sort capability, including a discussion of "Key Functions" which gives you whatever additional flexibility you need beyond the basic sort.
Use your sorting criteria function as key in the sorting.
input_list = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 2, 0], [1, 0, 1], [1, 1, 0], [2, 0, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], [0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0], [4, 0, 0]]
sorted_list = sorted(input_list,key=my_sorting_func)
Related
I have a column in dataframe of type list
categories
[0, 0, 2, 2, 2]
[0, 0, 2, 2]
[0, 0, 2, 2, 2]
[1, 1, 2, 2]
[2, 2, 0, 0]
[1, 0, 2, 3]
here is the sample list
li = [[0, 0, 2, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [1, 1, 2, 2], [2, 2, 0, 0], [1, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [0, 0, 2, 2], [2, 2, 0, 0], [2, 2, 0, 0], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [2, 2, 0, 0], [2, 2], [1, 1, 2], [0, 2, 2, 0], [0, 0, 2, 2], [0, 1], [0, 0], [0, 0, 2, 2], [0, 0], [0, 0, 2, 2], [0, 2, 2, 0], [2, 2, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [2, 2, 0, 0], [0, 0, 2, 2], [2, 2, 0, 1], [2, 2, 0, 0], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [2, 1], [0, 0, 2, 2, 2], [2, 2, 0, 0], [2, 0], [2, 2, 0, 0], [0, 2], [0, 2, 2], [0, 0, 2, 2], [0, 2, 2, 0], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [0, 0, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [3, 2, 0, 0], [0, 0], [0, 0, 2, 2], [0, 0, 2, 2, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [1, 3], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 2, 0, 2], [0, 0, 2, 2], [2, 2, 0, 0], [2, 2, 0, 0], [2, 2], [0, 0, 2, 2], [0, 2], [0, 0, 2, 2], [0, 0, 2, 2], [2, 2, 0], [2, 2, 0, 0], [0, 0, 2, 2], [0, 0, 2], [2], [0, 0, 2, 2], [2, 2, 2, 1, 1], [0, 0], [0, 3], [2, 2], [1, 2], [1,3]]
I want to create a new column (class_name) based on the following rule
The rules are based on priority and should be done one after other
if 1 and 3 are present, set class_name to class1
On the remaining rows, wherever 1 is present, set class_name to class2
On the remaining rows, wherever 3 is present, set class_name to class3
if 0 and 2 are present, set class_name to class4
On the remaining rows, wherever 0 is present, set class_name to class5
On the remaining rows, wherever 2 is present, set class_name to class6
What I have tried so far
df.loc[:, "class_name"] = None
for index, row in df.iterrows():
if row["class_name"] == None:
categories = list(row["categories"])
if 1 in categories and 3 in categories:
df.loc[index, "class_name"] = "class1"
Similarly, for each condition I have a separate loop.. but it's too slow.. is there a way to do it without looping ?
I think I understood the question correctly. And I tried something like this, it seems to work fine.
import pandas as pd
li = [[0, 0, 2, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [1, 1, 2, 2], [2, 2, 0, 0], [1, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [0, 0, 2, 2], [2, 2, 0, 0], [2, 2, 0, 0], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [2, 2, 0, 0], [2, 2], [1, 1, 2], [0, 2, 2, 0], [0, 0, 2, 2], [0, 1], [0, 0], [0, 0, 2, 2], [0, 0], [0, 0, 2, 2], [0, 2, 2, 0], [2, 2, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [2, 2, 0, 0], [0, 0, 2, 2], [2, 2, 0, 1], [2, 2, 0, 0], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [2, 1], [0, 0, 2, 2, 2], [2, 2, 0, 0], [2, 0], [2, 2, 0, 0], [0, 2], [0, 2, 2], [0, 0, 2, 2], [0, 2, 2, 0], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2, 2], [0, 0, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [3, 2, 0, 0], [0, 0], [0, 0, 2, 2], [0, 0, 2, 2, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [1, 3], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2], [0, 2, 0, 2], [0, 0, 2, 2], [2, 2, 0, 0], [2, 2, 0, 0], [2, 2], [0, 0, 2, 2], [0, 2], [0, 0, 2, 2], [0, 0, 2, 2], [2, 2, 0], [2, 2, 0, 0], [0, 0, 2, 2], [0, 0, 2], [2], [0, 0, 2, 2], [2, 2, 2, 1, 1], [0, 0], [0, 3], [2, 2], [1, 2], [1,3]]
df=pd.DataFrame(data={'category':li})
def check(x):
class_name=False
if all(item in x for item in [1,3]):
class_name='class1'
elif not all(item in x for item in [1,3]) and 1 in x:
class_name='class2'
elif not all(item in x for item in [1,3]) and 3 in x:
class_name='class3'
elif all(item in x for item in [0,2]):
class_name='class4'
elif 0 in x:
class_name='class5'
elif 2 in x:
class_name='class6'
else:
class_name='no_class'
return class_name
df['check']=df['category'].apply(lambda x: check(x))
print(df)
'''
category check
66 [1, 3] class1
92 [1, 3] class1
3 [1, 1, 2, 2] class2
5 [1, 0, 2, 2] class2
17 [1, 1, 2] class2
20 [0, 1] class2
32 [2, 2, 0, 1] class2
41 [2, 1] class2
87 [2, 2, 2, 1, 1] class2
91 [1, 2] class2
59 [3, 2, 0, 0] class3
89 [0, 3] class3
0 [0, 0, 2, 2, 2] class4
'''
Okay, so I ran your problem using a couple of different methods. The fastest of them all was using pandas.DataFrame.apply. Here's the code:
from __future__ import annotations
import pandas as pd
def class_name(row: list) -> str | None:
if 1 in row and 3 in row:
return "class1"
if 1 in row:
return "class2"
if 3 in row:
return "class3"
if 0 in row and 2 in row:
return "class4"
if 0 in row:
return "class5"
if 2 in row:
return "class6"
return None
# == How to use ============================
df["class_name"] = df["categories"].apply(class_name)
# Result using on a dataframe with 186000 rows:
"""
CPU times: user 80.9 ms, sys: 904 µs, total: 81.8 ms
Wall time: 82 ms
"""
Other Implementations I Tried
I've also tried some other implementations to compare. Here's them:
from __future__ import annotations
import pandas as pd
# == Code to Generate Sample DataFrame ==============
li = [
[0, 0, 2, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2, 2],
[1, 1, 2, 2],
[2, 2, 0, 0],
[1, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2, 2],
[0, 0, 2, 2],
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[2, 2, 0, 0],
[2, 2],
[1, 1, 2],
[0, 2, 2, 0],
[0, 0, 2, 2],
[0, 1],
[0, 0],
[0, 0, 2, 2],
[0, 0],
[0, 0, 2, 2],
[0, 2, 2, 0],
[2, 2, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[2, 2, 0, 0],
[0, 0, 2, 2],
[2, 2, 0, 1],
[2, 2, 0, 0],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2, 2],
[2, 1],
[0, 0, 2, 2, 2],
[2, 2, 0, 0],
[2, 0],
[2, 2, 0, 0],
[0, 2],
[0, 2, 2],
[0, 0, 2, 2],
[0, 2, 2, 0],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2, 2],
[0, 0, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[3, 2, 0, 0],
[0, 0],
[0, 0, 2, 2],
[0, 0, 2, 2, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[1, 3],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[0, 2, 0, 2],
[0, 0, 2, 2],
[2, 2, 0, 0],
[2, 2, 0, 0],
[2, 2],
[0, 0, 2, 2],
[0, 2],
[0, 0, 2, 2],
[0, 0, 2, 2],
[2, 2, 0],
[2, 2, 0, 0],
[0, 0, 2, 2],
[0, 0, 2],
[2],
[0, 0, 2, 2],
[2, 2, 2, 1, 1],
[0, 0],
[0, 3],
[2, 2],
[1, 2],
[1, 3],
]
def make_df(size=1):
return pd.DataFrame({'categories': [v for i in range(size) for v in li]})
# == Implementation 1 ============================
def class_name2(row: list) -> str | None:
categories = row["categories"]
if 1 in categories and 3 in categories:
return "class1"
if 1 in categories:
return "class2"
if 3 in categories:
return "class3"
if 0 in categories and 2 in categories:
return "class4"
if 0 in categories:
return "class5"
if 2 in categories:
return "class6"
return None
df = make_df(2000)
df["class_name"] = df.apply(class_name2, axis=1)
# Result:
"""
CPU times: user 1.69 s, sys: 17 ms, total: 1.71 s
Wall time: 1.71 s
"""
# == Implementation 2 ============================
# This is your original implementation
df = make_df(2000)
df.loc[:, "class_name"] = None
for index, row in df.iterrows():
if row["class_name"] == None:
categories = list(row["categories"])
if 1 in categories and 3 in categories:
df.loc[index, "class_name"] = "class1"
elif 1 in categories:
df.loc[index, "class_name"] = "class2"
elif 3 in categories:
df.loc[index, "class_name"] = "class3"
elif 0 in categories and 2 in categories:
df.loc[index, "class_name"] = "class4"
elif 0 in categories:
df.loc[index, "class_name"] = "class5"
elif 2 in categories:
df.loc[index, "class_name"] = "class6"
# Result:
"""
CPU times: user 24.2 s, sys: 65.6 ms, total: 24.3 s
Wall time: 24.5 s
"""
# == Implementation 3 ============================
# This is your original implementation without the if statement
df = make_df(2000)
df.loc[:, "class_name"] = None
for index, row in df.iterrows():
categories = list(row["categories"])
if 1 in categories and 3 in categories:
df.loc[index, "class_name"] = "class1"
elif 1 in categories:
df.loc[index, "class_name"] = "class2"
elif 3 in categories:
df.loc[index, "class_name"] = "class3"
elif 0 in categories and 2 in categories:
df.loc[index, "class_name"] = "class4"
elif 0 in categories:
df.loc[index, "class_name"] = "class5"
elif 2 in categories:
df.loc[index, "class_name"] = "class6"
# Result:
"""
CPU times: user 24 s, sys: 91.2 ms, total: 24.1 s
Wall time: 24.3 s
"""
# == Implementation 4 ============================
# This is your original implementation without the if statement
# and the list conversion
df = make_df(2000)
df.loc[:, "class_name"] = None
for index, row in df.iterrows():
categories = row["categories"]
if 1 in categories and 3 in categories:
df.loc[index, "class_name"] = "class1"
elif 1 in categories:
df.loc[index, "class_name"] = "class2"
elif 3 in categories:
df.loc[index, "class_name"] = "class3"
elif 0 in categories and 2 in categories:
df.loc[index, "class_name"] = "class4"
elif 0 in categories:
df.loc[index, "class_name"] = "class5"
elif 2 in categories:
df.loc[index, "class_name"] = "class6"
# Result:
"""
CPU times: user 23.4 s, sys: 80 ms, total: 23.5 s
Wall time: 24.2 s
"""
# == Implementation 5 ============================
# Using `swifter`. Install swifter before trying this one:
# pip install swifter
import swifter
def class_name(row: list) -> str | None:
if 1 in row and 3 in row:
return "class1"
if 1 in row:
return "class2"
if 3 in row:
return "class3"
if 0 in row and 2 in row:
return "class4"
if 0 in row:
return "class5"
if 2 in row:
return "class6"
return None
df = make_df(2000)
df["class_name"] = df["categories"].swifter.apply(class_name)
# Result:
"""
CPU times: user 572 ms, sys: 11 ms, total: 582 ms
Wall time: 930 ms
"""
Summary
Here's a summary of all the results:
Implementation
Total Time
Times Faster
Best
82 ms
300x
Implementation 1
1.71 s
14.3x
Implementation 2
24.5 s
1x
Implementation 3
24.3 s
1.008x
Implementation 4
24.2 s
1.012x
Implementation 5
930 ms
26x
I have a Python list with values, say:
a = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
I want to append in a loop a new list 'b' to the list 'a'.
b = [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]
The result should look like (when adding 'b' once to 'a'):
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
Now, I want to append the list b N times to the list a.
Both a and b have shape (4,3)
The result should then have shape: (N+1,4,3)
How do I do this?
Native Python Lists will not behave the way you expect here as described in a few comments, so if you can use a 3rd party library, consider NumPy, which will behave more like a matrix of values as you expect and can then be converted back into a Python List
Setup
>>> a = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
>>> b = [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]
Replicate b vertically
the second argument to np.tile() describes the replications in each dimension
.reshape() to prepare it as a 3-dimensional array
>>> import numpy as np
>>> b_tiled = np.tile(np.array(b), (4,1)).reshape(4,4,3)
>>> b_tiled
array([[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]]])
Collect a and b_tiled into the same array
NOTE a should be reshaped or [a] to match the shape of b_tiled
>>> np.vstack((np.array([a]), b_tiled))
array([[[0, 0, 0],
[1, 0, 1],
[1, 1, 0],
[0, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]]])
.tolist()
You can use .tolist() to make a native Python list again, though it may be more convenient to you as a numpy array
>>> np.vstack((np.array([a]), b_tiled)).tolist()
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
Wrapping up my contributions to this discussion into an actual answer, summarizing what I think are the two best solutions:
1 Using NumPy arrays
Start from #Andreas' solution, but follow #ti7's lead and wrap the results into a numpy array, which manages the memory correctly:
result = np.array([a] + [b] * 5)
This solution brings the results into much more usable and versatile NumPy arrays.
2 Using deepcopy
Start from #Andreas' solution, and add a deep copy so the result does not alias parts of the array together:
import copy
result = [a] + [copy.deepcopy(b) for _ in range(5)]
This solution keeps the results as standard Python lists of lists.
Kuddos to #ti7 for noticing that the deep copy had to be done by instance of b rather than over the results, since deepcopy does not break aliasing that is internal to its input; and to #Andreas for assembling this line of code from the comments.
Caveat: since a is not deep copied here, result[0] is an alias for a, and changes to either will change both. Deep copy a too to avoid this.
add the lists and multiply the second list:
l = [a] + [b]*5
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
In case you want to modify the list values later on, be aware that you actually have 5x the SAME list referenced (as mentioned by #ti7), this means if you change one value in list b you change all, like this:
l[1][1][1] = "foo"
[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
to avoid that use (as mentioned by #joanis):
l = [a] + [copy.deepcopy(b) for _ in range(5)]
[a+b*5] this will create
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
list = [[1, 2, 3, 0, 0], [0, 0, 3, 2, 1], [1, 0, 0, 3, 2],
[2, 3, 1, 0, 0], [3, 0, 1, 2, 0], [2, 0, 1, 3, 0]]
I would like to check if the number 1 is in the third column of all the nested lists, if it is than it should replace the 1 with a 0 and the 2 in that list with a 1.
Thanks in advance
Try the following:
nested_lists = [[1, 2, 3, 0, 0], [0, 0, 3, 2, 1], [1, 0, 0, 3, 2],
[2, 3, 1, 0, 0], [3, 0, 1, 2, 0], [2, 0, 1, 3, 0]]
for list_ in nested_lists:
if list_[2] == 1:
list_[2] = 0
list_ = [1 if n == 2 else n for n in list_]
After execution, nested_lists goes from the given
[[1, 2, 3, 0, 0], [0, 0, 3, 2, 1], [1, 0, 0, 3, 2],
[2, 3, 1, 0, 0], [3, 0, 1, 2, 0], [2, 0, 1, 3, 0]]
To
[[1, 2, 3, 0, 0], [0, 0, 3, 2, 1], [1, 0, 0, 3, 2]
[1, 3, 0, 0, 0], [3, 0, 0, 1, 0], [1, 0, 0, 3, 0]]
As an attempt to further my knowledge in python, I have started to create a very simple tic tac toe AI.
Currently, I am stumped at some behavior I have not expected from python where when I append a class instance variable to a local list and change the item in the local list, the instance variable will have changed too.
How can I change only the local list element without affecting the class instance variable?
This is the extract of the program which is affected:
class ticAI:
def __init__(self, board):
self.board = board
self.tic = tictactoe(board)
def calc(self):
possibilities = []
ycord = 0
for y in self.board:
xcord = 0
for x in y:
if x == 0:
possibilities.append(self.board)
possibilities[len(possibilities)-1][ycord][xcord] = 2
print(self.board)
xcord += 1
ycord += 1
self.board looks like this:
[
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]
]
and outputs this:
[[2, 0, 0], [0, 1, 0], [0, 0, 0]]
[[2, 2, 0], [0, 1, 0], [0, 0, 0]]
[[2, 2, 2], [0, 1, 0], [0, 0, 0]]
[[2, 2, 2], [2, 1, 0], [0, 0, 0]]
[[2, 2, 2], [2, 1, 2], [0, 0, 0]]
[[2, 2, 2], [2, 1, 2], [2, 0, 0]]
[[2, 2, 2], [2, 1, 2], [2, 2, 0]]
[[2, 2, 2], [2, 1, 2], [2, 2, 2]]
it should however, output this:
[[2, 0, 0], [0, 1, 0], [0, 0, 0]]
[[0, 2, 0], [0, 1, 0], [0, 0, 0]]
[[0, 0, 2], [0, 1, 0], [0, 0, 0]]
[[0, 0, 0], [2, 1, 0], [0, 0, 0]]
[[0, 0, 0], [0, 1, 2], [0, 0, 0]]
[[0, 0, 0], [0, 1, 0], [2, 0, 0]]
[[0, 0, 0], [0, 1, 0], [0, 2, 0]]
[[0, 0, 0], [0, 1, 0], [0, 0, 2]]
As made aware by #jonrsharpe, you can use deepcopy to create a copy of a variable.
Original code:
possibilities.append(self.board)
possibilities[len(possibilities)-1][ycord][xcord] = 2
print(self.board)
New code:
b = copy.deepcopy(self.board)
possibilities.append(b)
possibilities[len(possibilities)-1][ycord][xcord] = 2
print(self.board)
I have 2 functions that use mahotas python library for detecting branched point and end point in an image.
The 2 functions:
def branchedPoints(skel):
branch1=np.array([[2, 1, 2], [1, 1, 1], [2, 2, 2]])
branch2=np.array([[1, 2, 1], [2, 1, 2], [1, 2, 1]])
branch3=np.array([[1, 2, 1], [2, 1, 2], [1, 2, 2]])
branch4=np.array([[2, 1, 2], [1, 1, 2], [2, 1, 2]])
branch5=np.array([[1, 2, 2], [2, 1, 2], [1, 2, 1]])
branch6=np.array([[2, 2, 2], [1, 1, 1], [2, 1, 2]])
branch7=np.array([[2, 2, 1], [2, 1, 2], [1, 2, 1]])
branch8=np.array([[2, 1, 2], [2, 1, 1], [2, 1, 2]])
branch9=np.array([[1, 2, 1], [2, 1, 2], [2, 2, 1]])
br1=mh.morph.hitmiss(skel,branch1)
br2=mh.morph.hitmiss(skel,branch2)
br3=mh.morph.hitmiss(skel,branch3)
br4=mh.morph.hitmiss(skel,branch4)
br5=mh.morph.hitmiss(skel,branch5)
br6=mh.morph.hitmiss(skel,branch6)
br7=mh.morph.hitmiss(skel,branch7)
br8=mh.morph.hitmiss(skel,branch8)
br9=mh.morph.hitmiss(skel,branch9)
return br1+br2+br3+br4+br5+br6+br7+br8+br9
def endPoints(skel):
endpoint1=np.array([[0, 0, 0],[0, 1, 0],[2, 1, 2]])
endpoint2=np.array([[0, 0, 0],[0, 1, 2],[0, 2, 1]])
endpoint3=np.array([[0, 0, 2],[0, 1, 1],[0, 0, 2]])
endpoint4=np.array([[0, 2, 1],[0, 1, 2],[0, 0, 0]])
endpoint5=np.array([[2, 1, 2],[0, 1, 0],[0, 0, 0]])
endpoint6=np.array([[1, 2, 0],[2, 1, 0],[0, 0, 0]])
endpoint7=np.array([[2, 0, 0],[1, 1, 0],[2, 0, 0]])
endpoint8=np.array([[0, 0, 0],[2, 1, 0],[1, 2, 0]])
ep1=mh.morph.hitmiss(skel,endpoint1)
ep2=mh.morph.hitmiss(skel,endpoint2)
ep3=mh.morph.hitmiss(skel,endpoint3)
ep4=mh.morph.hitmiss(skel,endpoint4)
ep5=mh.morph.hitmiss(skel,endpoint5)
ep6=mh.morph.hitmiss(skel,endpoint6)
ep7=mh.morph.hitmiss(skel,endpoint7)
ep8=mh.morph.hitmiss(skel,endpoint8)
ep = ep1+ep2+ep3+ep4+ep5+ep6+ep7+ep8
return ep
There is a way to obtain these functions with Scikit-image library?
Morphology section of scikit image hasn't got hit and miss transform.
from scipy import ndimage
ndimage.binary_hit_or_miss(...)
import mahotas as mh
def branchedPoints(skel, showSE=True):
X=[]
#cross X
X0 = np.array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]])
X1 = np.array([[1, 0, 1],
[0, 1, 0],
[1, 0, 1]])
X.append(X0)
X.append(X1)
#T like
T=[]
#T0 contains X0
T0=np.array([[2, 1, 2],
[1, 1, 1],
[2, 2, 2]])
T1=np.array([[1, 2, 1],
[2, 1, 2],
[1, 2, 2]]) # contains X1
T2=np.array([[2, 1, 2],
[1, 1, 2],
[2, 1, 2]])
T3=np.array([[1, 2, 2],
[2, 1, 2],
[1, 2, 1]])
T4=np.array([[2, 2, 2],
[1, 1, 1],
[2, 1, 2]])
T5=np.array([[2, 2, 1],
[2, 1, 2],
[1, 2, 1]])
T6=np.array([[2, 1, 2],
[2, 1, 1],
[2, 1, 2]])
T7=np.array([[1, 2, 1],
[2, 1, 2],
[2, 2, 1]])
T.append(T0)
T.append(T1)
T.append(T2)
T.append(T3)
T.append(T4)
T.append(T5)
T.append(T6)
T.append(T7)
#Y like
Y=[]
Y0=np.array([[1, 0, 1],
[0, 1, 0],
[2, 1, 2]])
Y1=np.array([[0, 1, 0],
[1, 1, 2],
[0, 2, 1]])
Y2=np.array([[1, 0, 2],
[0, 1, 1],
[1, 0, 2]])
Y2=np.array([[1, 0, 2],
[0, 1, 1],
[1, 0, 2]])
Y3=np.array([[0, 2, 1],
[1, 1, 2],
[0, 1, 0]])
Y4=np.array([[2, 1, 2],
[0, 1, 0],
[1, 0, 1]])
Y5=np.rot90(Y3)
Y6 = np.rot90(Y4)
Y7 = np.rot90(Y5)
Y.append(Y0)
Y.append(Y1)
Y.append(Y2)
Y.append(Y3)
Y.append(Y4)
Y.append(Y5)
Y.append(Y6)
Y.append(Y7)
bp = np.zeros(skel.shape, dtype=int)
for x in X:
bp = bp + mh.morph.hitmiss(skel,x)
for y in Y:
bp = bp + mh.morph.hitmiss(skel,y)
for t in T:
bp = bp + mh.morph.hitmiss(skel,t)
if showSE==True:
fig = plt.figure(figsize=(4,5))
tX =['X0','X1']
tY =['Y'+str(i) for i in range(0,8)]
tT =['T'+str(i) for i in range(0,8)]
ti= tX+tY+tT
SE=X+Y+T
print len(SE), len(ti)
n = 1
ti = iter(ti)
for se in SE:
#print next(ti)
#print se
mycmap = mpl.colors.ListedColormap(['black','blue','red'])
ax = fig.add_subplot(4,5,n,frameon=False, xticks=[], yticks=[])
title(str(next(ti)))
imshow(se, interpolation='nearest',vmin=0,vmax=2,cmap=mycmap)
n = n+1
fig.subplots_adjust(hspace=0.1,wspace=0.08)
#ax_cb = fig.add_axes([.9,.25,.1,.3])#
color_vals=[0,1,2]
#cb = mpl.colorbar.ColorbarBase(ax_cb,cmap=mycmap, ticks=color_vals)
#cb.set_ticklabels(['back', 'hit', 'don\'t care'])
plt.show()
return bp
def endPoints(skel):
endpoint1=np.array([[0, 0, 0],
[0, 1, 0],
[2, 1, 2]])
endpoint2=np.array([[0, 0, 0],
[0, 1, 2],
[0, 2, 1]])
endpoint3=np.array([[0, 0, 2],
[0, 1, 1],
[0, 0, 2]])
endpoint4=np.array([[0, 2, 1],
[0, 1, 2],
[0, 0, 0]])
endpoint5=np.array([[2, 1, 2],
[0, 1, 0],
[0, 0, 0]])
endpoint6=np.array([[1, 2, 0],
[2, 1, 0],
[0, 0, 0]])
endpoint7=np.array([[2, 0, 0],
[1, 1, 0],
[2, 0, 0]])
endpoint8=np.array([[0, 0, 0],
[2, 1, 0],
[1, 2, 0]])
ep1=mh.morph.hitmiss(skel,endpoint1)
ep2=mh.morph.hitmiss(skel,endpoint2)
ep3=mh.morph.hitmiss(skel,endpoint3)
ep4=mh.morph.hitmiss(skel,endpoint4)
ep5=mh.morph.hitmiss(skel,endpoint5)
ep6=mh.morph.hitmiss(skel,endpoint6)
ep7=mh.morph.hitmiss(skel,endpoint7)
ep8=mh.morph.hitmiss(skel,endpoint8)
ep = ep1+ep2+ep3+ep4+ep5+ep6+ep7+ep8
return ep
a = np.array([[0,0,0,0,0,0],
[0,0,1,0,1,0],
[0,1,1,0,1,0],
[0,0,0,1,0,0],
[0,0,1,0,1,0],
[0,1,0,0,0,0]])
lab,_ = mh.label(a>0)
sk =mh.thin(a)
print a.dtype, sk.dtype
bp = branchedPoints(a>0)
h = mh.labeled.labeled_size(bp)
ep = endPoints(a)
subplot(141)
title('skeleton')
imshow(a,interpolation='nearest')
subplot(142)
title('label')
imshow(lab,interpolation='nearest')
subplot(143)
title('junction')
imshow(bp,interpolation='nearest')
subplot(144)
title('end-points')
imshow(ep,interpolation='nearest')
The structuring elements can be plotted:
Branched points and end-points can be detected from a skeleton-like shape:
from a bigger image: