Get all monotonic sublists from a list
$begingroup$
So, i recently wrote code that can count the number of monotonic items (increasing, decreasing and constant). For an input such as x = [1,2,3,3,2,0] The provided example was:
1. Increasing: [1,2], [2,3], [1,2,3]
2. Constant: [3,3]
3. Decreasing: [3,2], [2,0], [3,2,0]
So, i broke the problem into two steps, firstly just getting all biggest monotonic sequences, and then finding all sub-lists within those sequences.
During the process it seemed to me that things started getting rather long and i was surprised by how "big" the whole thing seemed by the end of it. I was wondering if there are any tricks i missed or steps i could have done better. Also looking for tips on code readability as well. Code starts below:
x = [1,2,3,3,2,0]
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
def two_item_relation(prev, curr): #compare two items in list, results in what is effectively a 3 way flag
if prev < curr:
return "increasing"
elif prev == curr:
return "equal"
else:
return "decreasing"
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
def all_subcombinations(lst): #given a list, get all "sublists" using sliding windows
if len(lst) < 3:
return [lst]
else:
result =
for i in range(2, len(lst) + 1):
for j in range(len(lst) - i + 1):
result.extend([lst[j:j + i]])
return result
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
'equal': [[3, 3]],
'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
python python-3.x
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
$endgroup$
add a comment |
$begingroup$
So, i recently wrote code that can count the number of monotonic items (increasing, decreasing and constant). For an input such as x = [1,2,3,3,2,0] The provided example was:
1. Increasing: [1,2], [2,3], [1,2,3]
2. Constant: [3,3]
3. Decreasing: [3,2], [2,0], [3,2,0]
So, i broke the problem into two steps, firstly just getting all biggest monotonic sequences, and then finding all sub-lists within those sequences.
During the process it seemed to me that things started getting rather long and i was surprised by how "big" the whole thing seemed by the end of it. I was wondering if there are any tricks i missed or steps i could have done better. Also looking for tips on code readability as well. Code starts below:
x = [1,2,3,3,2,0]
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
def two_item_relation(prev, curr): #compare two items in list, results in what is effectively a 3 way flag
if prev < curr:
return "increasing"
elif prev == curr:
return "equal"
else:
return "decreasing"
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
def all_subcombinations(lst): #given a list, get all "sublists" using sliding windows
if len(lst) < 3:
return [lst]
else:
result =
for i in range(2, len(lst) + 1):
for j in range(len(lst) - i + 1):
result.extend([lst[j:j + i]])
return result
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
'equal': [[3, 3]],
'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
python python-3.x
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
$endgroup$
add a comment |
$begingroup$
So, i recently wrote code that can count the number of monotonic items (increasing, decreasing and constant). For an input such as x = [1,2,3,3,2,0] The provided example was:
1. Increasing: [1,2], [2,3], [1,2,3]
2. Constant: [3,3]
3. Decreasing: [3,2], [2,0], [3,2,0]
So, i broke the problem into two steps, firstly just getting all biggest monotonic sequences, and then finding all sub-lists within those sequences.
During the process it seemed to me that things started getting rather long and i was surprised by how "big" the whole thing seemed by the end of it. I was wondering if there are any tricks i missed or steps i could have done better. Also looking for tips on code readability as well. Code starts below:
x = [1,2,3,3,2,0]
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
def two_item_relation(prev, curr): #compare two items in list, results in what is effectively a 3 way flag
if prev < curr:
return "increasing"
elif prev == curr:
return "equal"
else:
return "decreasing"
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
def all_subcombinations(lst): #given a list, get all "sublists" using sliding windows
if len(lst) < 3:
return [lst]
else:
result =
for i in range(2, len(lst) + 1):
for j in range(len(lst) - i + 1):
result.extend([lst[j:j + i]])
return result
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
'equal': [[3, 3]],
'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
python python-3.x
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
$endgroup$
So, i recently wrote code that can count the number of monotonic items (increasing, decreasing and constant). For an input such as x = [1,2,3,3,2,0] The provided example was:
1. Increasing: [1,2], [2,3], [1,2,3]
2. Constant: [3,3]
3. Decreasing: [3,2], [2,0], [3,2,0]
So, i broke the problem into two steps, firstly just getting all biggest monotonic sequences, and then finding all sub-lists within those sequences.
During the process it seemed to me that things started getting rather long and i was surprised by how "big" the whole thing seemed by the end of it. I was wondering if there are any tricks i missed or steps i could have done better. Also looking for tips on code readability as well. Code starts below:
x = [1,2,3,3,2,0]
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
def two_item_relation(prev, curr): #compare two items in list, results in what is effectively a 3 way flag
if prev < curr:
return "increasing"
elif prev == curr:
return "equal"
else:
return "decreasing"
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
def all_subcombinations(lst): #given a list, get all "sublists" using sliding windows
if len(lst) < 3:
return [lst]
else:
result =
for i in range(2, len(lst) + 1):
for j in range(len(lst) - i + 1):
result.extend([lst[j:j + i]])
return result
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
'equal': [[3, 3]],
'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
python python-3.x
python python-3.x
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked 8 hours ago
Paritosh SinghParitosh Singh
1164
1164
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Paritosh Singh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
A few changes:
Strings shouldn't be used here for keeping track of comparison results. Strings are prone to being typo'd, and may lead to unexpected results (like indexing result causing KeyErrors at runtime). I'd take a page from Java (and other languages) and use -1, 0 and 1 to indicate the results of a comparison. You can see it being used in an answer here. I'd make the following changes:
result = {1: , # Increasing
0: , # Equal
-1: # Decreasing
}
def two_item_relation(prev, curr):
if prev < curr:
return 1
elif prev == curr:
return 0
else:
return -1
It's much harder to mistype -1 than it is, for example, "decreasing".
You could also look into enums, although I don't have enough experience with them in Python to make a good example of their use.
If you really wanted Strings for pretty printing purposes (like for your output at the bottom), you could maintain a dictionary mapping comparison numbers to strings:
pp_result = {1: "Increasing",
0: "Equal",
-1: "Decreasing"
}
The point is that you shouldn't use easily mistyped things as keys unless necessary.
Strings also may be slower to compare, and may take more memory, but hash caching and String interning may negate those problems in some cases.
You could also write that function as something like:
def two_item_relation(prev, curr):
return 1 if prev < curr else
0 if prev == curr else
-1
But I'm probably going to get yelled at for even bringing that up. Conditional expressions/ternaries are nice in many cases when you want to conditionally return one or another thing, but they get a little murky as soon as you're using them to decide between three different things. It's especially bad here because this pretty much needs to be split over a few lines, which necessitates the use of line continuation characters, which are a little noisy.
I'm bringing it up in case you're unaware of conditional expressions, not because I'm necessarily suggesting their use here.
Honestly, I'm too tired right now to comment on the algorithm, but hopefully this was helpful.
$endgroup$
add a comment |
$begingroup$
Organize
I believe you would do well to restructure your code. You have two functions, why not write one more, and then separate your testing from your actual code?
if __name__ == '__main__':
x = [1,2,3,3,2,0]
result = find_monotone_sequences(x) # Wrap your code in this function
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
#{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
# 'equal': [[3, 3]],
# 'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
Use collections.defaultdict
Next, take advantage of some built-in features:
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
This is a dictionary where every value defaults to an empty list. Another word for that is a collections.defaultdict:
from collections import defaultdict
result = defaultdict(list) # Note: no parens after list - passing in function
Now you don't have to provide the explicit names and values!
Use your iterators
Next, you should take advantage of the iterator you are already creating!
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
Instead of accessing x[0] and x[1], why not use the iterator?
xiter = iter(x)
prev = next(xiter)
curr = next(xiter)
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
for curr in xiter:
# etc...
Recognize patterns in your code (use itertools!)
Finally, I'd like to point out the behavior of your main loop:
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
This loops over the input values, comparing each value with the prior one, and determines a 'state'. Depending on the state, the input values are broken into groups corresponding to the state.
Or: the input sequence is grouped by the computed state.
It turns out there's an app for that: itertools.groupby will take a sequence and a key function, and break the sequence into groups according to the values taken on by the key!
This means your can rewrite your code into a simple processing loop that computes the state and associates it with the values (except the initial member, of course). Furthermore, if you investigate the recipes section of the itertools module, you will find a function named pairwise that allows a sequence to be processed in pairs:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Adding this function to your code enables you to do this:
seq = x # x is not a very good name
relations = [(two_item_relation(*pair), *pair) for pair in pairwise(seq)]
There is still the matter of the special treatment of the first value, but you can do it with the values all in hand.
(If you're just learning Python, the *pair syntax "flattens" the pair in place. It is equivalent to writing: pair[0], pair[1] where-ever *pair is seen. Thus relation(*pair) is like relation(pair[0], pair[1]).)
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Paritosh Singh is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213198%2fget-all-monotonic-sublists-from-a-list%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
A few changes:
Strings shouldn't be used here for keeping track of comparison results. Strings are prone to being typo'd, and may lead to unexpected results (like indexing result causing KeyErrors at runtime). I'd take a page from Java (and other languages) and use -1, 0 and 1 to indicate the results of a comparison. You can see it being used in an answer here. I'd make the following changes:
result = {1: , # Increasing
0: , # Equal
-1: # Decreasing
}
def two_item_relation(prev, curr):
if prev < curr:
return 1
elif prev == curr:
return 0
else:
return -1
It's much harder to mistype -1 than it is, for example, "decreasing".
You could also look into enums, although I don't have enough experience with them in Python to make a good example of their use.
If you really wanted Strings for pretty printing purposes (like for your output at the bottom), you could maintain a dictionary mapping comparison numbers to strings:
pp_result = {1: "Increasing",
0: "Equal",
-1: "Decreasing"
}
The point is that you shouldn't use easily mistyped things as keys unless necessary.
Strings also may be slower to compare, and may take more memory, but hash caching and String interning may negate those problems in some cases.
You could also write that function as something like:
def two_item_relation(prev, curr):
return 1 if prev < curr else
0 if prev == curr else
-1
But I'm probably going to get yelled at for even bringing that up. Conditional expressions/ternaries are nice in many cases when you want to conditionally return one or another thing, but they get a little murky as soon as you're using them to decide between three different things. It's especially bad here because this pretty much needs to be split over a few lines, which necessitates the use of line continuation characters, which are a little noisy.
I'm bringing it up in case you're unaware of conditional expressions, not because I'm necessarily suggesting their use here.
Honestly, I'm too tired right now to comment on the algorithm, but hopefully this was helpful.
$endgroup$
add a comment |
$begingroup$
A few changes:
Strings shouldn't be used here for keeping track of comparison results. Strings are prone to being typo'd, and may lead to unexpected results (like indexing result causing KeyErrors at runtime). I'd take a page from Java (and other languages) and use -1, 0 and 1 to indicate the results of a comparison. You can see it being used in an answer here. I'd make the following changes:
result = {1: , # Increasing
0: , # Equal
-1: # Decreasing
}
def two_item_relation(prev, curr):
if prev < curr:
return 1
elif prev == curr:
return 0
else:
return -1
It's much harder to mistype -1 than it is, for example, "decreasing".
You could also look into enums, although I don't have enough experience with them in Python to make a good example of their use.
If you really wanted Strings for pretty printing purposes (like for your output at the bottom), you could maintain a dictionary mapping comparison numbers to strings:
pp_result = {1: "Increasing",
0: "Equal",
-1: "Decreasing"
}
The point is that you shouldn't use easily mistyped things as keys unless necessary.
Strings also may be slower to compare, and may take more memory, but hash caching and String interning may negate those problems in some cases.
You could also write that function as something like:
def two_item_relation(prev, curr):
return 1 if prev < curr else
0 if prev == curr else
-1
But I'm probably going to get yelled at for even bringing that up. Conditional expressions/ternaries are nice in many cases when you want to conditionally return one or another thing, but they get a little murky as soon as you're using them to decide between three different things. It's especially bad here because this pretty much needs to be split over a few lines, which necessitates the use of line continuation characters, which are a little noisy.
I'm bringing it up in case you're unaware of conditional expressions, not because I'm necessarily suggesting their use here.
Honestly, I'm too tired right now to comment on the algorithm, but hopefully this was helpful.
$endgroup$
add a comment |
$begingroup$
A few changes:
Strings shouldn't be used here for keeping track of comparison results. Strings are prone to being typo'd, and may lead to unexpected results (like indexing result causing KeyErrors at runtime). I'd take a page from Java (and other languages) and use -1, 0 and 1 to indicate the results of a comparison. You can see it being used in an answer here. I'd make the following changes:
result = {1: , # Increasing
0: , # Equal
-1: # Decreasing
}
def two_item_relation(prev, curr):
if prev < curr:
return 1
elif prev == curr:
return 0
else:
return -1
It's much harder to mistype -1 than it is, for example, "decreasing".
You could also look into enums, although I don't have enough experience with them in Python to make a good example of their use.
If you really wanted Strings for pretty printing purposes (like for your output at the bottom), you could maintain a dictionary mapping comparison numbers to strings:
pp_result = {1: "Increasing",
0: "Equal",
-1: "Decreasing"
}
The point is that you shouldn't use easily mistyped things as keys unless necessary.
Strings also may be slower to compare, and may take more memory, but hash caching and String interning may negate those problems in some cases.
You could also write that function as something like:
def two_item_relation(prev, curr):
return 1 if prev < curr else
0 if prev == curr else
-1
But I'm probably going to get yelled at for even bringing that up. Conditional expressions/ternaries are nice in many cases when you want to conditionally return one or another thing, but they get a little murky as soon as you're using them to decide between three different things. It's especially bad here because this pretty much needs to be split over a few lines, which necessitates the use of line continuation characters, which are a little noisy.
I'm bringing it up in case you're unaware of conditional expressions, not because I'm necessarily suggesting their use here.
Honestly, I'm too tired right now to comment on the algorithm, but hopefully this was helpful.
$endgroup$
A few changes:
Strings shouldn't be used here for keeping track of comparison results. Strings are prone to being typo'd, and may lead to unexpected results (like indexing result causing KeyErrors at runtime). I'd take a page from Java (and other languages) and use -1, 0 and 1 to indicate the results of a comparison. You can see it being used in an answer here. I'd make the following changes:
result = {1: , # Increasing
0: , # Equal
-1: # Decreasing
}
def two_item_relation(prev, curr):
if prev < curr:
return 1
elif prev == curr:
return 0
else:
return -1
It's much harder to mistype -1 than it is, for example, "decreasing".
You could also look into enums, although I don't have enough experience with them in Python to make a good example of their use.
If you really wanted Strings for pretty printing purposes (like for your output at the bottom), you could maintain a dictionary mapping comparison numbers to strings:
pp_result = {1: "Increasing",
0: "Equal",
-1: "Decreasing"
}
The point is that you shouldn't use easily mistyped things as keys unless necessary.
Strings also may be slower to compare, and may take more memory, but hash caching and String interning may negate those problems in some cases.
You could also write that function as something like:
def two_item_relation(prev, curr):
return 1 if prev < curr else
0 if prev == curr else
-1
But I'm probably going to get yelled at for even bringing that up. Conditional expressions/ternaries are nice in many cases when you want to conditionally return one or another thing, but they get a little murky as soon as you're using them to decide between three different things. It's especially bad here because this pretty much needs to be split over a few lines, which necessitates the use of line continuation characters, which are a little noisy.
I'm bringing it up in case you're unaware of conditional expressions, not because I'm necessarily suggesting their use here.
Honestly, I'm too tired right now to comment on the algorithm, but hopefully this was helpful.
edited 7 hours ago
answered 7 hours ago
CarcigenicateCarcigenicate
3,29211431
3,29211431
add a comment |
add a comment |
$begingroup$
Organize
I believe you would do well to restructure your code. You have two functions, why not write one more, and then separate your testing from your actual code?
if __name__ == '__main__':
x = [1,2,3,3,2,0]
result = find_monotone_sequences(x) # Wrap your code in this function
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
#{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
# 'equal': [[3, 3]],
# 'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
Use collections.defaultdict
Next, take advantage of some built-in features:
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
This is a dictionary where every value defaults to an empty list. Another word for that is a collections.defaultdict:
from collections import defaultdict
result = defaultdict(list) # Note: no parens after list - passing in function
Now you don't have to provide the explicit names and values!
Use your iterators
Next, you should take advantage of the iterator you are already creating!
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
Instead of accessing x[0] and x[1], why not use the iterator?
xiter = iter(x)
prev = next(xiter)
curr = next(xiter)
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
for curr in xiter:
# etc...
Recognize patterns in your code (use itertools!)
Finally, I'd like to point out the behavior of your main loop:
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
This loops over the input values, comparing each value with the prior one, and determines a 'state'. Depending on the state, the input values are broken into groups corresponding to the state.
Or: the input sequence is grouped by the computed state.
It turns out there's an app for that: itertools.groupby will take a sequence and a key function, and break the sequence into groups according to the values taken on by the key!
This means your can rewrite your code into a simple processing loop that computes the state and associates it with the values (except the initial member, of course). Furthermore, if you investigate the recipes section of the itertools module, you will find a function named pairwise that allows a sequence to be processed in pairs:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Adding this function to your code enables you to do this:
seq = x # x is not a very good name
relations = [(two_item_relation(*pair), *pair) for pair in pairwise(seq)]
There is still the matter of the special treatment of the first value, but you can do it with the values all in hand.
(If you're just learning Python, the *pair syntax "flattens" the pair in place. It is equivalent to writing: pair[0], pair[1] where-ever *pair is seen. Thus relation(*pair) is like relation(pair[0], pair[1]).)
$endgroup$
add a comment |
$begingroup$
Organize
I believe you would do well to restructure your code. You have two functions, why not write one more, and then separate your testing from your actual code?
if __name__ == '__main__':
x = [1,2,3,3,2,0]
result = find_monotone_sequences(x) # Wrap your code in this function
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
#{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
# 'equal': [[3, 3]],
# 'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
Use collections.defaultdict
Next, take advantage of some built-in features:
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
This is a dictionary where every value defaults to an empty list. Another word for that is a collections.defaultdict:
from collections import defaultdict
result = defaultdict(list) # Note: no parens after list - passing in function
Now you don't have to provide the explicit names and values!
Use your iterators
Next, you should take advantage of the iterator you are already creating!
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
Instead of accessing x[0] and x[1], why not use the iterator?
xiter = iter(x)
prev = next(xiter)
curr = next(xiter)
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
for curr in xiter:
# etc...
Recognize patterns in your code (use itertools!)
Finally, I'd like to point out the behavior of your main loop:
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
This loops over the input values, comparing each value with the prior one, and determines a 'state'. Depending on the state, the input values are broken into groups corresponding to the state.
Or: the input sequence is grouped by the computed state.
It turns out there's an app for that: itertools.groupby will take a sequence and a key function, and break the sequence into groups according to the values taken on by the key!
This means your can rewrite your code into a simple processing loop that computes the state and associates it with the values (except the initial member, of course). Furthermore, if you investigate the recipes section of the itertools module, you will find a function named pairwise that allows a sequence to be processed in pairs:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Adding this function to your code enables you to do this:
seq = x # x is not a very good name
relations = [(two_item_relation(*pair), *pair) for pair in pairwise(seq)]
There is still the matter of the special treatment of the first value, but you can do it with the values all in hand.
(If you're just learning Python, the *pair syntax "flattens" the pair in place. It is equivalent to writing: pair[0], pair[1] where-ever *pair is seen. Thus relation(*pair) is like relation(pair[0], pair[1]).)
$endgroup$
add a comment |
$begingroup$
Organize
I believe you would do well to restructure your code. You have two functions, why not write one more, and then separate your testing from your actual code?
if __name__ == '__main__':
x = [1,2,3,3,2,0]
result = find_monotone_sequences(x) # Wrap your code in this function
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
#{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
# 'equal': [[3, 3]],
# 'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
Use collections.defaultdict
Next, take advantage of some built-in features:
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
This is a dictionary where every value defaults to an empty list. Another word for that is a collections.defaultdict:
from collections import defaultdict
result = defaultdict(list) # Note: no parens after list - passing in function
Now you don't have to provide the explicit names and values!
Use your iterators
Next, you should take advantage of the iterator you are already creating!
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
Instead of accessing x[0] and x[1], why not use the iterator?
xiter = iter(x)
prev = next(xiter)
curr = next(xiter)
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
for curr in xiter:
# etc...
Recognize patterns in your code (use itertools!)
Finally, I'd like to point out the behavior of your main loop:
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
This loops over the input values, comparing each value with the prior one, and determines a 'state'. Depending on the state, the input values are broken into groups corresponding to the state.
Or: the input sequence is grouped by the computed state.
It turns out there's an app for that: itertools.groupby will take a sequence and a key function, and break the sequence into groups according to the values taken on by the key!
This means your can rewrite your code into a simple processing loop that computes the state and associates it with the values (except the initial member, of course). Furthermore, if you investigate the recipes section of the itertools module, you will find a function named pairwise that allows a sequence to be processed in pairs:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Adding this function to your code enables you to do this:
seq = x # x is not a very good name
relations = [(two_item_relation(*pair), *pair) for pair in pairwise(seq)]
There is still the matter of the special treatment of the first value, but you can do it with the values all in hand.
(If you're just learning Python, the *pair syntax "flattens" the pair in place. It is equivalent to writing: pair[0], pair[1] where-ever *pair is seen. Thus relation(*pair) is like relation(pair[0], pair[1]).)
$endgroup$
Organize
I believe you would do well to restructure your code. You have two functions, why not write one more, and then separate your testing from your actual code?
if __name__ == '__main__':
x = [1,2,3,3,2,0]
result = find_monotone_sequences(x) # Wrap your code in this function
print(" all Outputs ")
result_all_combinations = {}
for k, v in result.items():
result_all_combinations[k] =
for item in v:
result_all_combinations[k].extend(all_subcombinations(item))
print(result_all_combinations)
#Output:
#{'increasing': [[1, 2], [2, 3], [1, 2, 3]],
# 'equal': [[3, 3]],
# 'decreasing': [[3, 2], [2, 0], [3, 2, 0]]}
Use collections.defaultdict
Next, take advantage of some built-in features:
result = {"increasing": ,
"equal": ,
"decreasing": ,
}
This is a dictionary where every value defaults to an empty list. Another word for that is a collections.defaultdict:
from collections import defaultdict
result = defaultdict(list) # Note: no parens after list - passing in function
Now you don't have to provide the explicit names and values!
Use your iterators
Next, you should take advantage of the iterator you are already creating!
prev = x[0]
curr = x[1] #keep track of two items together during iteration, previous and current
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
x_shifted = iter(x)
next(x_shifted) #x_shifted is now similar to x[1:]
Instead of accessing x[0] and x[1], why not use the iterator?
xiter = iter(x)
prev = next(xiter)
curr = next(xiter)
prev_state = two_item_relation(prev, curr) #keep track of previous state
result[prev_state].append([prev]) #handle first item of list
for curr in xiter:
# etc...
Recognize patterns in your code (use itertools!)
Finally, I'd like to point out the behavior of your main loop:
for curr in x_shifted:
curr_state = two_item_relation(prev, curr)
if prev_state == curr_state: #compare if current and previous states were same.
result[curr_state][-1].append(curr)
else: #states were different. aka a change in trend
result[curr_state].append()
result[curr_state][-1].extend([prev, curr])
prev = curr
prev_state = curr_state
This loops over the input values, comparing each value with the prior one, and determines a 'state'. Depending on the state, the input values are broken into groups corresponding to the state.
Or: the input sequence is grouped by the computed state.
It turns out there's an app for that: itertools.groupby will take a sequence and a key function, and break the sequence into groups according to the values taken on by the key!
This means your can rewrite your code into a simple processing loop that computes the state and associates it with the values (except the initial member, of course). Furthermore, if you investigate the recipes section of the itertools module, you will find a function named pairwise that allows a sequence to be processed in pairs:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Adding this function to your code enables you to do this:
seq = x # x is not a very good name
relations = [(two_item_relation(*pair), *pair) for pair in pairwise(seq)]
There is still the matter of the special treatment of the first value, but you can do it with the values all in hand.
(If you're just learning Python, the *pair syntax "flattens" the pair in place. It is equivalent to writing: pair[0], pair[1] where-ever *pair is seen. Thus relation(*pair) is like relation(pair[0], pair[1]).)
answered 7 hours ago
Austin HastingsAustin Hastings
6,3791232
6,3791232
add a comment |
add a comment |
Paritosh Singh is a new contributor. Be nice, and check out our Code of Conduct.
Paritosh Singh is a new contributor. Be nice, and check out our Code of Conduct.
Paritosh Singh is a new contributor. Be nice, and check out our Code of Conduct.
Paritosh Singh is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213198%2fget-all-monotonic-sublists-from-a-list%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown