मैं पांडा डेटाफ़्रेम द्वारा उपयोग की जाने वाली मेमोरी कैसे जारी करता हूं?


111

मेरे पास एक बहुत बड़ी सीएसवी फ़ाइल है जिसे मैंने पांडा में इस प्रकार खोला है ...।

import pandas
df = pandas.read_csv('large_txt_file.txt')

एक बार जब मैं ऐसा करता हूं तो मेरी मेमोरी का उपयोग 2GB बढ़ जाता है, जो कि अपेक्षित है क्योंकि इस फाइल में लाखों पंक्तियां हैं। मेरी समस्या तब आती है जब मुझे इस मेमोरी को रिलीज़ करने की आवश्यकता होती है। मैं भागा…।

del df

हालाँकि, मेरा मेमोरी उपयोग कम नहीं हुआ। क्या यह पांडा डेटा फ्रेम द्वारा उपयोग की गई मेमोरी जारी करने का गलत तरीका है? यदि यह है, तो उचित तरीका क्या है?


3
यह सही है, कचरा संग्रहकर्ता मेमोरी को सीधे जारी नहीं कर सकता है, आप gcमॉड्यूल और कॉल को भी आयात कर सकते हैं gc.collect()लेकिन यह मेमोरी को पुनर्प्राप्त नहीं कर सकता है
EdChum

del dfसीधे df के निर्माण के बाद नहीं कहा जाता है? मुझे लगता है कि df को हटाने के बिंदु पर df के संदर्भ हैं। इसलिए यह नाम हटाए जाने के बजाय हटा दिया जाएगा।
मार्लोन एबेकून

4
कचरा संग्रहकर्ता द्वारा रिकवर की गई मेमोरी वास्तव में OS पर वापस दी जाती है या नहीं, यह कार्यान्वयन पर निर्भर है; गारबेज कलेक्टर बनाता है एकमात्र गारंटी यह है कि पुनः प्राप्त मेमोरी का उपयोग वर्तमान पायथन प्रक्रिया द्वारा ओएस से पूछने या यहां तक ​​कि अधिक मेमोरी के बजाय अन्य चीजों के लिए किया जा सकता है ।
चेप्टर

मैं निर्माण के ठीक बाद डेल डीएफ कह रहा हूं। मैंने df में कोई अन्य संदर्भ नहीं जोड़ा। मैंने जो भी किया वो ipython ओपन था और कोड के उन तीन लाइनों को चलाया। अगर मैं उसी कोड को किसी अन्य ऑब्जेक्ट पर चलाता हूं जो बहुत सारी मेमोरी लेता है, जैसे कि एक सुस्पष्ट सरणी। डेल नैपर्रे पूरी तरह से काम करता है
b10hazard

@ b10hazard: क्या df = ''आप कोड के अंत में कुछ पसंद है ? डेटाफ्रेम द्वारा उपयोग की जाने वाली रैम को साफ करने के लिए लगता है।
जिबॉनेट

जवाबों:


119

पायथन में मेमोरी का उपयोग कम करना मुश्किल है, क्योंकि पायथन वास्तव में मेमोरी को ऑपरेटिंग सिस्टम में वापस नहीं करता है । यदि आप ऑब्जेक्ट्स को हटाते हैं, तो मेमोरी नए पायथन ऑब्जेक्ट्स के लिए उपलब्ध है, लेकिन free()सिस्टम में वापस नहीं आई ( यह प्रश्न देखें )।

यदि आप संख्यात्मक सुन्न सरणियों से चिपके रहते हैं, तो उन्हें मुक्त कर दिया जाता है, लेकिन बॉक्सिंग ऑब्जेक्ट नहीं हैं।

>>> import os, psutil, numpy as np
>>> def usage():
...     process = psutil.Process(os.getpid())
...     return process.get_memory_info()[0] / float(2 ** 20)
... 
>>> usage() # initial memory usage
27.5 

>>> arr = np.arange(10 ** 8) # create a large array without boxing
>>> usage()
790.46875
>>> del arr
>>> usage()
27.52734375 # numpy just free()'d the array

>>> arr = np.arange(10 ** 8, dtype='O') # create lots of objects
>>> usage()
3135.109375
>>> del arr
>>> usage()
2372.16796875  # numpy frees the array, but python keeps the heap big

डेटाफ्रेम की संख्या को कम करना

अजगर हमारी स्मृति को उच्च वॉटरमार्क पर रखता है, लेकिन हम हमारे द्वारा बनाए गए डेटाफ्रेम की कुल संख्या को कम कर सकते हैं। अपने डेटाफ़्रेम को संशोधित करते समय, पसंद करें inplace=True, ताकि आप प्रतियां न बनाएँ।

एक और आम गोथा, जो पहले से तैयार डेटाफ़्रेम की प्रतियों को ipython में पकड़े हुए है:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'foo': [1,2,3,4]})

In [3]: df + 1
Out[3]: 
   foo
0    2
1    3
2    4
3    5

In [4]: df + 2
Out[4]: 
   foo
0    3
1    4
2    5
3    6

In [5]: Out # Still has all our temporary DataFrame objects!
Out[5]: 
{3:    foo
 0    2
 1    3
 2    4
 3    5, 4:    foo
 0    3
 1    4
 2    5
 3    6}

आप %reset Outअपना इतिहास साफ़ करने के लिए इसे टाइप करके ठीक कर सकते हैं । वैकल्पिक रूप से, आप समायोजित कर सकते हैं कि ipython कितना इतिहास रखता है ipython --cache-size=5(डिफ़ॉल्ट 1000 है)।

डेटाफ़्रेम आकार को कम करना

जहां भी संभव हो, वस्तु dtypes का उपयोग करने से बचें।

>>> df.dtypes
foo    float64 # 8 bytes per value
bar      int64 # 8 bytes per value
baz     object # at least 48 bytes per value, often more

किसी ऑब्जेक्ट dtype वाले मानों को बॉक्स किया जाता है, जिसका अर्थ है कि केवल एक सरणी में सूचक समाहित होता है और आपके डेटाफ़्रेम में प्रत्येक मान के लिए ढेर पर एक पूर्ण पायथन ऑब्जेक्ट होता है। इसमें तार शामिल हैं।

Whilst numpy सरणियों में निश्चित आकार के तार का समर्थन करता है, पांडा नहीं करता है ( यह उपयोगकर्ता भ्रम का कारण है )। यह एक महत्वपूर्ण अंतर बना सकता है:

>>> import numpy as np
>>> arr = np.array(['foo', 'bar', 'baz'])
>>> arr.dtype
dtype('S3')
>>> arr.nbytes
9

>>> import sys; import pandas as pd
>>> s = pd.Series(['foo', 'bar', 'baz'])
dtype('O')
>>> sum(sys.getsizeof(x) for x in s)
120

आप स्ट्रिंग कॉलम का उपयोग करने से बचना चाहते हैं, या संख्याओं के रूप में स्ट्रिंग डेटा का प्रतिनिधित्व करने का एक तरीका खोज सकते हैं।

यदि आपके पास एक डेटाफ्रेम है जिसमें कई दोहराया मान हैं (NaN बहुत आम है), तो आप मेमोरी उपयोग को कम करने के लिए एक विरल डेटा संरचना का उपयोग कर सकते हैं :

>>> df1.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo    float64
dtypes: float64(1)
memory usage: 605.5 MB

>>> df1.shape
(39681584, 1)

>>> df1.foo.isnull().sum() * 100. / len(df1)
20.628483479893344 # so 20% of values are NaN

>>> df1.to_sparse().info()
<class 'pandas.sparse.frame.SparseDataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo    float64
dtypes: float64(1)
memory usage: 543.0 MB

मेमोरी उपयोग देखना

आप मेमोरी उपयोग ( डॉक्स ) देख सकते हैं :

>>> df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 14 columns):
...
dtypes: datetime64[ns](1), float64(8), int64(1), object(4)
memory usage: 4.4+ GB

पांडा 0.17.1 के अनुसार, आप df.info(memory_usage='deep')ऑब्जेक्ट सहित मेमोरी उपयोग को देखने के लिए भी कर सकते हैं।


2
इस पर 'स्वीकृत उत्तर' अंकित करना होगा। यह संक्षेप में, लेकिन स्पष्ट रूप से बताता है कि कैसे अजगर स्मृति पर रखता है जब भी वास्तव में इसकी आवश्यकता नहीं होती है। मेमोरी को बचाने के लिए टिप्स सभी समझदार और उपयोगी हैं। एक और टिप के रूप में मैं सिर्फ 'मल्टीप्रोसेसिंग' का उपयोग कर
जोड़ूंगा

46

जैसा कि टिप्पणियों में कहा गया है, कोशिश करने के लिए कुछ चीजें हैं: gc.collect(@EdChum) स्पष्ट सामग्री, उदाहरण के लिए। कम से कम मेरे अनुभव से, ये चीजें कभी-कभी काम करती हैं और अक्सर नहीं।

एक चीज है जो हमेशा काम करती है, हालांकि, क्योंकि यह ओएस पर किया जाता है, न कि भाषा, स्तर पर।

मान लें कि आपके पास एक फ़ंक्शन है जो एक मध्यवर्ती विशाल डेटाफ़्रेम बनाता है, और एक छोटा परिणाम देता है (जो डेटाफ़्रेम भी हो सकता है):

def huge_intermediate_calc(something):
    ...
    huge_df = pd.DataFrame(...)
    ...
    return some_aggregate

फिर अगर आप कुछ ऐसा करते हैं

import multiprocessing

result = multiprocessing.Pool(1).map(huge_intermediate_calc, [something_])[0]

फिर फ़ंक्शन को एक अलग प्रक्रिया में निष्पादित किया जाता है । जब वह प्रक्रिया पूरी हो जाती है, तो ओएस अपने उपयोग किए गए सभी संसाधनों को पुनः प्राप्त कर लेता है। वहाँ वास्तव में कुछ भी नहीं पायथन, पांडा, कचरा कलेक्टर, को रोकने के लिए कर सकता है।


1
@ b10hazard पांडा के बिना भी, मैं कभी भी पूरी तरह से समझ नहीं पाया कि अभ्यास में पायथन मेमोरी कैसे काम करती है। यह क्रूड तकनीक एकमात्र ऐसी चीज है जिस पर मैं भरोसा करता हूं।
अमी तावोरी

9
वास्तव में अच्छा काम करता है। हालाँकि एक ipython वातावरण में (ज्यूपिटर नोटबुक की तरह) मैंने पाया कि आपको स्पॉक्ड प्रक्रिया से छुटकारा पाने के लिए .close () और .join () या .terminate () पूल की आवश्यकता है। ऐसा करने का सबसे आसान तरीका है कि पायथन 3.3 का संदर्भ प्रबंधन प्रोटोकॉल का उपयोग करना है: with multiprocessing.Pool(1) as pool: result = pool.map(huge_intermediate_calc, [something])जो एक बार किए गए पूल को बंद करने का होता है।
ज़र्टरीन

2
यह अच्छा काम करता है, कार्य समाप्त होने के बाद बस समाप्त करना और पूल में शामिल होना मत भूलना।
एंड्री निकिशाव

1
एक अजगर वस्तु से स्मृति को वापस करने का दावा करने के बारे में कई बार पढ़ने के बाद, यह ऐसा करने का सबसे अच्छा तरीका लगता है। एक प्रक्रिया बनाएँ, और जब उस प्रक्रिया को मार दिया जाता है तो OS मेमोरी को रिलीज़ करता है।
मुअम्मर

1
हो सकता है कि यह किसी की मदद करे, जब पूल बनाने के लिए प्रक्रिया को जारी करने और काम पूरा होने के बाद एक नया काम शुरू करने के लिए अधिकतम 1 = 1 का उपयोग करने की कोशिश करें।
गिविरो

22

यह मेरे लिए स्मृति जारी करने की समस्या को हल करता है !!!

del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()

डेटा-फ़्रेम स्पष्ट रूप से शून्य पर सेट किया जाएगा


1
डेटाफ़्रेम को उप-सूची [[df_1, df_2]] में क्यों जोड़ा गया? कोई खास वजह? कृपया समझाएँ।
गोक्स

5
आप पिछले दो वक्तव्यों का उपयोग क्यों नहीं करते? मुझे नहीं लगता कि आपको पहले दो वक्तव्यों की जरूरत है।
spacedustpi

3

del dfdfहटाए जाने के समय कोई संदर्भ होने पर हटाया नहीं जाएगा । तो आपको del dfमेमोरी जारी करने के साथ इसके सभी संदर्भों को हटाने की आवश्यकता है ।

तो df के लिए बाध्य सभी उदाहरणों को कचरा संग्रह को ट्रिगर करने के लिए हटा दिया जाना चाहिए।

ऑब्जेक्ट पर जो जाँच करने के लिए objgragh का उपयोग करें ।


लिंक को objgraph ( mg.pov.lt/objgraph ) की ओर इंगित करता है, यह आपके उत्तर में एक टाइपो है जब तक कि कोई objgragh नहीं है
SatZ

1

ऐसा लगता है कि glibc के साथ एक मुद्दा है जो पंडों में मेमोरी आवंटन को प्रभावित करता है: https://github.com/pandas-dev/pandas/issues/2659

बंदर पैच इस मुद्दे पर विस्तृत मेरे लिए समस्या हल हो गया है:

# monkeypatches.py

# Solving memory leak problem in pandas
# https://github.com/pandas-dev/pandas/issues/2659#issuecomment-12021083
import pandas as pd
from ctypes import cdll, CDLL
try:
    cdll.LoadLibrary("libc.so.6")
    libc = CDLL("libc.so.6")
    libc.malloc_trim(0)
except (OSError, AttributeError):
    libc = None

__old_del = getattr(pd.DataFrame, '__del__', None)

def __new_del(self):
    if __old_del:
        __old_del(self)
    libc.malloc_trim(0)

if libc:
    print('Applying monkeypatch for pd.DataFrame.__del__', file=sys.stderr)
    pd.DataFrame.__del__ = __new_del
else:
    print('Skipping monkeypatch for pd.DataFrame.__del__: libc or malloc_trim() not found', file=sys.stderr)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.