Ana içeriğe geç

Python: Bir Listeden Yinelenenleri Kaldırma (Beş Çözüm)

Sıra korunurken bir Python listesindeki yinelenenleri kaldırmak için listeden bir sözlük oluşturun ve ardından anahtarlarını yeni bir liste olarak çıkarın: list(dict.fromkeys(my_list)).
· 9 dk. oku

Yapay Zeka ile Keşfedin

Bir Python list, yinelenen değerler içerebilen, sıralı bir dizidir. Bazı Python uygulamaları yalnızca benzersiz öğeler içeren bir liste gerektirebilir. Python'da bir listeden yinelenenleri kaldırmanın birkaç tekniği vardır.

Doğru çözüm, yinelenenler kaldırıldıktan sonra uygulamanın listenin öğelerinin sırasını korumasına ihtiyaç duyup duymamasına bağlıdır. Bu derste sunulan çözümler okunabilirlik ve karmaşıklık açısından da farklılık gösterir ve bazıları üçüncü taraf modüllerdeki işlevlere dayanır.

Yinelenen Değer Nedir?

Bir listeden kaldırma çözümlerini sunmadan önce yinelenen değerler ile ne kastettiğimizi netleştirmek faydalıdır. Genel olarak iki nesne, değerleri eşit olduğunda yinelenen kabul edilir. Python'da eşitlik işleci ==, iki nesnenin aynı değere sahip olması durumunda True döndürür. Farklı veri türlerindeki nesneler eşit olabilir:

print(10.0 == 10)
print(1 == True)
True
True

Kayan noktalı 10.0 ile tamsayı 10 eşittir ve tamsayı 1 ile Boolean True da eşittir. Çoğu senaryoda bu değerler yinelenen kabul edilir.

Bazı Python veri yapıları benzersiz değerleri zorunlu kılar. Bir set, yalnızca benzersiz öğeler içeren bir koleksiyondur:

print({10, 10.0})
print({10.0, 10})
print({1, True, 1.0})
{10}
{10.0}
{1}

Bu kod, {} süslü parantez gösterimini kullanarak üç küme oluşturur. Her küme, birbirine eşit nesneler içerir. Bir kümeye farklı veri türlerinde eşit nesneler eklenirse yalnızca ilk nesne dahil edilir.

Python ayrıca dictionary anahtarlarının benzersiz olmasını gerektirir:

print({10: "Integer 10", 10.0: "Float 10"})
print({True: "Boolean True", 1: "Integer 1"})
{10: 'Float 10'}
{True: 'Integer 1'}

Bu koddaki sözlükler eşit anahtarlar içerir. Yalnızca ilk anahtar korunur. Ancak, bu anahtarla ilişkili değer, sözlüğe eklenen son değerle değiştirilir. Bu nedenle ilk örnekte korunan anahtar tamsayı 10'dur ancak değeri "Float 10" dizesidir. İkinci örnekte 1 ile eşit olan True anahtarı, "Integer 1" değeriyle ilişkilendirilmiştir.

Kümeler ve sözlükler, benzersiz öğe veya anahtar gereksinimleri nedeniyle bu dersteki çözümlerin birçoğunda rol oynar.

Bir Kümeyle Listeden Yinelenenleri Kaldırma

Bir listeden yinelenenleri kaldırmanın en basit tekniği listeyi kümeye dönüştürmek ve tekrar listeye çevirmektir. Bir küme yalnızca benzersiz değerler içerebilir. Bu nedenle, küme oluşturulurken yinelenenler elenir:

names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
print(list(set(names)))
['Mark', 'Bob', 'Kate', 'James', 'Sarah']

Bu tekniği kullanarak yinelenenleri kaldırmak için gereken adımlara bakalım:

  1. Liste bir kümeye dönüştürülür ve tüm yinelenenler kaldırılır
  2. Küme tekrar yeni bir listeye dönüştürülür ve bu liste yalnızca benzersiz değerler içerir

James ve Kate özgün listede iki kez görünür, ancak son listede yalnızca bir kez yer alır.

Ancak, kümeler sırasız koleksiyonlardır. Bir kümedeki öğelerin sırası korunmaz. Son listedeki adların, özgün listedeki sırayla aynı olmadığını fark edin. Aynı kod, bir kümedeki öğelerin sırasına dair bir garanti olmadığından, yeniden çalıştırıldığında veya farklı bir Python yorumlayıcısı kullanıldığında farklı bir sıra üretebilir.

Bu çözüm, listedeki öğelerin sırasına ilişkin bilginin kaybının kabul edilebilir olduğu durumlar için idealdir. Ancak, öğelerin sırası korunmalıysa diğer çözümlere ihtiyaç duyarız.

Listeden Yinelenenleri Kaldırıp Sırayı Korumak

Sözlük anahtarları, benzersiz olmaları gerektiği için kümelere benzer. Bir sözlükte yinelenen değerler olabilir ancak yinelenen anahtarlar olamaz.

Python 3.6'dan önce sözlükler öğelerinin sırasını korumazdı. Ancak Python 3.6'da sözlüklerin uygulamasında yapılan değişikliklerin bir yan etkisi olarak, sözlükler artık öğeler eklenme sırasını korur. Bu, Python 3.6'da yalnızca bir uygulama detayıydı ancak Python 3.7'de dilin resmi bir parçası haline geldi.

dict.fromkeys() işlevi, yinelenebilir bir nesneden bir sözlük oluşturur. Yinelenebilirdeki öğeler yeni sözlüğün anahtarları olur:

names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
print(dict.fromkeys(names))
{'James': None, 'Bob': None, 'Mark': None, 'Kate': None, 'Sarah': None}

Bir sözlükte anahtarlar benzersiz olduğundan, sözlük oluşturulurken yinelenen değerler elenir. Python 3.7 veya sonrasında kullanılıyorsa anahtarların sırasının listede yer aldıkları sırayla aynı olması garanti edilir. Tüm anahtarlar varsayılan olarak None değerine sahiptir. Ancak bu değerlere ihtiyaç yoktur; çünkü bu sözlük tekrar listeye dönüştürülebilir:

names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
print(list(dict.fromkeys(names)))
['James', 'Bob', 'Mark', 'Kate', 'Sarah']

Bir sözlük listeye dönüştürüldüğünde yalnızca anahtarlar kullanılır. Bu teknik, özgün listeyi yinelenenler olmadan ve öğelerin özgün sırası korunarak yeni bir listeye dönüştürür.

Listeden Yinelenenleri Kaldırmanın Diğer Seçenekleri

Bu dersin ilk bölümünde sunulan seçenekler, yinelenenlerin listeden kaldırılması gereken iki durumu kapsar:

  1. Öğelerin sırasına ihtiyaç olmadığında bir küme kullanın.
  2. Öğelerin sırasını korumak için dict.fromkeys() kullanın.

Bu iki seçenek, çoğu durumda muhtemelen en iyi çözümlerdir. Ancak, bir listeden yinelenenleri kaldırmanın başka teknikleri de vardır.

Yeni bir listeyi döngüyle doldurma

Doğrudan bir seçenek, özgün liste üzerinde yineleyip yeni öğeleri yeni bir listeye eklemektir:

names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
unique_names = []
for name in names:
    if name not in unique_names:
        unique_names.append(name)
print(unique_names)
['James', 'Bob', 'Mark', 'Kate', 'Sarah']

Yeni unique_names listesi, for döngüsünden önce boş bir liste olarak başlatılır. Bir öğe, yeni listede zaten yoksa bu listeye eklenir. Bu sürüm, for döngüsü listede sırayla dolaştığı için özgün listedeki öğelerin sırasını korur.

Bu çözüm büyük listeler için verimsiz olabilir. Rastgele sayılardan oluşan daha uzun bir liste düşünün. Bu çözümün performansını dict.fromkeys() kullanan sürümle karşılaştırabiliriz:

import random
import timeit
data = [random.randint(0, 100) for _ in range(100)]
use_fromkeys = "unique_data = list(dict.fromkeys(data))"
use_for_loop = """unique_data = []
for item in data:
    if item not in unique_data:
        unique_data.append(item)"""
from_keys_time = timeit.timeit(use_fromkeys, globals=globals())
for_loop_time = timeit.timeit(use_for_loop, globals=globals())
print(f"Time for 'dict.fromkeys()' version: {from_keys_time:0.2f}s")
print(f"Time for loop version: {for_loop_time:0.2f}s")
Time for 'dict.fromkeys()' version: 2.26s
Time for loop version: 17.88s

timeit() işlevi, aynı adlı modülde bir deyimin yürütülme süresini ölçer. Deyimler timeit.timeit()'e dize olarak aktarılır. Çıktı, for döngüsü sürümünün dict.fromkeys() kullanımına göre belirgin derecede daha yavaş olduğunu gösterir. Yürütme süreleri farklı bilgisayar ve kurulumlarda değişecektir, ancak dict.fromkeys() seçeneği her zaman belirgin şekilde daha hızlı olacaktır.

Bir kümeyle liste üreteci kullanma

Önceki bölümdeki for döngüsü bir liste üretecine dönüştürülemez; çünkü if ifadesi, bir öğenin yeni listeye zaten eklenip eklenmediğini kontrol eder. Bir liste üreteci kullanmak için ayrı bir veri yapısı gerekir. Listeye zaten eklenmiş öğeleri takip etmek için bir küme kullanılabilir:

names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
items = set()
unique_names = [name for name in names if not (name in items or items.add(name))]
print(unique_names)
['James', 'Bob', 'Mark', 'Kate', 'Sarah']

Bu ders, bütünlük açısından bu çözümü gösterir; ancak bu seçenek okunabilirlikten yoksundur ve dict.fromkeys() kullanımına kıyasla bir performans iyileştirmesi sunmaz.

İsim zaten items kümesindeyse, liste üretecindeki parantez içindeki ifade True olarak değerlendirilir. or anahtar sözcüğünden sonraki ifade, ilk işlenen True olduğunda değerlendirilmez; çünkü or kısa devre yapar. Parantez içindeki ifade True olduğundan if koşulu False olur ve ad unique_names listesine eklenmez.

İsim items kümesinde değilse, or anahtar sözcüğünden sonraki ifade değerlendirilir ve adı kümeye ekler. Ancak .add() metodu None döndürür ve bu da yanlış değerdir. Artık if koşulu True olur ve ad unique_names listesine de eklenir.

Üçüncü taraf kütüphaneleri kullanma

Bir dizi üçüncü taraf modülü, yinelenenleri kaldırmaya yönelik araçlar sunar. more_itertools üçüncü taraf kütüphanesi, sıralarını korurken benzersiz öğeler üreten bir yineleyici döndüren unique_everseen() içerir.

more_itertools kütüphanesi terminalde pip veya diğer paket yöneticileriyle kurulabilir:

$ pip install more_itertools

Artık bir listeden yinelenenleri kaldırmak için more_itertools.unique_everseen() kullanabiliriz:

import more_itertools
names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
unique_names = list(more_itertools.unique_everseen(names))
print(unique_names)
['James', 'Bob', 'Mark', 'Kate', 'Sarah']

unique_everseen() bir yineleyici döndürdüğünden, çıktısı bir listeye dönüştürülür.

Diğer popüler bir üçüncü taraf kütüphanesi olan NumPy da yinelenenleri kaldırmaya yönelik bir çözüm sunar. NumPy, pip veya diğer paket yöneticileriyle kurulabilir:

$ pip install numpy

NumPy'nin unique() işlevi, argümanındaki benzersiz öğelerle bir NumPy dizisi döndürür. Bu NumPy dizisi, NumPy'nin .tolist() yöntemiyle listeye dönüştürülebilir:

import numpy as np
names = ["James", "Bob", "James", "Mark", "Kate", "Sarah", "Kate"]
unique_names = np.unique(names).tolist()
print(unique_names)
['Bob', 'James', 'Kate', 'Mark', 'Sarah']

Ancak, NumPy'nin unique() işlevinin öğelerin sırasını korumadığını unutmayın. Bunun yerine sıralı öğeler döndürür. Diğer popüler bir üçüncü taraf kütüphanesi olan Pandas da benzer şekilde çalışan ancak öğelerin sırasını koruyan bir unique() işlevine sahiptir. NumPy ve Pandas çözümleri, bu kütüphanelerin zaten kod tabanında kullanıldığı ve sadece listeden yinelenenleri kaldırmak için kurulup içe aktarılmasına gerek olmayan durumlar için idealdir.

dict.fromkeys() Bir Kümeye Göre Daha mı İyi?

Çoğu durumda, bu derste sunulan ilk iki seçenek en iyileridir:

  1. Listeyi kümeye dönüştürün ve kümeyi tekrar listeye çevirin.
  2. dict.fromkeys() kullanarak listeden bir sözlük oluşturun ve tekrar listeye dönüştürün.

Bir sözlük kullanmak öğelerin sırasını korur. Ancak, bir küme kullanmak daha verimlidir ve öğelerin sırasına ihtiyaç duyulmadığında performans iyileştirmesi sağlar.

Performans iyileşmesi timeit kullanılarak sayısallaştırılabilir:

import random
import timeit
data = [random.randint(0, 100) for _ in range(100)]
use_fromkeys = "unique_data = list(dict.fromkeys(data))"
use_set = "unique_data = list(set(data))"
from_keys_time = timeit.timeit(use_fromkeys, globals=globals())
set_time = timeit.timeit(use_set, globals=globals())
print(f"Time for 'dict.fromkeys()' version: {from_keys_time:0.2f}s")
print(f"Time for set version: {set_time:0.2f}s")
Time for 'dict.fromkeys()' version: 2.08s
Time for set version: 1.05s

Bir kümeyle listeden yinelenenleri kaldırmak, bir sözlük kullanmaya göre kabaca iki kat daha hızlıdır.

Sonuç

Python ve üçüncü taraf kütüphaneleri, bir listeden yinelenenleri kaldırmak için çeşitli seçenekler sunar.

Python öğrenmeye şu blog yazılarıyla devam edebilirsiniz:

Stephen Gruppetta

Yazar

Stephen Gruppetta

Malta Üniversitesi’nde lisans düzeyinde Fizik ve Matematik okudum. Ardından Londra’ya taşındım ve Imperial College’da Fizik alanında doktoramı tamamladım. İnsan retinasını görüntülemek için yeni optik teknikler üzerine çalıştım. Şimdi Python hakkında yazmaya, Python’u anlatmaya ve Python öğretmeye odaklanıyorum.

Konular

İlgili