69193
# 実現したいこと

以下のように、文中の単語で強調表現などで使われている全て大文字（以下の例・BLUE）の単語は小文字に変換し、固有名詞や文頭の語だけ除外しようとしています。

IDはできればそのまま大文字にしたいのですが、固有名詞や所有格（人物名＋'）ではなく、判別がむずかしい場合は小文字にしてしまおうと思っています。

Python 3.7.4でJupyter notebookでコードを実行しています。

```

    #入力
    "Kate forgot John's login ID.",
    "Her BLUE skirt looks good."
    
    #出力
    "Kate forgot John's login id.",
    "Her blue skirt looks good."
    
```

# 問題

`str.capitalize()`: 先頭の一文字を大文字、他を小文字に変換  
というメソッドがあったので使ってみたのですが、先頭以外の固有名詞などは判別できないので、どのようにしたら、強調表現などの全て大文字の単語のみ小文字に変換できるでしょうか。

自然言語処理のライブラリnltkやspaCyなども調べて見てみたのですが、方法がわからない状態です。

```

    ["Kate forgot john's login id.", 'Her blue skirt looks good.']
    
```

# 現在のコード

```

    for i in range(len(samples)):
        text = samples[i]
        samples[i] = text.capitalize()
    samples
    
```

# ご回答を受けて伺いたいこと

固有名詞がすべて大文字だったときも、小文字にするのを避けたいのですが、現在のコードだと小文字になってしまいます。

この点に関して補足をいただけると大変助かります。

```

    import nltk
    
    sentences = """KATE forgot JOHN's login ID.
    Her BLUE skirt looks good.
    """
    
    def replace_if_all_uppercase(word):
        # もし単語のすべての文字が大文字ならば、すべて小文字に変換
        if word[1] is not 'NNP':
            if all(map(lambda w: w.isupper(), word[0].split())):
                return word[0].lower()
        
            return word[0]
        
    
    for sentence in sentences.splitlines():
        # nltkを使って分かち書き＆品詞の取得
        tokens = nltk.word_tokenize(sentence)
        tagged = nltk.pos_tag(tokens)
        print(tagged)
        print(" -> ")
        conved = list(map(lambda s: replace_if_all_uppercase(s), tagged))
        print(" ".join(conved))
    
```

```

    [('KATE', 'NNP'), ('forgot', 'VBD'), ('JOHN', 'NNP'), ("'s", 'POS'), ('login', 'NN'), ('ID', 'NNP'), ('.', '.')]
     -> 
    kate forgot john 's login id .
    [('Her', 'PRP$'), ('BLUE', 'NNP'), ('skirt', 'NN'), ('looks', 'VBZ'), ('good', 'JJ'), ('.', '.')]
     -> 
    Her blue skirt looks good .
    
```
CC BY-SA 4.0
2020-08-02T15:25:47.303
69188
2020-08-18T07:44:50.387
2020-08-18T07:17:37.607
32568
32568
question
python python3 自然言語処理
文中の単語を大文字から小文字に変換する時に、固有名詞や文頭の語だけ除外する方法について
全部大文字の表現を小文字にしたいならば正規表現を使うことで柔軟に対応できます。  
ただし下記のサンプルコードでは"I"が大文字判定され、数字を含むものや"MX."などの敬称が大文字だとちぐはぐな結果となりますので、条件や正規表現を工夫する必要があるかもしれません。

**サンプルコード:**

```

    import re
    
    samples = ["Kate forgot John's login ID.",
               "HEAD And TAIL",
               "OOPS, I DON'T know WHAT to do this. -- ID42",
               "He is MX.BONES."]
    
    p = re.compile(r"(?:^|\W)([A-Z']+)(?:\W|$)")
    for sample in samples:
        for m in p.finditer(sample):
            start = m.start(1)
            lower = m.group(1).lower()
            sample = sample[:start] + lower + sample[start + len(lower):]
        print(sample)
    
```

**出力結果:**

```

    Kate forgot John's login id.
    head And tail
    oops, i DON't know what to do this. -- ID42
    He is mx.BONES.
    
```
CC BY-SA 4.0
2020-08-03T00:14:27.750
69192
2020-08-03T00:14:27.750
9820
69188
answer
nltkを使って形態素解析しつつ変換する例です。

```

    import nltk
    
    sentences = """Kate forgot John's login ID.
    Her BLUE skirt looks good.
    """
    
    def replace_if_all_uppercase(word):
        # もし単語のすべての文字が大文字ならば、すべて小文字に変換
        # ただしそれが固有名詞のときは除く
        if all(map(lambda w: w.isupper(), word[0].split())) and word[1] != "NNP":
            return word[0].lower()
        
        return word[0]
        
    
    for sentence in sentences.splitlines():
        # nltkを使って分かち書き＆品詞の取得
        tokens = nltk.word_tokenize(sentence)
        tagged = nltk.pos_tag(tokens)
        print(tagged)
        print(" -> ")
        conved = list(map(lambda s: replace_if_all_uppercase(s), tagged))
        print(" ".join(conved))
    
```

出力

```

    [('Kate', 'NNP'), ('forgot', 'VBD'), ('John', 'NNP'), ("'s", 'POS'), ('login', 'NN'), ('ID', 'NNP'), ('.', '.')]
     -> 
    Kate forgot John 's login ID .
    [('Her', 'PRP$'), ('BLUE', 'NNP'), ('skirt', 'NN'), ('looks', 'VBZ'), ('good', 'JJ'), ('.', '.')]
     -> 
    Her BLUE skirt looks good .
    
```

> 文中の単語で強調表現などで使われている全て大文字（以下の例・BLUE）の単語は小文字に変換し、固有名詞や文頭の語だけ除外しようとしています。

固有名詞は`NNP`で判別されます。状況に応じて使うといいと思います。  
例えば全て大文字で固有名詞ならば変換しないなど。

**追記**

> 固有名詞がすべて大文字だったときも、小文字にするのを避けたい

単純に制御構文にandを追加すればいいような気がします。追記しました。

参考

  * [NLTKの使い方をいろいろ調べてみた](https://qiita.com/m__k/items/ffd3b7774f2fde1083fa)
CC BY-SA 4.0
2020-08-03T00:30:03.667
69193
2020-08-18T07:44:50.387
2020-08-18T07:44:50.387
4715
4715
69188
answer