Biểu đồ phân tán trong Pandas / Pyplot: Cách lập biểu đồ theo danh mục


89

Tôi đang cố gắng tạo một biểu đồ phân tán đơn giản trong pyplot bằng cách sử dụng đối tượng Pandas DataFrame, nhưng muốn một cách hiệu quả để vẽ biểu đồ hai biến nhưng có các ký hiệu được chỉ định bởi một cột thứ ba (khóa). Tôi đã thử nhiều cách khác nhau bằng cách sử dụng df.groupby, nhưng không thành công. Dưới đây là một tập lệnh df mẫu. Điều này tô màu các điểm đánh dấu theo 'key1', nhưng tôi muốn thấy một chú giải với các danh mục 'key1'. Tôi có thân thiết không? Cảm ơn.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
fig1 = plt.figure(1)
ax1 = fig1.add_subplot(111)
ax1.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
plt.show()

Câu trả lời:


118

Bạn có thể sử dụng scattercho điều này, nhưng điều đó yêu cầu phải có các giá trị số cho của bạn key1và bạn sẽ không có chú giải, như bạn đã nhận thấy.

Tốt hơn là chỉ nên sử dụng plotcho các danh mục rời rạc như thế này. Ví dụ:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.random.seed(1974)

# Generate Data
num = 20
x, y = np.random.random((2, num))
labels = np.random.choice(['a', 'b', 'c'], num)
df = pd.DataFrame(dict(x=x, y=y, label=labels))

groups = df.groupby('label')

# Plot
fig, ax = plt.subplots()
ax.margins(0.05) # Optional, just adds 5% padding to the autoscaling
for name, group in groups:
    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12, label=name)
ax.legend()

plt.show()

nhập mô tả hình ảnh ở đây

Nếu bạn muốn mọi thứ trông giống như pandaskiểu mặc định , thì chỉ cần cập nhật biểu rcParamsđịnh kiểu gấu trúc và sử dụng trình tạo màu của nó. (Tôi cũng đang điều chỉnh chú thích một chút):

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.random.seed(1974)

# Generate Data
num = 20
x, y = np.random.random((2, num))
labels = np.random.choice(['a', 'b', 'c'], num)
df = pd.DataFrame(dict(x=x, y=y, label=labels))

groups = df.groupby('label')

# Plot
plt.rcParams.update(pd.tools.plotting.mpl_stylesheet)
colors = pd.tools.plotting._get_standard_colors(len(groups), color_type='random')

fig, ax = plt.subplots()
ax.set_color_cycle(colors)
ax.margins(0.05)
for name, group in groups:
    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12, label=name)
ax.legend(numpoints=1, loc='upper left')

plt.show()

nhập mô tả hình ảnh ở đây


Tại sao trong ví dụ RGB ở trên, biểu tượng được hiển thị hai lần trong chú giải? Làm thế nào để chỉ hiển thị một lần?
Steve Schulist

1
@SteveSchulist - Sử dụng ax.legend(numpoints=1)để chỉ hiển thị một điểm đánh dấu. Có hai, như với a Line2D, thường có một đường nối hai điểm đánh dấu.
Joe Kington

Mã này chỉ hoạt động đối với tôi sau khi thêm vào plt.hold(True)sau ax.plot()lệnh. Bất kỳ ý tưởng tại sao?
Yuval Atzmon

set_color_cycle() không được chấp nhận trong matplotlib 1.5. Có set_prop_cycle(), bây giờ.
ale

52

Điều này rất đơn giản để thực hiện với Seaborn ( pip install seaborn) dưới dạng oneliner

sns.scatterplot(x_vars="one", y_vars="two", data=df, hue="key1") :

import seaborn as sns
import pandas as pd
import numpy as np
np.random.seed(1974)

df = pd.DataFrame(
    np.random.normal(10, 1, 30).reshape(10, 3),
    index=pd.date_range('2010-01-01', freq='M', periods=10),
    columns=('one', 'two', 'three'))
df['key1'] = (4, 4, 4, 6, 6, 6, 8, 8, 8, 8)

sns.scatterplot(x="one", y="two", data=df, hue="key1")

nhập mô tả hình ảnh ở đây

Đây là khung dữ liệu để tham khảo:

nhập mô tả hình ảnh ở đây

Vì bạn có ba cột biến đổi trong dữ liệu của mình, bạn có thể muốn vẽ tất cả các thứ nguyên theo cặp với:

sns.pairplot(vars=["one","two","three"], data=df, hue="key1")

nhập mô tả hình ảnh ở đây

https://rasbt.github.io/mlxtend/user_guide/plotting/category_scatter/ là một tùy chọn khác.


19

Với plt.scatter, tôi chỉ có thể nghĩ đến một: sử dụng một proxy artist:

df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
fig1 = plt.figure(1)
ax1 = fig1.add_subplot(111)
x=ax1.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)

ccm=x.get_cmap()
circles=[Line2D(range(1), range(1), color='w', marker='o', markersize=10, markerfacecolor=item) for item in ccm((array([4,6,8])-4.0)/4)]
leg = plt.legend(circles, ['4','6','8'], loc = "center left", bbox_to_anchor = (1, 0.5), numpoints = 1)

Và kết quả là:

nhập mô tả hình ảnh ở đây


10

Bạn có thể sử dụng df.plot.scatter và truyền một mảng vào đối số c = xác định màu của mỗi điểm:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
colors = np.where(df["key1"]==4,'r','-')
colors[df["key1"]==6] = 'g'
colors[df["key1"]==8] = 'b'
print(colors)
df.plot.scatter(x="one",y="two",c=colors)
plt.show()

nhập mô tả hình ảnh ở đây


4

Bạn cũng có thể thử Altair hoặc ggpot tập trung vào các hình ảnh trực quan mang tính khai báo.

import numpy as np
import pandas as pd
np.random.seed(1974)

# Generate Data
num = 20
x, y = np.random.random((2, num))
labels = np.random.choice(['a', 'b', 'c'], num)
df = pd.DataFrame(dict(x=x, y=y, label=labels))

Mã Altair

from altair import Chart
c = Chart(df)
c.mark_circle().encode(x='x', y='y', color='label')

nhập mô tả hình ảnh ở đây

mã ggplot

from ggplot import *
ggplot(aes(x='x', y='y', color='label'), data=df) +\
geom_point(size=50) +\
theme_bw()

nhập mô tả hình ảnh ở đây


3

Từ matplotlib 3.1 trở đi bạn có thể sử dụng .legend_elements(). Một ví dụ được hiển thị trong Tạo chú giải tự động . Ưu điểm là có thể sử dụng một cuộc gọi phân tán duy nhất.

Trong trường hợp này:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), 
                  index = pd.date_range('2010-01-01', freq = 'M', periods = 10), 
                  columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)


fig, ax = plt.subplots()
sc = ax.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
ax.legend(*sc.legend_elements())
plt.show()

nhập mô tả hình ảnh ở đây

Trong trường hợp các khóa không được cung cấp trực tiếp dưới dạng số, nó sẽ giống như

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), 
                  index = pd.date_range('2010-01-01', freq = 'M', periods = 10), 
                  columns = ('one', 'two', 'three'))
df['key1'] = list("AAABBBCCCC")

labels, index = np.unique(df["key1"], return_inverse=True)

fig, ax = plt.subplots()
sc = ax.scatter(df['one'], df['two'], marker = 'o', c = index, alpha = 0.8)
ax.legend(sc.legend_elements()[0], labels)
plt.show()

nhập mô tả hình ảnh ở đây


Tôi gặp lỗi khi nói rằng đối tượng 'PathCollection' không có thuộc tính 'Legends_elements'. Mã của tôi như sau. fig, ax = plt.subplots(1, 1, figsize = (4,4)) scat = ax.scatter(rand_jitter(important_dataframe["workout_type_int"], jitter = 0.04), important_dataframe["distance"], c = color_list, marker = 'o', alpha = 0.9) print(scat.legends_elements()) #ax.legend(*scat.legend_elements())
Nandish Patel

1
@NandishPatel Kiểm tra câu đầu tiên của câu trả lời này. Cũng đảm bảo không nhầm lẫn legends_elementslegend_elements.
Tầm quan

Vâng, cảm ơn. Đó là một lỗi đánh máy (truyền thuyết / huyền thoại). Tôi đang làm việc gì đó từ 6 giờ qua nên phiên bản Matplotlib không xảy ra với tôi. Tôi nghĩ rằng tôi đang sử dụng cái mới nhất. Tôi đã nhầm lẫn rằng tài liệu nói rằng có phương pháp như vậy nhưng mã đang báo lỗi. Cám ơn bạn một lần nữa. Tôi có thể ngủ bây giờ.
Nandish Patel


1

seaborn có một chức năng bao bọc scatterplotđể làm điều đó hiệu quả hơn.

sns.scatterplot(data = df, x = 'one', y = 'two', data =  'key1'])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.