質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • Python 3.x

    8901questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • pandas

    798questions

    Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

波形のpeak値検出プログラムを作りたい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 83

H.Ka2

score 18

前提・実現したいこと

python3で、波形のピーク値検出のメソッドを作成しようと思っています。
inputは、pandasのseriesで、ロジックは、離散値の2点を順番に読み込み、それらのPeakを更新していくプログラムとなります。
イメージとしては下記となります。
イメージ説明

発生している問題・エラーメッセージ

一通りロジックを作成したのですが、下記エラーが出てきて先に進めなくなってしまいました。
ネットで検索してみたのですが、解決方法がよくわかりませんでした。。。

---> 19         if (wav.iloc[i] * wav.iloc[i+1] < 0):
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

解決したいこと

①エラーコードが出ている原因が知りたい(下記コードがなぜNGか)
②他にプログラム上、まずいところがあればご教示いただきたい

該当のソースコード

ソースコードは下記となります。

def f_A95pp(wav=None):
    # peakのリスト
    peaks = pd.Series([0])

    # 各サンプルごとのpeak(最大の絶対値)のみを格納していく。
    for i, _ in enumerate(wav):
        if i == len(wav)-1:
            break

        print(i, wav.iloc[i], wav.iloc[i+1], type(wav.iloc[i]), type(wav.iloc[i+1]))
        # i,i+1サンプルの積が負の場合は次のpeakの入れ物を作る。
        if (wav.iloc[i] * wav.iloc[i+1] < 0):
            print("***i:{}, i+1:{}***".format(wav.iloc[i], wav.iloc[i+1]))
            peaks.append(wav.iloc[i:i+1].copy().abs())
            print("peaks_len:{}***".format(len(peaks)))
        # peaksの末尾のデータを越えた値がくる場合は、peaksの末尾を更新
        elif(abs(peaks.iloc[-1]) < abs(wav.iloc[i])):
            peaks.iloc[-1] = wav.iloc[i].copy()

        print("peaks:",peaks)

    # peaksが2個以下(1周期着てない場合)は、Noneを代入する。
    if(len(peaks) < 2):
        peaks = None

    # peak_max(peakの最大値をとる。)
#    print(peaks)
    dif_peak = peaks.iloc[0:-1].copy()-peaks.iloc[1:].copy()
    peak_max = dif_peak.max()

    # p2p:peaksのリスト(indexが,時間tとなっている)
    p2p = dif_peak.abs()
    print("temp:",temp)
    p2p.sort_index(ascending=False)
#    temp.sort(key=lambda x: x[1], reverse=True)  #

    # PA95(peaksの上位5%を除いた値のリストの中の最大値を取りたい)
    temp_a95pp = p2p[round(len(ptp)*0.95+0.5):]
    print("temp_a95pp:",temp_a95pp)

    if temp_a95pp is None:
        a95pp = 0
    else:
        a95pp = max(temp_a95pp)

    return (peak_max, a95pp, ptp)

試したこと

エラーコードについて検索した。
テストコードを描いて試してみた。

補足情報(FW/ツールのバージョンなど)

Python3.6

ご回答いただけましたら幸甚に存じます。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

ザッと見てみましたが、かなり問題が・・・
順番に
【1】
hayataka2049さんが既に指摘しておりますが、
peaks.append(wav.iloc[i:i+1].copy().abs())
の箇所は
peaks = peaks.append(wav.iloc[i:i+1].copy().abs())
ですね
【2】
dif_peak = peaks.iloc[0:-1].copy()-peaks.iloc[1:].copy()
の箇所ですが、Series同士の引き算では同じIndexでの引き算となるので、結果全ての値が0となってしまいます
やるならば
dif_peak = peaks.diff(-1)[:-1]
で良いかと思います。
【3】
peak_max = dif_peak.max()
の箇所ですが、負の値の無視されますがこれは問題ないのでしょうか?(仕様なのかもしれませんが)
【4】
p2p.sort_index(ascending=False)
の箇所は 【1】と同じく
p2p = p2p.sort_index(ascending=False)
でしょうね。
【5】
上と同じ箇所で
p2p.sort_index(ascending=False)
ですが、ソートは Indexで行うのではなくValueで行うのでは?と思うのですが違いますかね?
であれば
p2p = p2p.sort_values(ascending=False)
となります。
【6】
途中でいきなり出現するtemp って何でしょう???
更には ptpも不明です。これはp2pの間違いなのでしょうか?
【7】
temp_a95pp = p2p[round(len(ptp)*0.95+0.5):]
の箇所ですが、p2pは降順に(多分)並んでおりますので、上方から95%以降のデータを抜き出すと下位5%のデータだけが残りませんかね???
ここは
temp_a95pp = p2p[round(len(p2p)*0.05+0.5):]
が正解なのではないでしょうか
【8】
あとは全体的にかなり冗長な気がします。
Pandasを使うのであればPandasっぽい記述ってのがありますので是非覚えてください。(【2】で挙げたdiff()など)
例えば最初の peaksを求める箇所もループを使わずに groupby()を使ってかけば

peaks = wav[wav.groupby((wav*wav.shift(1)<0).cumsum()).apply(lambda d: d.abs().idxmax())]


の一行(わかりやすいように分割しても2・3行)で収まるコード量で記述できます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/19 23:09

    お返事遅くなりました。。すみません。
    ありがとうございます。pandasの使い方とかもっと勉強しないとですね…。色々調べてみます。

    キャンセル

+2

wavはほんとうにSeriesですか? うっかりDataFrameで渡しているというのが一番ありそうなケースです。

そうだった場合、(wav.iloc[i] * wav.iloc[i+1] < 0)で、wav.iloc[i]wav.iloc[i+1]Series型になり、Series同士の掛け算もまたSeriesを返します。ベクトルの要素同士の演算のようなものです。そして比較演算を行うとboolSeriesになます。if文は条件式を単一のboolに変換しようとしますが、boolSeriesはこの変換ができないことになっています(「一意に決まらない仕様」が自然であるため。それを踏まえた上でメッセージを読んでみてください)。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/14 23:58

    print(i, wav.iloc[i], wav.iloc[i+1], type(wav.iloc[i]), type(wav.iloc[i+1]))
    しているようですが、
    数字、数字、数字、intやfloatなどに類する型、intやfloatなどに類する型
    という感じで出てこなければ引数が間違っています。

    キャンセル

  • 2019/03/15 00:21

    すみません。失礼しました・・・。ご指摘の通りdataframeとなってました…。
    seriesに変換したら、上記エラーコードはとることができました。ありがとうございます。

    ただ、別の問題が発生しており、今悩んでおります。(下記のエラーコードが出ます)
    34 # print(peaks)
    ---> 35 dif_peak = peaks.iloc[0:-1].copy()-peaks.iloc[1:].copy()
    36 peak_max = dif_peak.max()
    AttributeError: 'NoneType' object has no attribute 'iloc'

    プログラムを追ってみたところ、どうも、peaksが1つしか検出されず、2個以下となっているため、
    Noneが代入されてしまっているようです・・・。。
    入力波形としては、ただのsin波を数十周期分入れているだけなのですが、forが一通り回っても
    peakが増えない状況となっています。。。

    キャンセル

  • 2019/03/15 00:35

    ロジックの正しさを判断するにはコードをちゃんと読まないといけないのですが、ちょっとしんどいのでざっくり見て思うことを書きます。
    まず、
    print("***i:{}, i+1:{}***".format(wav.iloc[i], wav.iloc[i+1]))
    peaks.append(wav.iloc[i:i+1].copy().abs())
    print("peaks_len:{}***".format(len(peaks)))
    あたりのprintは何回表示されますか? 一応ここの回数個分だけ追加されるという意図だと思うので、まずはそれを確認してください。

    キャンセル

  • 2019/03/15 00:37

    peaks.append(wav.iloc[i:i+1].copy().abs())
    は意図通り動かないと思います。Seriesのappendはin-place処理ではありません。やるとしたらpeaks=のように再代入すればよいのですが、そんな面倒で遅いことをするなら最初からリストを使うといいかと。

    キャンセル

  • 2019/03/15 00:39

    正直に言ってしまうと、pandasでごちゃごちゃやるより、numpy配列でデータを受け取り、可変長のコレクションが必要ならlistを使う実装にした方が余計な手間がかからないし速度的にもいいと思います。

    キャンセル

  • 2019/03/15 01:01

    ご回答ありがとうございます。
    上記のprintは、回数個数分だけ回っていることを確認しています。
    >peaks.append(wav.iloc[i:i+1].copy().abs())
    >は意図通り動かないと思います。Seriesのappendはin-place処理ではありません。
    >やるとしたらpeaks=のように再代入すればよいのですが、
    >そんな面倒で遅いことをするなら最初からリストを使うといいかと。
    なるほど。。listか、numpyで実装したほうがよさそうですか…。検討してみます。
    ありがとうございます。。。

    キャンセル

  • 2019/03/15 01:05

    peaks = peaks.append(wav.iloc[i:i+1].copy().abs())

    こんな風に書けば確かにそこは動くようになると思うんですが(他にエラーがあるかどうかは知らない)、なんていうかpandasで書くのが苦行だろうし、慣れていればともかく不慣れだとエラー箇所を増やすだけだろうというのがコードを見ていて想像できるので・・・

    キャンセル

  • 2019/03/15 01:31

    ありがとうございます。確かに、ほかにもエラーを生み出しそうですね…。
    とはいえ入力および出力はseriesで変えられなさそうなので(諸般の事情で)、、、
    (中で変換して、最期にまたseriesに変換ですかね…。
    ともあれ、ありがとうございます。

    キャンセル

同じタグがついた質問を見る

  • Python 3.x

    8901questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • pandas

    798questions

    Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。