reduce()を使用する便利なコード?[閉まっている]


123

誰かがPythonでreduce()関数を使用する便利なコードを持っていますか?例にある通常の+および*以外のコードはありますか?

GvRによるPython 3000のreduce()の運命を参照してください


1
from functools import reduce同じコードをPython 2と3の両方で動作させることができます
jfs

回答:


66

+と*以外に私が見つけた他の用途は、andとorでしたが、今ではanyandall、これらのケースを交換します。

foldl そして foldr、Schemeでたくさん登場します...

ここにいくつかのかわいい使用法があります:

リストをフラット化する

目標:ターン[[1, 2, 3], [4, 5], [6, 7, 8]][1, 2, 3, 4, 5, 6, 7, 8]

reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])

数字への数字のリスト

目標:ターン[1, 2, 3, 4, 5, 6, 7, 8]12345678

醜い、遅い方法:

int("".join(map(str, [1,2,3,4,5,6,7,8])))

かなりのreduce方法:

reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)

23
リストを平坦化するために、私はlist(itertools.chain(* nested_list))を好む
Roberto Bonvallet '28

13
sum([[1、2、3]、[4、5]、[
ゴードンリグリー

3
また、ビット単位の操作にも役立ちます。たとえば、フラグをリストからビットマスクに変換する必要がある場合など、ビット単位または一連の数値を取得する場合はどうなりますか?
アンチモン2010年

6
いくつかのベンチマークを行うと、「醜い」方法は大きなリストの方が速くなります。 timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)約0.09秒timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)かかりますが、0.36秒かかります(約4倍遅くなります)。基本的に、リストが大きくなると10による乗算は高価になりますが、strと連結のintは安価なままです。
ジンボブ博士、2013

3
もちろん、小さなリスト(サイズ10)の場合は、reduceメソッドの方が1.3倍高速です。ただし、この場合でも、reduceの回避と単純なループの実行はさらに速くなりtimeit.repeat('convert_digit_list_to_int(digit_list)', setup = 'digit_list = [d%10 for d in xrange(1,10)]\ndef convert_digit_list_to_int(digits):\n i = 0\n for d in digits:\n i = 10*i + d\n return i', number=100000)、0.06秒timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,10))', number=100000)かかり、0.12秒かかり、桁をstrメソッドに変換するには、0.16秒かかります。
ジンボブ博士、2013

51

reduce()3つ以上の数値の最小公倍数を見つけるために使用できます。

#!/usr/bin/env python
from fractions import gcd
from functools import reduce

def lcm(*args):
    return reduce(lambda a,b: a * b // gcd(a, b), args)

例:

>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560

1
lcm2行目は何ですか?
beardc、2012年

1
@BirdJaguarIV:回答のリンクをたどってくださいlcm()2つの数値の最小公倍数を返します。
jfs

39

reduce()ドットで区切られた名前を解決するために使用できます(使用するにeval()は危険すぎるため)。

>>> import __main__
>>> reduce(getattr, "os.path.abspath".split('.'), __main__)
<function abspath at 0x009AB530>


12

reduceはばかげたコマンドだと思います。したがって:

reduce(lambda hold,next:hold+chr(((ord(next.upper())-65)+13)%26+65),'znlorabggbbhfrshy','')

1
私はここの皮肉も好きです
ローマ

11

reduceコードで見つけた使用法には、論理式のクラス構造があり、これらの式オブジェクトのリストを式の結合に変換する必要がある状況が含まれていました。make_and2つの式を与えられた結合詞を作成する関数はすでに持っていたので、と書きましたreduce(make_and,l)。(私はリストが空ではないことを知っていました;そうでなければそれはのようなものだったでしょうreduce(make_and,l,make_true)。)

これがまさに、(一部の)関数型プログラマーが好むreduce(または、そのような関数が通常呼び出されるため、関数を折り畳む)理由です。以下のような、既に多くのバイナリの機能がしばしばあり+*minmax、連結と、私の場合では、make_andmake_or。持っているreduceするとこれらの操作をリスト(またはツリーや、一般にフォールド関数の場合は取得したもの)に引き上げることが簡単になります。

もちろん、特定のインスタンス化(などsum)が頻繁に使用される場合は、書き続けたくありませんreduce。ただし、sumforループを使用してを定義する代わりに、することができます同じように簡単でそれを定義しますreduce

他の人が言うように、読みやすさは確かに問題です。しかし、人々がreduce「明確でない」と感じる理由は、それが多くの人々が知っている、または使用している機能ではないためだと主張できます。


空のリストから保護するために、and演算子の短絡動作を悪用する可能性L and reduce(make_and, L)があります。この場合、空のリストを返すことが適切な場合
jfs

9

関数構成:次のように、続けて適用したい関数のリストがある場合:

color = lambda x: x.replace('brown', 'blue')
speed = lambda x: x.replace('quick', 'slow')
work = lambda x: x.replace('lazy', 'industrious')
fs = [str.lower, color, speed, work, str.title]

次に、次のようにしてすべてを連続して適用できます。

>>> call = lambda s, func: func(s)
>>> s = "The Quick Brown Fox Jumps Over the Lazy Dog"
>>> reduce(call, fs, s)
'The Slow Blue Fox Jumps Over The Industrious Dog'

この場合、メソッドチェーンの方が読みやすくなります。しかし、それができない場合もあり、この種の構成は、f1(f2(f3(f4(x))))一種の構文よりも読みやすく、保守しやすい場合があります。


1
利点は、コードに適用する関数のリストを変更できることです。
hakanc


7

@Blair Conrad:次のように、sumを使用してglob / reduceを実装することもできます。

files = sum([glob.glob(f) for f in args], [])

これは2つの例のどちらよりも冗長ではなく、完全にPythonであり、コードは1行だけです。

元の質問に答えるために、私は個人的には、reduceの使用を避けようとしています。なぜなら、reduceは本当に必要なことは決してなく、他のアプローチよりも明確ではないからです。しかし、一部の人々は、内包表記を一覧表示するよりも減らして好むようになります(特にHaskellプログラマ)。ただし、reduceの問題をまだ考えていない場合は、おそらくそれを使用することについて心配する必要はありません。


2
両方ともsumreduce2次動作につながります。これは線形時間で実行できますfiles = chain.from_iterable(imap(iglob, args))。この場合、glob()がディスクにアクセスするのに時間がかかるため、おそらく問題ではありません。
jfs

6

reduce 連鎖属性ルックアップをサポートするために使用できます:

reduce(getattr, ('request', 'user', 'email'), self)

もちろん、これは

self.request.user.email

ただし、コードが属性の任意のリストを受け入れる必要がある場合に役立ちます。

(Djangoモデルを扱う場合、任意の長さの連鎖属性が一般的です。)


4

reducesetようなオブジェクトのシーケンスの和集合または交差を見つける必要がある場合に役立ちます。

>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3}))  # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3}))  # intersection
{1}

(実際setのsは別として、これらの例はDjangoのQオブジェクトです。)

一方、bools を扱う場合は、anyand を使用する必要がありallます。

>>> any((True, False, True))
True


3

言語の作成関数を作成しているので、reduce演算子とapply演算子を使用して、作成した関数を作成します。

簡単に言えば、composeは関数のリストを取り、単一の関数に構成します。段階的に適用される複雑な操作がある場合は、次のようにまとめます。

complexop = compose(stage4, stage3, stage2, stage1)

このようにして、それを次のような式に適用できます。

complexop(expression)

そして、私はそれが以下と同等であることを望みます:

stage4(stage3(stage2(stage1(expression))))

ここで、内部オブジェクトを作成するために、次のように伝えます。

Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))

(Lambdaクラスはユーザー定義関数を作成し、Applyは関数アプリケーションを作成します。)

さて、残念ながら、フォールドが間違った方法で縮小されるので、大まかに使用してしまいました。

reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))

reduceが何を生成するかを理解するには、REPLで以下を試してください。

reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))

私は、パフォーマンステストのために関数の可能なすべての組み合わせcompose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg)生成する
jfs

3

reduceを使用して、最大のn番目の要素を持つリストを取得できます

reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])

それは最大の3番目の要素+を持つリストであるため、[5、2、5、7]を返します


max(lst、key = lambda x:x [2])
aoeu256

3

Reduceはスカラー演算に限定されません。物をバケットに分類するためにも使用できます。(これは、私が頻繁に使用するものです)。

オブジェクトのリストがあり、オブジェクトにフラットに格納されているプロパティに基づいて階層的に再編成したいとします。次の例では、articles関数を使用して、XMLエンコードされた新聞の記事に関連するメタデータオブジェクトのリストを生成します。articlesXML要素のリストを生成し、それらを1つずつマッピングして、それらに関する興味深い情報を保持するオブジェクトを生成します。フロントエンドでは、ユーザーがセクション/サブセクション/ヘッドラインで記事を閲覧できるようにしたいと思います。したがってreduce、記事のリストを取得して、セクション/サブセクション/記事の階層を反映する単一の辞書を返すために使用します。

from lxml import etree
from Reader import Reader

class IssueReader(Reader):
    def articles(self):
        arts = self.q('//div3')  # inherited ... runs an xpath query against the issue
        subsection = etree.XPath('./ancestor::div2/@type')
        section = etree.XPath('./ancestor::div1/@type')
        header_text = etree.XPath('./head//text()')
        return map(lambda art: {
            'text_id': self.id,
            'path': self.getpath(art)[0],
            'subsection': (subsection(art)[0] or '[none]'),
            'section': (section(art)[0] or '[none]'),
            'headline': (''.join(header_text(art)) or '[none]')
        }, arts)

    def by_section(self):
        arts = self.articles()

        def extract(acc, art):  # acc for accumulator
            section = acc.get(art['section'], False)
            if section:
                subsection = acc.get(art['subsection'], False)
                if subsection:
                    subsection.append(art)
                else:
                    section[art['subsection']] = [art]
            else:
                acc[art['section']] = {art['subsection']: [art]}
            return acc

        return reduce(extract, arts, {})

オブジェクトを処理するときにmapとreduceが互いにうまく補完できることを示していると思うので、ここで両方の関数を指定します。同じことはforループでも実現できたかもしれませんが、関数型言語で真剣な時間を費やすと、マップの観点から考えて還元する傾向があります。

ちなみに、誰かが私がやっているようにプロパティを設定するより良い方法がある場合 extractたいプロパティの親がまだ存在しない可能性がありますので、お知らせください。


3

これがあなたの目的であるかどうかはわかりませんが、Googleでソースコードを検索できます

「function:reduce()lang:python」を検索するには、リンクをクリックしてくださいGoogle Code検索を

一見、以下のプロジェクトが使用します reduce()

  • モインモイン
  • Zope
  • 数値
  • ScientificPython

などなどですが、巨大なプロジェクトであるため、これらは驚くに値しません。

reduceの機能は、関数の再帰を使用して実行できます。これは、Guidoがより明示的であると私が思ったと思います。

更新:

Googleのコード検索は2012年1月15日に廃止されたため、通常のGoogle検索に戻る以外に、有望に見えるコードスニペットコレクションがあります。他の多くのリソースが、この(終了した)質問の回答で言及されています

アップデート2(2017年5月29日):

Pythonの例(オープンソースコード内)の優れたソースは、Nullege検索エンジンです。


1
「reduceの機能は、関数の再帰を使用して実行できます」...またはforループ。
Jason Orendorff、

2
また、reduce()を検索すると、コード内にreduce関数を定義するプロジェクトが生成されます。あなたはLANGを検索する必要があります:pythonは、ビルトイン関数の実際の使用法を見つけることに「(削減」。
Seun Osewa

@Seun Osewa:検索しても、ソースコードのコーディングスタイルlang:python "reduce("reduce応じた定義が見つかります。
martineau

2
import os

files = [
    # full filenames
    "var/log/apache/errors.log",
    "home/kane/images/avatars/crusader.png",
    "home/jane/documents/diary.txt",
    "home/kane/images/selfie.jpg",
    "var/log/abc.txt",
    "home/kane/.vimrc",
    "home/kane/images/avatars/paladin.png",
]

# unfolding of plain filiname list to file-tree
fs_tree = ({}, # dict of folders
           []) # list of files
for full_name in files:
    path, fn = os.path.split(full_name)
    reduce(
        # this fucction walks deep into path
        # and creates placeholders for subfolders
        lambda d, k: d[0].setdefault(k,         # walk deep
                                     ({}, [])), # or create subfolder storage
        path.split(os.path.sep),
        fs_tree
    )[1].append(fn)

print fs_tree
#({'home': (
#    {'jane': (
#        {'documents': (
#           {},
#           ['diary.txt']
#        )},
#        []
#    ),
#    'kane': (
#       {'images': (
#          {'avatars': (
#             {},
#             ['crusader.png',
#             'paladin.png']
#          )},
#          ['selfie.jpg']
#       )},
#       ['.vimrc']
#    )},
#    []
#  ),
#  'var': (
#     {'log': (
#         {'apache': (
#            {},
#            ['errors.log']
#         )},
#         ['abc.txt']
#     )},
#     [])
#},
#[])

1
ここで何が起こっているのかについて少し説明を追加していただけますか?そうでなければ、その有用性はまったく明らかではありません。
Zoran Pavlovic 2014

2
def dump(fname,iterable):
  with open(fname,'w') as f:
    reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)


1

私は、reduceとglobモジュールを使用して処理するファイルのリストを作成するpipegrepの古いPython実装を持っています。

files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))

当時は便利だと思いましたが、似たようなものが同じくらいよく、おそらくもっと読みやすいので、それは本当に必要ではありません

files = []
for f in args:
    files.extend(glob.glob(f))

リストの理解はどうですか?これはそのための完璧なアプリケーションのようです: files = [glob.glob(f) for f in args]
steveha

実際、@ stevehaの例では、グロブに一致するすべてのアイテムのフラットリストではなく、展開されたグロブのリストのリストになりますが、@ [Eli Courtwright](#16198 ) 指摘している。
ブレアコンラッド

1
さて、あなたは正しいです、それについて申し訳ありません。それでも、extend / reduce / lambda / mapの組み合わせがあまり好きではありません!インポートしitertoolsdocs.python.org/library/itertools.htmlflatten()レシピを使用して、次のように記述することをお勧めします (今回は、コードを投稿する前にテストしましたが、これは正しく動作することがわかっています。)files = flatten(glob.glob(f) for f in args)
steveha

files = chain.from_iterable(imap(iglob, args))ここでchainimapitertoolsモジュールglob.iglobからのものでargsあり、パターンが複数のディレクトリからファイルを生成する場合に役立ちます。
jfs

1

カウンタのリストが保存されている年間統計データがあるとします。異なる年の各月のMIN / MAX値を検索します。たとえば、1月の場合は10、2月の場合は15になります。新しいカウンターに結果を保存する必要があります。

from collections import Counter

stat2011 = Counter({"January": 12, "February": 20, "March": 50, "April": 70, "May": 15,
           "June": 35, "July": 30, "August": 15, "September": 20, "October": 60,
           "November": 13, "December": 50})

stat2012 = Counter({"January": 36, "February": 15, "March": 50, "April": 10, "May": 90,
           "June": 25, "July": 35, "August": 15, "September": 20, "October": 30,
           "November": 10, "December": 25})

stat2013 = Counter({"January": 10, "February": 60, "March": 90, "April": 10, "May": 80,
           "June": 50, "July": 30, "August": 15, "September": 20, "October": 75,
           "November": 60, "December": 15})

stat_list = [stat2011, stat2012, stat2013]

print reduce(lambda x, y: x & y, stat_list)     # MIN
print reduce(lambda x, y: x | y, stat_list)     # MAX

1

ある種の重なり合う間隔(ゲノムエクソン)を表すオブジェクトがあり、それらを使用して交差を再定義しました__and__

class Exon:
    def __init__(self):
        ...
    def __and__(self,other):
        ...
        length = self.length + other.length  # (e.g.)
        return self.__class__(...length,...)

次に、それらのコレクション(たとえば、同じ遺伝子内)がある場合は、

intersection = reduce(lambda x,y: x&y, exons)

1

私はただの便利な使い方見つかっreduce区切り文字を削除せずに分割文字列をコードは完全にプログラム的に話すブログからです。これがコードです:

reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])

結果は次のとおりです。

['a\n', 'b\n', 'c\n', '']

SOでの一般的な回答では対応できないエッジケースを処理することに注意してください。より詳しい説明については、元のブログ投稿にリダイレクトします。


0

reduce()を使用して、日付のリストが連続しているかどうかを確認します。

from datetime import date, timedelta


def checked(d1, d2):
    """
    We assume the date list is sorted.
    If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
    can advance to the next reduction.
    If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
    will guarantee the result produced by reduce() to be something other than
    the last date in the sorted date list.

    Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
    Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive

    """
    #if (d2 - d1).days == 1 or (d2 - d1).days == 0:  # for Definition 1
    if (d2 - d1).days == 1:                          # for Definition 2
        return d2
    else:
        return d1 + timedelta(days=-1)

# datelist = [date(2014, 1, 1), date(2014, 1, 3),
#             date(2013, 12, 31), date(2013, 12, 30)]

# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),
#             date(2014, 2, 21), date(2014, 2, 22)]

datelist = [date(2014, 2, 19), date(2014, 2, 21),
            date(2014, 2, 22), date(2014, 2, 20)]

datelist.sort()

if datelist[-1] == reduce(checked, datelist):
    print "dates are consecutive"
else:
    print "dates are not consecutive"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.