iamlikeme / rainflow

Implementation of the rainflow-counting algorythm in Python
MIT License
105 stars 34 forks source link

Allow extracting cycle lows and highs #7

Closed iamlikeme closed 6 years ago

iamlikeme commented 6 years ago

Fixes #6.

The motivation is to allow calculating cycle means or scatter-plotting lows vs highs, such as shown in this article.

Function extract_cycles is now changed to a generator which yields cycle lows/highs in and multipliers (1.0 for full cycles, 0.5 for half cycles). This is how it works:

>>> for low, high, mult in rainflow.extract_cycles(y):
...     mean = 0.5 * (high + low)
...     rng = high - low

This change breaks backward compatibility because extract_cycles is changed from a function to a generator and has a new signature.

iamlikeme commented 6 years ago

Fixes #6

capnjess commented 5 years ago

Hi! Thanks for the good work in creating this package. I don't know if I am missing something with the 'extract-cycles' feature. I just get one set of values rather then values for each of the counted stress ranges. Can you assist with this?

capnjess commented 5 years ago

Hi! Thanks for the good work in creating this package. I don't know if I am missing something with the 'extract-cycles' feature. I just get one set of values rather then values for each of the counted stress ranges. Can you assist with this?

iamlikeme commented 5 years ago

Hi @capnjess, can you provide a code sample that illustrates the problem?

capnjess commented 5 years ago

Hi @iamlikeme , I used the following format that was given in the home page

for low, high, mult in rainflow.extract_cycles(y): ... mean = 0.5 * (high + low) ... rng = high - low

The only difference is my dataset (y) has about 5 million values. When I used the "rainflow.count cycles" feature I got a table of ranges and count, my issue is my mean stresses are not equal to zero so I have to use Goodman's correction on them. To do this I need more information from the counting such as Peaks and valleys which can be used to compute alternating stress and mean stress for each count. I assumed by using the extract cycles feature I could get a similar list to what I obtained with the countcyles feature but with mean and range(rng) for each processed entry.

Thanks for our help

iamlikeme commented 5 years ago

The extract_cycles indeed does not count cycles, like count_cycles does, but instead iterates over your series and yields cycles one by one. You also get high and low for each cycle so with this information you can customize your cycle count as you need.

Based on your description you need the cycle count for each mean and range pair, so you can do something along these lines:

from collections import defaultdict

counts = defaultdict(float)
for low, high, mult in rainflow.extract_cycles(y):
    mean = 0.5 * (high + low)  # here you can also apply rounding or binning as needed
    rng = high - low  # apply rounding or binning as needed
    counts[(mean, rng)] += mult
capnjess commented 5 years ago

The extract_cycles indeed does not count cycles, like count_cycles does, but instead iterates over your series and yields cycles one by one. You also get high and low for each cycle so with this information you can customize your cycle count as you need.

Based on your description you need the cycle count for each mean and range pair, so you can do something along these lines:

from collections import defaultdict

counts = defaultdict(float)
for low, high, mult in rainflow.extract_cycles(y):
    mean = 0.5 * (high + low)  # here you can also apply rounding or binning as needed
    rng = high - low  # apply rounding or binning as needed
    counts[(mean, rng)] += mult

Thanks this is exactly what I was looking for.

capnjess commented 5 years ago

The extract_cycles indeed does not count cycles, like count_cycles does, but instead iterates over your series and yields cycles one by one. You also get high and low for each cycle so with this information you can customize your cycle count as you need.

Based on your description you need the cycle count for each mean and range pair, so you can do something along these lines:

from collections import defaultdict

counts = defaultdict(float)
for low, high, mult in rainflow.extract_cycles(y):
    mean = 0.5 * (high + low)  # here you can also apply rounding or binning as needed
    rng = high - low  # apply rounding or binning as needed
    counts[(mean, rng)] += mult

Hi again, I have tried extracting the defaultdict(counts) created as a pandas dataframe with the following code

df = pd.DataFrame([(k, v[0][0], v[0][1]) for k, v in counts.items()], columns=['Mean', 'Range', 'Cycles'])

However I get this error "float' object is not subscriptable" . I am trying to have the counts dataframe in a format that has separate columns for mean, range, and cycles.

Thanks for your help

capnjess commented 5 years ago

Hi again, I have tried extracting the defaultdict(counts) created as a pandas dataframe with the following code

df = pd.DataFrame([(k, v[0][0], v[0][1]) for k, v in counts.items()], columns=['Mean', 'Range', 'Cycles'])

However I get this error "float' object is not subscriptable" . I am trying to have the counts dataframe in a format that has separate columns for mean, range, and cycles.

Thanks for your help

iamlikeme commented 5 years ago

The "'float' object is not subscriptable" error is caused by v[0], because v is not a sequence but a float (it's the number of cycles). Try this:

df = pd.DataFrame([(k[0], k[1], v) for k, v in count.items()], columns=['Mean', 'Range', 'Cycles'])

Your question is related to general Python, not rainflow in specific. I think StackOverflow is probably a better forum.

capnjess commented 5 years ago

The "'float' object is not subscriptable" error is caused by v[0], because v is not a sequence but a float (it's the number of cycles). Try this:

df = pd.DataFrame([(k[0], k[1], v) for k, v in count.items()], columns=['Mean', 'Range', 'Cycles'])

Your question is related to general Python, not rainflow in specific. I think StackOverflow is probably a better forum.

Hi! This worked perfectly thanks again. I have another question regarding the extract cycle feature. I know you stated initially that the extract cycle feature does not return the rainflow counts for the series like the "rainflow.count_cycles" feature. For example when I used the "count_cycles" feature I got a series with 178 values and their respective counts. When I used the "extract cycles" feature for the same series I got a series of 1780 values with their respective counts. I know you mentioned that I can apply binning or rounding to as required. My question is this when I used the "count_cycles" feature I did not apply any binning apart from using the arguments left =TRUE and right =True , I also used these arguments in the "extract_cycles" feature. I checked the source code to check if there was any default binning applied to the count_cycles feature but I did not find any, so I am a little surprised by my extracted cycles being 10 times greater than the counted cycles.

I am new to python so forgive my ignorance, I thought by using the extract cycle feature I will get the same count of 178 values as with the count_cycles feature but with more information for each counted cycle value in this case ( Mean , Range ).

Thanks again for providing this platform and for your assistance.

Capture

iamlikeme commented 5 years ago

178 is not the number of cycles but the number of distinct ranges. Sum the actual counts returned by count_cycles (sum(count for rng, count in count_cycles(y))) and you will probably get something around 1780/2.

extract_cycles does not round/bin the ranges but you can easily do it yourself, e.g.:

for low, high, mult in rainflow.extract_cycles(y):
    rng = round(high - low, -2)
capnjess commented 5 years ago

178 is not the number of cycles but the number of distinct ranges. Sum the actual counts returned by count_cycles (sum(count for rng, count in count_cycles(y))) and you will probably get something around 1780/2.

extract_cycles does not round/bin the ranges but you can easily do it yourself, e.g.:

for low, high, mult in rainflow.extract_cycles(y):
    rng = round(high - low, -2)

Hi! Thanks for your reply. I understand what you were saying, I meant earlier that when I used the count_cycles feature I got 178 distinct 'ranges' which had their respective 'cycle counts' meanwhile when i used the extract_cycles feature I got 1780 (ranges and mean pairs) with their respective 'cycle counts'. So I was wondering why the difference. I tried rounding but i still got the same results. Is there a way to tweak the count_cycles feature to get the 'mean' for each distinct range. Currently it only gives the range and cycle count

capnjess commented 5 years ago

178 is not the number of cycles but the number of distinct ranges. Sum the actual counts returned by count_cycles (sum(count for rng, count in count_cycles(y))) and you will probably get something around 1780/2.

extract_cycles does not round/bin the ranges but you can easily do it yourself, e.g.:

for low, high, mult in rainflow.extract_cycles(y):
    rng = round(high - low, -2)

Hi! Thanks for your reply. I understand what you were saying, I meant earlier that when I used the count_cycles feature I got 178 distinct 'ranges' which had their respective 'cycle counts' meanwhile when i used the extract_cycles feature I got 1780 (ranges and mean pairs) with their respective 'cycle counts'. So I was wondering why the difference. I tried rounding but i still got the same results. Is there a way to tweak the count_cycles feature to get the 'mean' for each distinct range. Currently it only gives the range and cycle count

iamlikeme commented 5 years ago

when I used the count_cycles feature I got 178 distinct 'ranges' which had their respective 'cycle counts' meanwhile when i used the extract_cycles feature I got 1780 (ranges and mean pairs). So I was wondering why the difference.

The difference is because you are comparing apples with pears. The number of distinct ranges is in general different than the number of distinct range and mean pairs. If you suspect that the result is incorrect please open a new issue and provide an example dataset which demonstrates the bug.

Is there a way to tweak the count_cycles feature to get the 'mean' for each distinct range. Currently it only gives the range and cycle count

count_cycles is a convenience function which addresses the most common case of counting cycle ranges, but it is simply using extract_cycles under the hood. extract_cycles is provided as a separate function to allow users to address their special cases (like, in your case, getting cycle mean in addition to range). So the answer to your question is no, you should use extract_cycles directly.

capnjess commented 5 years ago

Hi,

I was wondering if there is a way to have only full cycles returned after applying the count_cycles feature? This is necessary to apply Miner's rule where only full cycles can be used. Thanks for your help

ghost commented 5 years ago

Hi,

I was wondering if there is a way to have only full cycles returned after applying the count_cycles feature? This is necessary to apply Miner's rule where only full cycles can be used. Thanks for your help

Just round up or down the cycle counts using python ?

But you don't need to, since Miner's rule does not actually require full cycles. You can calculate damages and sum them together using non-integer cycle counts.

iamlikeme commented 5 years ago

Hi @capnjess, if you want to discard half-cycles you have to filter the output from extract_cycles and then count the cycles yourself. In the present implementation it is not possible to tell count_cycles to drop half-cycles.