python - संपादनयोग्य QTableView और Pandas ठीक से काम नहीं करते




model pyqt4 (3)

ऐसा लगता है कि जब मैं PyQt4 के बजाय PySide पर स्विच करता हूं:

import sys
from PySide import QtCore, QtGui
import pandas as pd
Qt = QtCore.Qt

    class PandasModelEditable(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
                return unicode(self._data.iloc[index.row(), index.column()])
        return None

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return None
        if orientation == QtCore.Qt.Horizontal:
            try:
                return '%s' % unicode(self._data.columns.tolist()[section])
            except (IndexError,):
                return unicode()
        elif orientation == QtCore.Qt.Vertical:
            try:
                return '%s' % unicode(self._data.index.tolist()[section])
            except (IndexError,):
                return unicode()

    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
               QtCore.Qt.ItemIsEditable

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            self._data.iloc[index.row(), index.column()] = value
            if self.data(index, QtCore.Qt.DisplayRole) == value:
                self.dataChanged.emit(index, index)
                return True
        return False


if __name__ == '__main__':
    application = QtGui.QApplication(sys.argv)
    view = QtGui.QTableView()
    df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'], index=['x', 'y'])

    model = PandasModelEditable(df)
    view.setModel(model)

    view.show()
    sys.exit(application.exec_())

संपादन योग्य दृश्यों को प्राप्त करते समय मैं पंडों और क्यूटेबल दृश्य का उपयोग करने के लिए उदाहरण के कोड का आत्म-निहित टुकड़ा प्राप्त करने की कोशिश कर रहा हूं।

इसके लिए मैं पहले की चर्चा का पालन कर रहा हूं: संपादन योग्य QTableView में Pandas DF: चेक बॉक्स को हटा दें

जबकि अन्य चर्चा में जवाब और प्रस्तावित संशोधनों से चेकबॉक्स को छुटकारा पाने में सहायता मिलती है, वहां पर चर्चा की गई कोड मेरे लिए अभी भी काम नहीं कर रहा है (अजगर 2.7)।

जब मैं नीचे कोड का उपयोग कर एक कक्ष को संशोधित करता हूं, तो सेल में दिखाया गया सामग्री है: PtQt4.PtCore.QtVariant ऑब्जेक्ट ...

मेरे द्वारा उपयोग किए जाने वाले पैकेज संस्करण हैं:

पांडा: 0.20.2
पाइसाइड 1.2.4
क्यूटी संस्करण: 4.8.4
एसआईपी संस्करण: 4.14.4 पीएईटीटी संस्करण: 4.10

import sys
from PyQt4 import QtCore, QtGui
import pandas as pd
Qt = QtCore.Qt

    class PandasModelEditable(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
                return unicode(self._data.iloc[index.row(), index.column()])
        return None

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return None
        if orientation == QtCore.Qt.Horizontal:
            try:
                return '%s' % unicode(self._data.columns.tolist()[section])
            except (IndexError,):
                return unicode()
        elif orientation == QtCore.Qt.Vertical:
            try:
                return '%s' % unicode(self._data.index.tolist()[section])
            except (IndexError,):
                return unicode()

    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
               QtCore.Qt.ItemIsEditable

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            self._data.iloc[index.row(), index.column()] = value
            if self.data(index, QtCore.Qt.DisplayRole) == value:
                self.dataChanged.emit(index, index)
                return True
        return False


if __name__ == '__main__':
    application = QtGui.QApplication(sys.argv)
    view = QtGui.QTableView()
    df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'], index=['x', 'y'])

    model = PandasModelEditable(df)
    view.setModel(model)

    view.show()
    sys.exit(application.exec_())

तत्काल समस्या अंतर्निहित डेटाबेस में एक असंबद्ध QVariant ऑब्जेक्ट गुजरने के कारण होती है। सरलतम तय यह एक अजगर ऑब्जेक्ट में बदलता है, जैसे:

self._data.iloc[index.row(), index.column()] = value.toPyObject()

हालांकि, यह वास्तव में कोड के साथ सबसे मौलिक समस्या से निपटने के लिए नहीं है, जो कि आप पायथन और पीएएटीटी के पुराने संस्करणों का उपयोग कर रहे हैं। Qt आधिकारिक तौर पर किसी भी अधिक Qt4 का समर्थन नहीं करता है, और यह पायथन और पायथन 2 के लिए सही होने से पहले लंबा नहीं होगा। कड़ाई से बोलते हुए, PyQt4 पहले से ही अप्रचलित विरासत कोड है - इसलिए आपको इसे नई परियोजनाओं के लिए उपयोग नहीं करना चाहिए, जब तक आपके पास ऐसा करने का एक बहुत अच्छा कारण नहीं है (जैसे पीछे की ओर compatibilty)।

यदि आप कर सकते हैं, तो मैं दृढ़ता से अनुशंसा करता हूं कि आप जितनी जल्दी हो सके Python3 / PyQt5 को अपना कोड बंद कर दें, क्योंकि इससे आपको लंबे समय तक माध्यम से बहुत परेशानी की बचत होगी। हालांकि, यदि आप किसी कारण के लिए ऐसा नहीं कर सकते हैं, और आप Python2 / PyQt4 का उपयोग जारी रखना चाहते हैं, तो आप अपने कार्यक्रम की शुरुआत में निम्नलिखित को जोड़कर, PySide के समान व्यवहार प्राप्त कर सकते हैं:

import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtCore, QtGui

ऐसा करने के बाद, PyQt स्वचालित रूप से सभी QVariant और QVariant वस्तुओं को साधारण अजगर डेटा प्रकारों में परिवर्तित कर देगा, ताकि आपको किसी भी स्पष्ट रूपांतरण की आवश्यकता नहीं होगी (यानी आप अपने कोड में उन सभी unicode() और toPyObject() कॉल को निकाल सकते हैं)।

वैकल्पिक रूप से, आप Py Python3 को PyQt4 के साथ भी इस्तेमाल कर सकते हैं, जिसका डिफ़ॉल्ट रूप से PySide के समान व्यवहार है (इसलिए setapi सामान की आवश्यकता नहीं होगी)।


पांडा डेटाफ्रेम से पंक्तियों का चयन करने के कुछ बुनियादी तरीके हैं।

  1. बूलियन इंडेक्सिंग
  2. पोजिशनल इंडेक्सिंग
  3. लेबल इंडेक्सिंग
  4. एपीआई

प्रत्येक आधार प्रकार के लिए, हम अपने आप को पांडा एपीआई में सीमित करके चीजों को सरल बना सकते हैं या हम एपीआई के बाहर उद्यम कर सकते हैं, आमतौर पर numpy , और गति चीजों को ऊपर।

मैं आपको प्रत्येक के उदाहरण दिखाऊंगा और आपको कुछ तकनीकों का उपयोग करने के लिए मार्गदर्शन करूंगा।

सेट अप
पहली चीज़ जो हमें चाहिए वह ऐसी स्थिति की पहचान करना है जो पंक्तियों का चयन करने के लिए हमारे मानदंड के रूप में कार्य करेगी। ओपी column_name == some_value प्रदान करता है। हम वहां शुरू करेंगे और कुछ अन्य सामान्य उपयोग मामलों को शामिल करेंगे।

@unutbu से उधार:

import pandas as pd, numpy as np

df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
                   'B': 'one one two three two two one three'.split(),
                   'C': np.arange(8), 'D': np.arange(8) * 2})

मान लें कि हमारा मानदंड स्तंभ 'A' = 'foo'

1।
बूलियन इंडेक्सिंग को प्रत्येक पंक्ति के 'A' कॉलम के सत्य मूल्य को 'foo' बराबर होने की आवश्यकता होती है, फिर उन सत्य मानों का उपयोग करके यह पता लगाने के लिए कि कौन सी पंक्तियां रखना है। आम तौर पर, हम इस श्रृंखला का नाम, सत्य मूल्यों, mask की एक सरणी का नाम देंगे। हम यहां भी ऐसा करेंगे।

mask = df['A'] == 'foo'

हम डेटाफ्रेम को स्लाइस या इंडेक्स करने के लिए इस मास्क का उपयोग कर सकते हैं

df[mask]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

यह इस कार्य को पूरा करने के सबसे सरल तरीकों में से एक है और यदि प्रदर्शन या अंतर्ज्ञान कोई मुद्दा नहीं है, तो यह आपकी चुनी विधि होनी चाहिए। हालांकि, यदि प्रदर्शन एक चिंता है, तो आप mask बनाने का एक वैकल्पिक तरीका विचार करना चाहेंगे।

2।
पोजिशनल इंडेक्सिंग के उपयोग के मामले हैं, लेकिन यह उनमें से एक नहीं है। स्लाइस कहां से पहचानने के लिए, हमें पहले उसी बूलियन विश्लेषण को करने की आवश्यकता है जो हमने ऊपर किया था। यह हमें एक ही कार्य को पूरा करने के लिए एक अतिरिक्त कदम प्रदर्शन करता है।

mask = df['A'] == 'foo'
pos = np.flatnonzero(mask)
df.iloc[pos]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

3।
लेबल इंडेक्सिंग बहुत आसान हो सकती है, लेकिन इस मामले में, हम फिर से लाभ के लिए और अधिक काम कर रहे हैं

df.set_index('A', append=True, drop=False).xs('foo', level=1)

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

4।
pd.DataFrame.query इस कार्य को करने के लिए एक बहुत ही सुरुचिपूर्ण / सहज ज्ञान युक्त तरीका है। लेकिन अक्सर धीमा होता है। हालांकि , यदि आप बड़े डेटा के लिए नीचे दिए गए समय पर ध्यान देते हैं, तो क्वेरी बहुत ही कुशल है। मेरे सर्वोत्तम सुझाव के रूप में मानक दृष्टिकोण और समान परिमाण से कहीं अधिक।

df.query('A == "foo"')

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

मेरी प्राथमिकता Boolean mask का उपयोग करना mask

संशोधित करके वास्तविक सुधार किए जा सकते हैं कि हम अपने Boolean mask कैसे बनाते हैं।

mask विकल्प 1
अंतर्निहित numpy सरणी का प्रयोग करें और एक और pd.Series बनाने के ऊपरी हिस्से से pd.Series

mask = df['A'].values == 'foo'

मैं अंत में अधिक पूर्णकालिक परीक्षण दिखाऊंगा, लेकिन नमूना डेटाफ्रेम का उपयोग करके हमें प्राप्त होने वाले प्रदर्शन लाभों पर नज़र डालें। सबसे पहले हम mask बनाने में अंतर देखते mask

%timeit mask = df['A'].values == 'foo'
%timeit mask = df['A'] == 'foo'

5.84 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
166 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

numpy सरणी के साथ mask मूल्यांकन ~ 30 गुना तेजी से है। यह आंशिक रूप से numpy मूल्यांकन के कारण अक्सर तेज़ होता है। यह आंशिक रूप से इंडेक्स और संबंधित pd.Series ऑब्जेक्ट बनाने के लिए आवश्यक ओवरहेड की कमी के कारण भी है।

इसके बाद हम एक mask बनाम दूसरे के साथ टुकड़े करने के लिए समय देखेंगे।

mask = df['A'].values == 'foo'
%timeit df[mask]
mask = df['A'] == 'foo'
%timeit df[mask]

219 µs ± 12.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
239 µs ± 7.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

प्रदर्शन लाभ स्पष्ट नहीं हैं। हम देखेंगे कि यह अधिक मजबूत परीक्षण पर निर्भर करता है या नहीं।

mask विकल्प 2
हम डेटाफ्रेम का पुनर्निर्माण भी कर सकते थे। dtypes का पुनर्निर्माण करते समय एक बड़ी चेतावनी है-ऐसा करने पर आपको dtypes का ख्याल रखना चाहिए!

df[mask] बजाय हम यह करेंगे

pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)

यदि df.values मिश्रित प्रकार का है, जो हमारा उदाहरण है, तो जब हमें df.values मिलता है परिणामी सरणी dtype object और इसके परिणामस्वरूप, नए dtype सभी कॉलम dtype object । इस प्रकार astype(df.dtypes) आवश्यकता है और किसी भी संभावित प्रदर्शन लाभ की हत्या।

%timeit df[m]
%timeit pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)

216 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.43 ms ± 39.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

हालांकि, अगर डेटाफ्रेम मिश्रित प्रकार का नहीं है, तो यह करने का यह एक बहुत ही उपयोगी तरीका है।

दिया हुआ

np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))

d1

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
2  0  2  0  4  9
3  7  3  2  4  3
4  3  6  7  7  4
5  5  3  7  5  9
6  8  7  6  4  7
7  6  2  6  6  5
8  2  8  7  5  8
9  4  7  6  1  5    
%%timeit
mask = d1['A'].values == 7
d1[mask]

179 µs ± 8.73 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

बनाम

%%timeit
mask = d1['A'].values == 7
pd.DataFrame(d1.values[mask], d1.index[mask], d1.columns)

87 µs ± 5.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

हम समय आधे में कटौती।

mask विकल्प 3
@unutbu हमें दिखाता है कि pd.Series.isin का उपयोग df['A'] प्रत्येक तत्व के मानों के सेट में होने के लिए खाते में कैसे किया pd.Series.isin । यह एक ही चीज़ का मूल्यांकन करता है यदि मूल्यों का हमारा सेट एक मान का एक सेट है, अर्थात् 'foo' । लेकिन यदि आवश्यक हो तो मूल्यों के बड़े सेट भी शामिल करना सामान्यीकृत है। बाहर निकलता है, यह अभी भी बहुत तेज़ है, भले ही यह एक सामान्य समाधान है। अवधारणा से परिचित लोगों के लिए एकमात्र वास्तविक हानि अंतर्ज्ञान में है।

mask = df['A'].isin(['foo'])
df[mask]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

हालांकि, पहले की तरह, हम लगभग कुछ भी sacraficing जबकि प्रदर्शन में सुधार करने के लिए numpy का उपयोग कर सकते हैं। हम np.in1d उपयोग np.in1d

mask = np.in1d(df['A'].values, ['foo'])
df[mask]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

समय
मैं संदर्भ के लिए अन्य पदों में उल्लिखित अन्य अवधारणाओं को भी शामिल करूंगा।
नीचे कोड

इस तालिका में प्रत्येक कॉलम एक अलग लंबाई डेटाफ्रेम का प्रतिनिधित्व करता है जिस पर हम प्रत्येक फ़ंक्शन का परीक्षण करते हैं। प्रत्येक कॉलम 1.0 बेस इंडेक्स को दिए गए सबसे तेज़ फ़ंक्शन के साथ सापेक्ष समय 1.0

res.div(res.min())

                         10        30        100       300       1000      3000      10000     30000
mask_standard         2.156872  1.850663  2.034149  2.166312  2.164541  3.090372  2.981326  3.131151
mask_standard_loc     1.879035  1.782366  1.988823  2.338112  2.361391  3.036131  2.998112  2.990103
mask_with_values      1.010166  1.000000  1.005113  1.026363  1.028698  1.293741  1.007824  1.016919
mask_with_values_loc  1.196843  1.300228  1.000000  1.000000  1.038989  1.219233  1.037020  1.000000
query                 4.997304  4.765554  5.934096  4.500559  2.997924  2.397013  1.680447  1.398190
xs_label              4.124597  4.272363  5.596152  4.295331  4.676591  5.710680  6.032809  8.950255
mask_with_isin        1.674055  1.679935  1.847972  1.724183  1.345111  1.405231  1.253554  1.264760
mask_with_in1d        1.000000  1.083807  1.220493  1.101929  1.000000  1.000000  1.000000  1.144175

आप देखेंगे कि सबसे तेज़ समय mask_with_values और mask_with_in1d बीच साझा किया mask_with_in1d

res.T.plot(loglog=True)

कार्य

def mask_standard(df):
    mask = df['A'] == 'foo'
    return df[mask]

def mask_standard_loc(df):
    mask = df['A'] == 'foo'
    return df.loc[mask]

def mask_with_values(df):
    mask = df['A'].values == 'foo'
    return df[mask]

def mask_with_values_loc(df):
    mask = df['A'].values == 'foo'
    return df.loc[mask]

def query(df):
    return df.query('A == "foo"')

def xs_label(df):
    return df.set_index('A', append=True, drop=False).xs('foo', level=-1)

def mask_with_isin(df):
    mask = df['A'].isin(['foo'])
    return df[mask]

def mask_with_in1d(df):
    mask = np.in1d(df['A'].values, ['foo'])
    return df[mask]

परिक्षण

res = pd.DataFrame(
    index=[
        'mask_standard', 'mask_standard_loc', 'mask_with_values', 'mask_with_values_loc',
        'query', 'xs_label', 'mask_with_isin', 'mask_with_in1d'
    ],
    columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    dtype=float
)

for j in res.columns:
    d = pd.concat([df] * j, ignore_index=True)
    for i in res.index:a
        stmt = '{}(d)'.format(i)
        setp = 'from __main__ import d, {}'.format(i)
        res.at[i, j] = timeit(stmt, setp, number=50)

विशेष समय
विशेष dtype को देखते हुए जब हमारे पास संपूर्ण dtype लिए एक गैर-ऑब्जेक्ट dtypeनीचे कोड

spec.div(spec.min())

                     10        30        100       300       1000      3000      10000     30000
mask_with_values  1.009030  1.000000  1.194276  1.000000  1.236892  1.095343  1.000000  1.000000
mask_with_in1d    1.104638  1.094524  1.156930  1.072094  1.000000  1.000000  1.040043  1.027100
reconstruct       1.000000  1.142838  1.000000  1.355440  1.650270  2.222181  2.294913  3.406735

बाहर निकलता है, पुनर्निर्माण कुछ सौ पंक्तियों के पीछे लायक नहीं है।

spec.T.plot(loglog=True)

कार्य

np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))

def mask_with_values(df):
    mask = df['A'].values == 'foo'
    return df[mask]

def mask_with_in1d(df):
    mask = np.in1d(df['A'].values, ['foo'])
    return df[mask]

def reconstruct(df):
    v = df.values
    mask = np.in1d(df['A'].values, ['foo'])
    return pd.DataFrame(v[mask], df.index[mask], df.columns)

spec = pd.DataFrame(
    index=['mask_with_values', 'mask_with_in1d', 'reconstruct'],
    columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    dtype=float
)

परिक्षण

for j in spec.columns:
    d = pd.concat([df] * j, ignore_index=True)
    for i in spec.index:
        stmt = '{}(d)'.format(i)
        setp = 'from __main__ import d, {}'.format(i)
        spec.at[i, j] = timeit(stmt, setp, number=50)






python pandas model pyqt4 qtableview